Create record action (#8460)

- Add create record action
- Migrate all step name => action in a common folder
- Separate types
This commit is contained in:
Thomas Trompette
2024-11-13 15:21:40 +01:00
committed by GitHub
parent ba79a1d324
commit faeea2b887
27 changed files with 268 additions and 128 deletions

View File

@ -2,8 +2,8 @@ import { Field, InputType } from '@nestjs/graphql';
import graphqlTypeJson from 'graphql-type-json'; import graphqlTypeJson from 'graphql-type-json';
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
import { WorkflowStep } from 'src/modules/workflow/workflow-executor/types/workflow-action.type';
@InputType() @InputType()
export class ComputeStepOutputSchemaInput { export class ComputeStepOutputSchemaInput {
@ -11,5 +11,5 @@ export class ComputeStepOutputSchemaInput {
description: 'Step JSON format', description: 'Step JSON format',
nullable: false, nullable: false,
}) })
step: WorkflowTrigger | WorkflowStep; step: WorkflowTrigger | WorkflowAction;
} }

View File

@ -10,7 +10,7 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { WorkflowBuilderWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-builder.workspace-service'; import { WorkflowBuilderWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-builder.workspace-service';
import { OutputSchema } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type'; import { OutputSchema } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
@Resolver() @Resolver()
@UseGuards(WorkspaceAuthGuard, UserAuthGuard) @UseGuards(WorkspaceAuthGuard, UserAuthGuard)

View File

@ -21,7 +21,7 @@ import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/f
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity'; import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity'; import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity'; import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity';
import { WorkflowStep } from 'src/modules/workflow/workflow-executor/types/workflow-action.type'; import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
export enum WorkflowVersionStatus { export enum WorkflowVersionStatus {
@ -98,7 +98,7 @@ export class WorkflowVersionWorkspaceEntity extends BaseWorkspaceEntity {
icon: 'IconSettingsAutomation', icon: 'IconSettingsAutomation',
}) })
@WorkspaceIsNullable() @WorkspaceIsNullable()
steps: WorkflowStep[] | null; steps: WorkflowAction[] | null;
@WorkspaceField({ @WorkspaceField({
standardId: WORKFLOW_VERSION_STANDARD_FIELD_IDS.status, standardId: WORKFLOW_VERSION_STANDARD_FIELD_IDS.status,

View File

@ -5,24 +5,24 @@ import { join } from 'path';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { checkStringIsDatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/utils/check-string-is-database-event-action';
import { INDEX_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/constants/index-file-name'; import { INDEX_FILE_NAME } from 'src/engine/core-modules/serverless/drivers/constants/index-file-name';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
import { CodeIntrospectionService } from 'src/modules/code-introspection/code-introspection.service'; import { CodeIntrospectionService } from 'src/modules/code-introspection/code-introspection.service';
import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record'; import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record';
import { generateFakeObjectRecordEvent } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event';
import { WorkflowSendEmailStepOutputSchema } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email.workflow-action';
import { import {
WorkflowAction,
WorkflowActionType, WorkflowActionType,
WorkflowStep, } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
} from 'src/modules/workflow/workflow-executor/types/workflow-action.type';
import { WorkflowSendEmailStepOutputSchema } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
import { import {
WorkflowTrigger, WorkflowTrigger,
WorkflowTriggerType, WorkflowTriggerType,
} from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
import { isDefined } from 'src/utils/is-defined'; import { isDefined } from 'src/utils/is-defined';
import { checkStringIsDatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/utils/check-string-is-database-event-action';
import { generateFakeObjectRecordEvent } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record-event';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
@Injectable() @Injectable()
export class WorkflowBuilderWorkspaceService { export class WorkflowBuilderWorkspaceService {
@ -37,7 +37,7 @@ export class WorkflowBuilderWorkspaceService {
step, step,
workspaceId, workspaceId,
}: { }: {
step: WorkflowTrigger | WorkflowStep; step: WorkflowTrigger | WorkflowAction;
workspaceId: string; workspaceId: string;
}): Promise<object> { }): Promise<object> {
const stepType = step.type; const stepType = step.type;
@ -57,7 +57,7 @@ export class WorkflowBuilderWorkspaceService {
return {}; return {};
} }
return await this.computeManualTriggerOutputSchema({ return await this.computeRecordOutputSchema({
objectType, objectType,
workspaceId, workspaceId,
objectMetadataRepository: this.objectMetadataRepository, objectMetadataRepository: this.objectMetadataRepository,
@ -78,6 +78,12 @@ export class WorkflowBuilderWorkspaceService {
codeIntrospectionService: this.codeIntrospectionService, codeIntrospectionService: this.codeIntrospectionService,
}); });
} }
case WorkflowActionType.RECORD_CRUD:
return await this.computeRecordOutputSchema({
objectType: step.settings.input.objectName,
workspaceId,
objectMetadataRepository: this.objectMetadataRepository,
});
default: default:
return {}; return {};
} }
@ -116,7 +122,7 @@ export class WorkflowBuilderWorkspaceService {
); );
} }
private async computeManualTriggerOutputSchema<Entity>({ private async computeRecordOutputSchema<Entity>({
objectType, objectType,
workspaceId, workspaceId,
objectMetadataRepository, objectMetadataRepository,

View File

@ -2,19 +2,21 @@ import { Injectable } from '@nestjs/common';
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface'; import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/types/workflow-action.type';
import { import {
WorkflowStepExecutorException, WorkflowStepExecutorException,
WorkflowStepExecutorExceptionCode, WorkflowStepExecutorExceptionCode,
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception'; } from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
import { CodeWorkflowAction } from 'src/modules/serverless/workflow-actions/code.workflow-action'; import { CodeWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/code/code.workflow-action';
import { SendEmailWorkflowAction } from 'src/modules/mail-sender/workflow-actions/send-email.workflow-action'; import { SendEmailWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email.workflow-action';
import { RecordCRUDWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud.workflow-action';
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
@Injectable() @Injectable()
export class WorkflowActionFactory { export class WorkflowActionFactory {
constructor( constructor(
private readonly codeWorkflowAction: CodeWorkflowAction, private readonly codeWorkflowAction: CodeWorkflowAction,
private readonly sendEmailWorkflowAction: SendEmailWorkflowAction, private readonly sendEmailWorkflowAction: SendEmailWorkflowAction,
private readonly recordCRUDWorkflowAction: RecordCRUDWorkflowAction,
) {} ) {}
get(stepType: WorkflowActionType): WorkflowAction { get(stepType: WorkflowActionType): WorkflowAction {
@ -23,6 +25,8 @@ export class WorkflowActionFactory {
return this.codeWorkflowAction; return this.codeWorkflowAction;
case WorkflowActionType.SEND_EMAIL: case WorkflowActionType.SEND_EMAIL:
return this.sendEmailWorkflowAction; return this.sendEmailWorkflowAction;
case WorkflowActionType.RECORD_CRUD:
return this.recordCRUDWorkflowAction;
default: default:
throw new WorkflowStepExecutorException( throw new WorkflowStepExecutorException(
`Workflow step executor not found for step type '${stepType}'`, `Workflow step executor not found for step type '${stepType}'`,

View File

@ -1,4 +1,4 @@
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/types/workflow-action-result.type'; import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
export interface WorkflowAction { export interface WorkflowAction {
execute(workflowStepInput: unknown): Promise<WorkflowActionResult>; execute(workflowStepInput: unknown): Promise<WorkflowActionResult>;

View File

@ -1,30 +0,0 @@
import {
WorkflowCodeStepSettings,
WorkflowSendEmailStepSettings,
WorkflowStepSettings,
} from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type';
export enum WorkflowActionType {
CODE = 'CODE',
SEND_EMAIL = 'SEND_EMAIL',
}
type BaseWorkflowStep = {
id: string;
name: string;
type: WorkflowActionType;
settings: WorkflowStepSettings;
valid: boolean;
};
export type WorkflowCodeStep = BaseWorkflowStep & {
type: WorkflowActionType.CODE;
settings: WorkflowCodeStepSettings;
};
export type WorkflowSendEmailStep = BaseWorkflowStep & {
type: WorkflowActionType.SEND_EMAIL;
settings: WorkflowSendEmailStepSettings;
};
export type WorkflowStep = WorkflowCodeStep | WorkflowSendEmailStep;

View File

@ -1,45 +0,0 @@
export type OutputSchema = object;
type BaseWorkflowStepSettings = {
input: object;
outputSchema: OutputSchema;
errorHandlingOptions: {
retryOnFailure: {
value: boolean;
};
continueOnFailure: {
value: boolean;
};
};
};
export type WorkflowCodeStepInput = {
serverlessFunctionId: string;
serverlessFunctionVersion: string;
serverlessFunctionInput: {
[key: string]: any;
};
};
export type WorkflowCodeStepSettings = BaseWorkflowStepSettings & {
input: WorkflowCodeStepInput;
};
export type WorkflowSendEmailStepInput = {
connectedAccountId: string;
email: string;
subject?: string;
body?: string;
};
export type WorkflowSendEmailStepOutputSchema = {
success: boolean;
};
export type WorkflowSendEmailStepSettings = BaseWorkflowStepSettings & {
input: WorkflowSendEmailStepInput;
};
export type WorkflowStepSettings =
| WorkflowSendEmailStepSettings
| WorkflowCodeStepSettings;

View File

@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
import { CodeWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/code/code.workflow-action';
@Module({
imports: [ServerlessFunctionModule],
providers: [ScopedWorkspaceContextFactory, CodeWorkflowAction],
exports: [CodeWorkflowAction],
})
export class CodeActionModule {}

View File

@ -8,8 +8,8 @@ import {
WorkflowStepExecutorException, WorkflowStepExecutorException,
WorkflowStepExecutorExceptionCode, WorkflowStepExecutorExceptionCode,
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception'; } from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/types/workflow-action-result.type'; import { WorkflowCodeActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-input.type';
import { WorkflowCodeStepInput } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type'; import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
@Injectable() @Injectable()
export class CodeWorkflowAction implements WorkflowAction { export class CodeWorkflowAction implements WorkflowAction {
@ -19,7 +19,7 @@ export class CodeWorkflowAction implements WorkflowAction {
) {} ) {}
async execute( async execute(
workflowStepInput: WorkflowCodeStepInput, workflowActionInput: WorkflowCodeActionInput,
): Promise<WorkflowActionResult> { ): Promise<WorkflowActionResult> {
try { try {
const { workspaceId } = this.scopedWorkspaceContextFactory.create(); const { workspaceId } = this.scopedWorkspaceContextFactory.create();
@ -33,9 +33,9 @@ export class CodeWorkflowAction implements WorkflowAction {
const result = const result =
await this.serverlessFunctionService.executeOneServerlessFunction( await this.serverlessFunctionService.executeOneServerlessFunction(
workflowStepInput.serverlessFunctionId, workflowActionInput.serverlessFunctionId,
workspaceId, workspaceId,
workflowStepInput.serverlessFunctionInput, workflowActionInput.serverlessFunctionInput,
); );
if (result.error) { if (result.error) {

View File

@ -0,0 +1,7 @@
export type WorkflowCodeActionInput = {
serverlessFunctionId: string;
serverlessFunctionVersion: string;
serverlessFunctionInput: {
[key: string]: any;
};
};

View File

@ -0,0 +1,6 @@
import { WorkflowCodeActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-input.type';
import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
export type WorkflowCodeActionSettings = BaseWorkflowActionSettings & {
input: WorkflowCodeActionInput;
};

View File

@ -1,13 +1,13 @@
import { CustomException } from 'src/utils/custom-exception'; import { CustomException } from 'src/utils/custom-exception';
export class MailSenderException extends CustomException { export class SendEmailActionException extends CustomException {
code: MailSenderExceptionCode; code: SendEmailActionExceptionCode;
constructor(message: string, code: MailSenderExceptionCode) { constructor(message: string, code: SendEmailActionExceptionCode) {
super(message, code); super(message, code);
} }
} }
export enum MailSenderExceptionCode { export enum SendEmailActionExceptionCode {
PROVIDER_NOT_SUPPORTED = 'PROVIDER_NOT_SUPPORTED', PROVIDER_NOT_SUPPORTED = 'PROVIDER_NOT_SUPPORTED',
CONNECTED_ACCOUNT_NOT_FOUND = 'CONNECTED_ACCOUNT_NOT_FOUND', CONNECTED_ACCOUNT_NOT_FOUND = 'CONNECTED_ACCOUNT_NOT_FOUND',
} }

View File

@ -0,0 +1,17 @@
import { Module } from '@nestjs/common';
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2-client-manager/oauth2-client-manager.module';
import { GmailClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/gmail-client.provider';
import { SendEmailWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email.workflow-action';
@Module({
imports: [OAuth2ClientManagerModule],
providers: [
GmailClientProvider,
ScopedWorkspaceContextFactory,
SendEmailWorkflowAction,
],
exports: [SendEmailWorkflowAction],
})
export class SendEmailActionModule {}

View File

@ -9,22 +9,23 @@ import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interface
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory'; import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import {
MailSenderException,
MailSenderExceptionCode,
} from 'src/modules/mail-sender/exceptions/mail-sender.exception';
import { GmailClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/gmail-client.provider'; import { GmailClientProvider } from 'src/modules/messaging/message-import-manager/drivers/gmail/providers/gmail-client.provider';
import { import {
WorkflowStepExecutorException, WorkflowStepExecutorException,
WorkflowStepExecutorExceptionCode, WorkflowStepExecutorExceptionCode,
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception'; } from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/types/workflow-action-result.type';
import { import {
WorkflowSendEmailStepInput, SendEmailActionException,
WorkflowSendEmailStepOutputSchema, SendEmailActionExceptionCode,
} from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type'; } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/exceptions/send-email-action.exception';
import { WorkflowSendEmailActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-input.type';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
import { isDefined } from 'src/utils/is-defined'; import { isDefined } from 'src/utils/is-defined';
export type WorkflowSendEmailStepOutputSchema = {
success: boolean;
};
@Injectable() @Injectable()
export class SendEmailWorkflowAction implements WorkflowAction { export class SendEmailWorkflowAction implements WorkflowAction {
private readonly logger = new Logger(SendEmailWorkflowAction.name); private readonly logger = new Logger(SendEmailWorkflowAction.name);
@ -54,9 +55,9 @@ export class SendEmailWorkflowAction implements WorkflowAction {
}); });
if (!isDefined(connectedAccount)) { if (!isDefined(connectedAccount)) {
throw new MailSenderException( throw new SendEmailActionException(
`Connected Account '${connectedAccountId}' not found`, `Connected Account '${connectedAccountId}' not found`,
MailSenderExceptionCode.CONNECTED_ACCOUNT_NOT_FOUND, SendEmailActionExceptionCode.CONNECTED_ACCOUNT_NOT_FOUND,
); );
} }
@ -64,20 +65,20 @@ export class SendEmailWorkflowAction implements WorkflowAction {
case 'google': case 'google':
return await this.gmailClientProvider.getGmailClient(connectedAccount); return await this.gmailClientProvider.getGmailClient(connectedAccount);
default: default:
throw new MailSenderException( throw new SendEmailActionException(
`Provider ${connectedAccount.provider} is not supported`, `Provider ${connectedAccount.provider} is not supported`,
MailSenderExceptionCode.PROVIDER_NOT_SUPPORTED, SendEmailActionExceptionCode.PROVIDER_NOT_SUPPORTED,
); );
} }
} }
async execute( async execute(
workflowStepInput: WorkflowSendEmailStepInput, workflowActionInput: WorkflowSendEmailActionInput,
): Promise<WorkflowActionResult> { ): Promise<WorkflowActionResult> {
const emailProvider = await this.getEmailClient( const emailProvider = await this.getEmailClient(
workflowStepInput.connectedAccountId, workflowActionInput.connectedAccountId,
); );
const { email, body, subject } = workflowStepInput; const { email, body, subject } = workflowActionInput;
try { try {
const emailSchema = z.string().trim().email('Invalid email'); const emailSchema = z.string().trim().email('Invalid email');

View File

@ -0,0 +1,6 @@
export type WorkflowSendEmailActionInput = {
connectedAccountId: string;
email: string;
subject?: string;
body?: string;
};

View File

@ -0,0 +1,6 @@
import { WorkflowSendEmailActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-input.type';
import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
export type WorkflowSendEmailActionSettings = BaseWorkflowActionSettings & {
input: WorkflowSendEmailActionInput;
};

View File

@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { RecordCRUDWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud.workflow-action';
@Module({
providers: [RecordCRUDWorkflowAction],
exports: [RecordCRUDWorkflowAction],
})
export class RecordCRUDActionModule {}

View File

@ -0,0 +1,45 @@
import { Injectable } from '@nestjs/common';
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import {
WorkflowCreateRecordActionInput,
WorkflowRecordCRUDActionInput,
WorkflowRecordCRUDType,
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
@Injectable()
export class RecordCRUDWorkflowAction implements WorkflowAction {
constructor(private readonly twentyORMManager: TwentyORMManager) {}
async execute(
workflowActionInput: WorkflowRecordCRUDActionInput,
): Promise<WorkflowActionResult> {
switch (workflowActionInput.type) {
case WorkflowRecordCRUDType.CREATE:
return this.createRecord(workflowActionInput);
default:
throw new Error(
`Unknown record operation type: ${workflowActionInput.type}`,
);
}
}
private async createRecord(
workflowActionInput: WorkflowCreateRecordActionInput,
): Promise<WorkflowActionResult> {
const repository = await this.twentyORMManager.getRepository(
workflowActionInput.objectName,
);
const objectRecord = await repository.create(
workflowActionInput.objectRecord,
);
const createdObjectRecord = await repository.save(objectRecord);
return { result: createdObjectRecord };
}
}

View File

@ -0,0 +1,31 @@
type ObjectRecord = Record<string, any>;
export enum WorkflowRecordCRUDType {
CREATE = 'create',
UPDATE = 'update',
DELETE = 'delete',
}
export type WorkflowCreateRecordActionInput = {
type: WorkflowRecordCRUDType.CREATE;
objectName: string;
objectRecord: ObjectRecord;
};
export type WorkflowUpdateRecordActionInput = {
type: WorkflowRecordCRUDType.UPDATE;
objectName: string;
objectRecord: ObjectRecord;
objectRecordId: string;
};
export type WorkflowDeleteRecordActionInput = {
type: WorkflowRecordCRUDType.DELETE;
objectName: string;
objectRecordId: string;
};
export type WorkflowRecordCRUDActionInput =
| WorkflowCreateRecordActionInput
| WorkflowUpdateRecordActionInput
| WorkflowDeleteRecordActionInput;

View File

@ -0,0 +1,6 @@
import { WorkflowRecordCRUDActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
export type WorkflowRecordCRUDActionSettings = BaseWorkflowActionSettings & {
input: WorkflowRecordCRUDActionInput;
};

View File

@ -0,0 +1,23 @@
import { WorkflowCodeActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-settings.type';
import { WorkflowSendEmailActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-settings.type';
import { WorkflowRecordCRUDActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-settings.type';
export type OutputSchema = object;
export type BaseWorkflowActionSettings = {
input: object;
outputSchema: OutputSchema;
errorHandlingOptions: {
retryOnFailure: {
value: boolean;
};
continueOnFailure: {
value: boolean;
};
};
};
export type WorkflowActionSettings =
| WorkflowSendEmailActionSettings
| WorkflowCodeActionSettings
| WorkflowRecordCRUDActionSettings;

View File

@ -0,0 +1,38 @@
import { WorkflowCodeActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-settings.type';
import { WorkflowSendEmailActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-settings.type';
import { WorkflowRecordCRUDActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-settings.type';
import { WorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
export enum WorkflowActionType {
CODE = 'CODE',
SEND_EMAIL = 'SEND_EMAIL',
RECORD_CRUD = 'RECORD_CRUD',
}
type BaseWorkflowAction = {
id: string;
name: string;
type: WorkflowActionType;
settings: WorkflowActionSettings;
valid: boolean;
};
export type WorkflowCodeAction = BaseWorkflowAction & {
type: WorkflowActionType.CODE;
settings: WorkflowCodeActionSettings;
};
export type WorkflowSendEmailAction = BaseWorkflowAction & {
type: WorkflowActionType.SEND_EMAIL;
settings: WorkflowSendEmailActionSettings;
};
export type WorkflowRecordCRUDAction = BaseWorkflowAction & {
type: WorkflowActionType.RECORD_CRUD;
settings: WorkflowRecordCRUDActionSettings;
};
export type WorkflowAction =
| WorkflowCodeAction
| WorkflowSendEmailAction
| WorkflowRecordCRUDAction;

View File

@ -1,26 +1,24 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module';
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory'; import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
import { SendEmailWorkflowAction } from 'src/modules/mail-sender/workflow-actions/send-email.workflow-action';
import { MessagingGmailDriverModule } from 'src/modules/messaging/message-import-manager/drivers/gmail/messaging-gmail-driver.module';
import { CodeWorkflowAction } from 'src/modules/serverless/workflow-actions/code.workflow-action';
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module'; import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
import { WorkflowActionFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-action.factory'; import { WorkflowActionFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-action.factory';
import { CodeActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/code/code-action.module';
import { SendEmailActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email-action.module';
import { RecordCRUDActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud-action.module';
import { WorkflowExecutorWorkspaceService } from 'src/modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service'; import { WorkflowExecutorWorkspaceService } from 'src/modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service';
@Module({ @Module({
imports: [ imports: [
WorkflowCommonModule, WorkflowCommonModule,
ServerlessFunctionModule, CodeActionModule,
MessagingGmailDriverModule, SendEmailActionModule,
RecordCRUDActionModule,
], ],
providers: [ providers: [
WorkflowExecutorWorkspaceService, WorkflowExecutorWorkspaceService,
ScopedWorkspaceContextFactory, ScopedWorkspaceContextFactory,
WorkflowActionFactory, WorkflowActionFactory,
CodeWorkflowAction,
SendEmailWorkflowAction,
], ],
exports: [WorkflowExecutorWorkspaceService], exports: [WorkflowExecutorWorkspaceService],
}) })

View File

@ -5,8 +5,8 @@ import {
WorkflowRunStatus, WorkflowRunStatus,
} from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity'; } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
import { WorkflowActionFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-action.factory'; import { WorkflowActionFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-action.factory';
import { WorkflowStep } from 'src/modules/workflow/workflow-executor/types/workflow-action.type';
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util'; import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
const MAX_RETRIES_ON_FAILURE = 3; const MAX_RETRIES_ON_FAILURE = 3;
@ -27,7 +27,7 @@ export class WorkflowExecutorWorkspaceService {
attemptCount = 1, attemptCount = 1,
}: { }: {
currentStepIndex: number; currentStepIndex: number;
steps: WorkflowStep[]; steps: WorkflowAction[];
output: WorkflowExecutorOutput; output: WorkflowExecutorOutput;
context: Record<string, unknown>; context: Record<string, unknown>;
attemptCount?: number; attemptCount?: number;

View File

@ -1,4 +1,4 @@
import { OutputSchema } from 'src/modules/workflow/workflow-executor/types/workflow-step-settings.type'; import { OutputSchema } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
export enum WorkflowTriggerType { export enum WorkflowTriggerType {
DATABASE_EVENT = 'DATABASE_EVENT', DATABASE_EVENT = 'DATABASE_EVENT',