400 workflows webhooks trigger (#11041)

https://github.com/user-attachments/assets/dc0ece22-4d87-417f-b9e1-a11c3fd52ce8
This commit is contained in:
martmull
2025-03-20 11:12:52 +01:00
committed by GitHub
parent bc94891a27
commit d99f027e8d
30 changed files with 399 additions and 49 deletions

View File

@ -190,6 +190,7 @@ describe('computeSchemaComponents', () => {
'IMPORT',
'MANUAL',
'SYSTEM',
'WEBHOOK',
],
},
},
@ -378,6 +379,7 @@ describe('computeSchemaComponents', () => {
'IMPORT',
'MANUAL',
'SYSTEM',
'WEBHOOK',
],
},
},
@ -565,6 +567,7 @@ describe('computeSchemaComponents', () => {
'IMPORT',
'MANUAL',
'SYSTEM',
'WEBHOOK',
],
},
workspaceMemberId: {

View File

@ -211,6 +211,7 @@ const getSchemaComponentsProperties = ({
'IMPORT',
'MANUAL',
'SYSTEM',
'WEBHOOK',
],
},
...(forResponse

View File

@ -0,0 +1,96 @@
import { Controller, Get, Param, UseFilters } from '@nestjs/common';
import { isDefined } from 'twenty-shared';
import { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { WorkflowTriggerRestApiExceptionFilter } from 'src/engine/core-modules/workflow/filters/workflow-trigger-rest-api-exception.filter';
import {
WorkflowTriggerException,
WorkflowTriggerExceptionCode,
} from 'src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception';
import { WorkflowTriggerType } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
@Controller('webhooks')
@UseFilters(WorkflowTriggerRestApiExceptionFilter)
export class WorkflowTriggerController {
constructor(
private readonly twentyORMManager: TwentyORMManager,
private readonly workflowTriggerWorkspaceService: WorkflowTriggerWorkspaceService,
) {}
@Get('workflows/:workspaceId/:workflowId')
async runWorkflow(
@Param('workspaceId') workspaceId: string,
@Param('workflowId') workflowId: string,
) {
const workflowRepository =
await this.twentyORMManager.getRepository<WorkflowWorkspaceEntity>(
'workflow',
);
const workflow = await workflowRepository.findOne({
where: { id: workflowId },
});
if (!isDefined(workflow)) {
throw new WorkflowTriggerException(
'Workflow not found',
WorkflowTriggerExceptionCode.NOT_FOUND,
);
}
if (
!isDefined(workflow.lastPublishedVersionId) ||
workflow.lastPublishedVersionId === ''
) {
throw new WorkflowTriggerException(
'Workflow not activated',
WorkflowTriggerExceptionCode.INVALID_WORKFLOW_STATUS,
);
}
const workflowVersionRepository =
await this.twentyORMManager.getRepository<WorkflowVersionWorkspaceEntity>(
'workflowVersion',
);
const workflowVersion = await workflowVersionRepository.findOne({
where: { id: workflow.lastPublishedVersionId },
});
if (!isDefined(workflowVersion)) {
throw new WorkflowTriggerException(
'Workflow version not found',
WorkflowTriggerExceptionCode.INVALID_WORKFLOW_VERSION,
);
}
if (workflowVersion.trigger?.type !== WorkflowTriggerType.WEBHOOK) {
throw new WorkflowTriggerException(
'Workflow does not have a Webhook trigger',
WorkflowTriggerExceptionCode.INVALID_WORKFLOW_TRIGGER,
);
}
const { workflowRunId } =
await this.workflowTriggerWorkspaceService.runWorkflowVersion({
workflowVersionId: workflow.lastPublishedVersionId,
payload: {},
createdBy: {
source: FieldActorSource.WEBHOOK,
workspaceMemberId: null,
name: 'Webhook',
context: {},
},
});
return {
workflowName: workflow.name,
success: true,
workflowRunId,
};
}
}

View File

@ -2,6 +2,7 @@ import { Catch, ExceptionFilter } from '@nestjs/common';
import {
InternalServerError,
NotFoundError,
UserInputError,
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import {
@ -19,8 +20,11 @@ export class WorkflowTriggerGraphqlApiExceptionFilter
case WorkflowTriggerExceptionCode.INVALID_WORKFLOW_VERSION:
case WorkflowTriggerExceptionCode.INVALID_ACTION_TYPE:
case WorkflowTriggerExceptionCode.INVALID_WORKFLOW_TRIGGER:
case WorkflowTriggerExceptionCode.INVALID_WORKFLOW_STATUS:
case WorkflowTriggerExceptionCode.FORBIDDEN:
throw new UserInputError(exception.message);
case WorkflowTriggerExceptionCode.NOT_FOUND:
throw new NotFoundError(exception.message);
default:
throw new InternalServerError(exception.message);
}

View File

@ -0,0 +1,54 @@
import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
import { Response } from 'express';
import { HttpExceptionHandlerService } from 'src/engine/core-modules/exception-handler/http-exception-handler.service';
import { CustomException } from 'src/utils/custom-exception';
import {
WorkflowTriggerException,
WorkflowTriggerExceptionCode,
} from 'src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception';
@Catch()
export class WorkflowTriggerRestApiExceptionFilter implements ExceptionFilter {
constructor(
private readonly httpExceptionHandlerService: HttpExceptionHandlerService,
) {}
catch(exception: WorkflowTriggerException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
switch (exception.code) {
case WorkflowTriggerExceptionCode.INVALID_INPUT:
case WorkflowTriggerExceptionCode.INVALID_WORKFLOW_TRIGGER:
case WorkflowTriggerExceptionCode.INVALID_WORKFLOW_VERSION:
case WorkflowTriggerExceptionCode.INVALID_ACTION_TYPE:
case WorkflowTriggerExceptionCode.INVALID_WORKFLOW_STATUS:
return this.httpExceptionHandlerService.handleError(
exception as CustomException,
response,
400,
);
case WorkflowTriggerExceptionCode.FORBIDDEN:
return this.httpExceptionHandlerService.handleError(
exception as CustomException,
response,
403,
);
case WorkflowTriggerExceptionCode.NOT_FOUND:
return this.httpExceptionHandlerService.handleError(
exception as CustomException,
response,
404,
);
case WorkflowTriggerExceptionCode.INTERNAL_ERROR:
default:
return this.httpExceptionHandlerService.handleError(
exception as CustomException,
response,
500,
);
}
}
}

View File

@ -10,6 +10,7 @@ import { AuthWorkspaceMemberId } from 'src/engine/decorators/auth/auth-workspace
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service';
import { buildCreatedByFromFullNameMetadata } from 'src/engine/core-modules/actor/utils/build-created-by-from-full-name-metadata.util';
@Resolver()
@UseGuards(WorkspaceAuthGuard, UserAuthGuard)
@ -43,11 +44,16 @@ export class WorkflowTriggerResolver {
@AuthUser() user: User,
@Args('input') { workflowVersionId, payload }: RunWorkflowVersionInput,
) {
return await this.workflowTriggerWorkspaceService.runWorkflowVersion(
return await this.workflowTriggerWorkspaceService.runWorkflowVersion({
workflowVersionId,
payload ?? {},
workspaceMemberId,
user,
);
payload: payload ?? {},
createdBy: buildCreatedByFromFullNameMetadata({
fullNameMetadata: {
firstName: user.firstName,
lastName: user.lastName,
},
workspaceMemberId: workspaceMemberId,
}),
});
}
}

View File

@ -8,6 +8,7 @@ import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-commo
import { WorkflowBuilderModule } from 'src/modules/workflow/workflow-builder/workflow-builder.module';
import { WorkflowVersionModule } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.module';
import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/workflow-trigger.module';
import { WorkflowTriggerController } from 'src/engine/core-modules/workflow/controllers/workflow-trigger.controller';
@Module({
imports: [
@ -16,6 +17,7 @@ import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/wor
WorkflowCommonModule,
WorkflowVersionModule,
],
controllers: [WorkflowTriggerController],
providers: [
WorkflowTriggerResolver,
WorkflowBuilderResolver,