Migrate workflow actions to executors (#10432)
Actions will now: - receive the complete input - get the step they want to execute by themself - check that the type is the right one - resolve variables These all share a common executor interface. It will allow for actions with a special execution process (forms, loop, router) to have all required informations. Main workflow executor should: - find the right executor to call for current step - store the output and context from step execution - call next step index
This commit is contained in:
@ -22,6 +22,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 { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity';
|
import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.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 { WorkflowExecutorOutput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-output.type';
|
||||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/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';
|
||||||
|
|
||||||
@ -32,13 +33,9 @@ export enum WorkflowRunStatus {
|
|||||||
FAILED = 'FAILED',
|
FAILED = 'FAILED',
|
||||||
}
|
}
|
||||||
|
|
||||||
type StepRunOutput = {
|
export type StepOutput = {
|
||||||
id: string;
|
id: string;
|
||||||
outputs: {
|
output: WorkflowExecutorOutput;
|
||||||
attemptCount: number;
|
|
||||||
result: object | undefined;
|
|
||||||
error: string | undefined;
|
|
||||||
}[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkflowRunOutput = {
|
export type WorkflowRunOutput = {
|
||||||
@ -46,7 +43,7 @@ export type WorkflowRunOutput = {
|
|||||||
trigger: WorkflowTrigger;
|
trigger: WorkflowTrigger;
|
||||||
steps: WorkflowAction[];
|
steps: WorkflowAction[];
|
||||||
};
|
};
|
||||||
stepsOutput?: Record<string, StepRunOutput>;
|
stepsOutput?: Record<string, WorkflowExecutorOutput>;
|
||||||
error?: string;
|
error?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/workflow-executor.interface';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
WorkflowStepExecutorException,
|
WorkflowStepExecutorException,
|
||||||
@ -10,22 +10,22 @@ import { CodeWorkflowAction } from 'src/modules/workflow/workflow-executor/workf
|
|||||||
import { SendEmailWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email.workflow-action';
|
import { SendEmailWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/send-email.workflow-action';
|
||||||
import { CreateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action';
|
import { CreateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action';
|
||||||
import { DeleteRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action';
|
import { DeleteRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action';
|
||||||
import { FindRecordsWorflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action';
|
import { FindRecordsWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action';
|
||||||
import { UpdateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action';
|
import { UpdateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action';
|
||||||
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkflowActionFactory {
|
export class WorkflowExecutorFactory {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly codeWorkflowAction: CodeWorkflowAction,
|
private readonly codeWorkflowAction: CodeWorkflowAction,
|
||||||
private readonly sendEmailWorkflowAction: SendEmailWorkflowAction,
|
private readonly sendEmailWorkflowAction: SendEmailWorkflowAction,
|
||||||
private readonly createRecordWorkflowAction: CreateRecordWorkflowAction,
|
private readonly createRecordWorkflowAction: CreateRecordWorkflowAction,
|
||||||
private readonly updateRecordWorkflowAction: UpdateRecordWorkflowAction,
|
private readonly updateRecordWorkflowAction: UpdateRecordWorkflowAction,
|
||||||
private readonly deleteRecordWorkflowAction: DeleteRecordWorkflowAction,
|
private readonly deleteRecordWorkflowAction: DeleteRecordWorkflowAction,
|
||||||
private readonly findRecordsWorflowAction: FindRecordsWorflowAction,
|
private readonly findRecordsWorkflowAction: FindRecordsWorkflowAction,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get(stepType: WorkflowActionType): WorkflowAction {
|
get(stepType: WorkflowActionType): WorkflowExecutor {
|
||||||
switch (stepType) {
|
switch (stepType) {
|
||||||
case WorkflowActionType.CODE:
|
case WorkflowActionType.CODE:
|
||||||
return this.codeWorkflowAction;
|
return this.codeWorkflowAction;
|
||||||
@ -38,7 +38,7 @@ export class WorkflowActionFactory {
|
|||||||
case WorkflowActionType.DELETE_RECORD:
|
case WorkflowActionType.DELETE_RECORD:
|
||||||
return this.deleteRecordWorkflowAction;
|
return this.deleteRecordWorkflowAction;
|
||||||
case WorkflowActionType.FIND_RECORDS:
|
case WorkflowActionType.FIND_RECORDS:
|
||||||
return this.findRecordsWorflowAction;
|
return this.findRecordsWorkflowAction;
|
||||||
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}'`,
|
||||||
@ -1,5 +0,0 @@
|
|||||||
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
|
|
||||||
|
|
||||||
export interface WorkflowAction {
|
|
||||||
execute(workflowStepInput: unknown): Promise<WorkflowActionResult>;
|
|
||||||
}
|
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { WorkflowExecutorInput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-input';
|
||||||
|
import { WorkflowExecutorOutput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-output.type';
|
||||||
|
|
||||||
|
export interface WorkflowExecutor {
|
||||||
|
execute(
|
||||||
|
workflowExecutorInput: WorkflowExecutorInput,
|
||||||
|
): Promise<WorkflowExecutorOutput>;
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
|
export type WorkflowExecutorInput = {
|
||||||
|
currentStepIndex: number;
|
||||||
|
steps: WorkflowAction[];
|
||||||
|
context: Record<string, unknown>;
|
||||||
|
workflowRunId: string;
|
||||||
|
attemptCount?: number;
|
||||||
|
};
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
export type WorkflowExecutorOutput = {
|
||||||
|
result?: object;
|
||||||
|
error?: string;
|
||||||
|
};
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/workflow-executor.interface';
|
||||||
|
|
||||||
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 { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||||
@ -8,19 +8,38 @@ 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 { WorkflowExecutorInput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-input';
|
||||||
|
import { WorkflowExecutorOutput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-output.type';
|
||||||
|
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||||
|
import { isWorkflowCodeAction } from 'src/modules/workflow/workflow-executor/workflow-actions/code/guards/is-workflow-code-action.guard';
|
||||||
import { WorkflowCodeActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-input.type';
|
import { WorkflowCodeActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/code/types/workflow-code-action-input.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 WorkflowExecutor {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly serverlessFunctionService: ServerlessFunctionService,
|
private readonly serverlessFunctionService: ServerlessFunctionService,
|
||||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(
|
async execute({
|
||||||
workflowActionInput: WorkflowCodeActionInput,
|
currentStepIndex,
|
||||||
): Promise<WorkflowActionResult> {
|
steps,
|
||||||
|
context,
|
||||||
|
}: WorkflowExecutorInput): Promise<WorkflowExecutorOutput> {
|
||||||
|
const step = steps[currentStepIndex];
|
||||||
|
|
||||||
|
if (!isWorkflowCodeAction(step)) {
|
||||||
|
throw new WorkflowStepExecutorException(
|
||||||
|
'Step is not a code action',
|
||||||
|
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowActionInput = resolveInput(
|
||||||
|
step.settings.input,
|
||||||
|
context,
|
||||||
|
) as WorkflowCodeActionInput;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
const { workspaceId } = this.scopedWorkspaceContextFactory.create();
|
||||||
|
|
||||||
@ -40,7 +59,7 @@ export class CodeWorkflowAction implements WorkflowAction {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
return { error: result.error };
|
return { error: result.error.errorMessage };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { result: result.data || {} };
|
return { result: result.data || {} };
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
WorkflowAction,
|
||||||
|
WorkflowActionType,
|
||||||
|
WorkflowCodeAction,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
|
export const isWorkflowCodeAction = (
|
||||||
|
action: WorkflowAction,
|
||||||
|
): action is WorkflowCodeAction => {
|
||||||
|
return action.type === WorkflowActionType.CODE;
|
||||||
|
};
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
WorkflowAction,
|
||||||
|
WorkflowActionType,
|
||||||
|
WorkflowSendEmailAction,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
|
export const isWorkflowSendEmailAction = (
|
||||||
|
action: WorkflowAction,
|
||||||
|
): action is WorkflowSendEmailAction => {
|
||||||
|
return action.type === WorkflowActionType.SEND_EMAIL;
|
||||||
|
};
|
||||||
@ -5,7 +5,7 @@ import { JSDOM } from 'jsdom';
|
|||||||
import { isDefined, isValidUuid } from 'twenty-shared';
|
import { isDefined, isValidUuid } from 'twenty-shared';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/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';
|
||||||
@ -15,19 +15,22 @@ 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 { WorkflowExecutorInput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-input';
|
||||||
|
import { WorkflowExecutorOutput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-output.type';
|
||||||
|
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||||
import {
|
import {
|
||||||
SendEmailActionException,
|
SendEmailActionException,
|
||||||
SendEmailActionExceptionCode,
|
SendEmailActionExceptionCode,
|
||||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/exceptions/send-email-action.exception';
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/exceptions/send-email-action.exception';
|
||||||
|
import { isWorkflowSendEmailAction } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/guards/is-workflow-send-email-action.guard';
|
||||||
import { WorkflowSendEmailActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-input.type';
|
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';
|
|
||||||
|
|
||||||
export type WorkflowSendEmailStepOutputSchema = {
|
export type WorkflowSendEmailStepOutputSchema = {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SendEmailWorkflowAction implements WorkflowAction {
|
export class SendEmailWorkflowAction implements WorkflowExecutor {
|
||||||
private readonly logger = new Logger(SendEmailWorkflowAction.name);
|
private readonly logger = new Logger(SendEmailWorkflowAction.name);
|
||||||
constructor(
|
constructor(
|
||||||
private readonly gmailClientProvider: GmailClientProvider,
|
private readonly gmailClientProvider: GmailClientProvider,
|
||||||
@ -79,12 +82,29 @@ export class SendEmailWorkflowAction implements WorkflowAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(
|
async execute({
|
||||||
workflowActionInput: WorkflowSendEmailActionInput,
|
currentStepIndex,
|
||||||
): Promise<WorkflowActionResult> {
|
steps,
|
||||||
|
context,
|
||||||
|
}: WorkflowExecutorInput): Promise<WorkflowExecutorOutput> {
|
||||||
|
const step = steps[currentStepIndex];
|
||||||
|
|
||||||
|
if (!isWorkflowSendEmailAction(step)) {
|
||||||
|
throw new WorkflowStepExecutorException(
|
||||||
|
'Step is not a send email action',
|
||||||
|
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const emailProvider = await this.getEmailClient(
|
const emailProvider = await this.getEmailClient(
|
||||||
workflowActionInput.connectedAccountId,
|
step.settings.input.connectedAccountId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const workflowActionInput = resolveInput(
|
||||||
|
step.settings.input,
|
||||||
|
context,
|
||||||
|
) as WorkflowSendEmailActionInput;
|
||||||
|
|
||||||
const { email, body, subject } = workflowActionInput;
|
const { email, body, subject } = workflowActionInput;
|
||||||
|
|
||||||
const emailSchema = z.string().trim().email('Invalid email');
|
const emailSchema = z.string().trim().email('Invalid email');
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||||||
|
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/workflow-executor.interface';
|
||||||
|
|
||||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||||
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||||
@ -11,15 +11,22 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
|||||||
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 { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||||
|
import {
|
||||||
|
WorkflowStepExecutorException,
|
||||||
|
WorkflowStepExecutorExceptionCode,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||||
|
import { WorkflowExecutorInput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-input';
|
||||||
|
import { WorkflowExecutorOutput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-output.type';
|
||||||
|
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||||
import {
|
import {
|
||||||
RecordCRUDActionException,
|
RecordCRUDActionException,
|
||||||
RecordCRUDActionExceptionCode,
|
RecordCRUDActionExceptionCode,
|
||||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
|
||||||
|
import { isWorkflowCreateRecordAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/guards/is-workflow-create-record-action.guard';
|
||||||
import { WorkflowCreateRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
|
import { WorkflowCreateRecordActionInput } 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()
|
@Injectable()
|
||||||
export class CreateRecordWorkflowAction implements WorkflowAction {
|
export class CreateRecordWorkflowAction implements WorkflowExecutor {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly twentyORMManager: TwentyORMManager,
|
private readonly twentyORMManager: TwentyORMManager,
|
||||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||||
@ -28,12 +35,19 @@ export class CreateRecordWorkflowAction implements WorkflowAction {
|
|||||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(
|
async execute({
|
||||||
workflowActionInput: WorkflowCreateRecordActionInput,
|
currentStepIndex,
|
||||||
): Promise<WorkflowActionResult> {
|
steps,
|
||||||
const repository = await this.twentyORMManager.getRepository(
|
context,
|
||||||
workflowActionInput.objectName,
|
}: WorkflowExecutorInput): Promise<WorkflowExecutorOutput> {
|
||||||
);
|
const step = steps[currentStepIndex];
|
||||||
|
|
||||||
|
if (!isWorkflowCreateRecordAction(step)) {
|
||||||
|
throw new WorkflowStepExecutorException(
|
||||||
|
'Step is not a create record action',
|
||||||
|
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const workspaceId = this.scopedWorkspaceContextFactory.create().workspaceId;
|
const workspaceId = this.scopedWorkspaceContextFactory.create().workspaceId;
|
||||||
|
|
||||||
@ -44,6 +58,15 @@ export class CreateRecordWorkflowAction implements WorkflowAction {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const workflowActionInput = resolveInput(
|
||||||
|
step.settings.input,
|
||||||
|
context,
|
||||||
|
) as WorkflowCreateRecordActionInput;
|
||||||
|
|
||||||
|
const repository = await this.twentyORMManager.getRepository(
|
||||||
|
workflowActionInput.objectName,
|
||||||
|
);
|
||||||
|
|
||||||
const objectMetadata = await this.objectMetadataRepository.findOne({
|
const objectMetadata = await this.objectMetadataRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
nameSingular: workflowActionInput.objectName,
|
nameSingular: workflowActionInput.objectName,
|
||||||
|
|||||||
@ -3,22 +3,29 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||||||
|
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/workflow-executor.interface';
|
||||||
|
|
||||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||||
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 { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||||
|
import {
|
||||||
|
WorkflowStepExecutorException,
|
||||||
|
WorkflowStepExecutorExceptionCode,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||||
|
import { WorkflowExecutorInput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-input';
|
||||||
|
import { WorkflowExecutorOutput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-output.type';
|
||||||
|
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||||
import {
|
import {
|
||||||
RecordCRUDActionException,
|
RecordCRUDActionException,
|
||||||
RecordCRUDActionExceptionCode,
|
RecordCRUDActionExceptionCode,
|
||||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
|
||||||
|
import { isWorkflowDeleteRecordAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/guards/is-workflow-delete-record-action.guard';
|
||||||
import { WorkflowDeleteRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
|
import { WorkflowDeleteRecordActionInput } 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()
|
@Injectable()
|
||||||
export class DeleteRecordWorkflowAction implements WorkflowAction {
|
export class DeleteRecordWorkflowAction implements WorkflowExecutor {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly twentyORMManager: TwentyORMManager,
|
private readonly twentyORMManager: TwentyORMManager,
|
||||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||||
@ -27,9 +34,25 @@ export class DeleteRecordWorkflowAction implements WorkflowAction {
|
|||||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(
|
async execute({
|
||||||
workflowActionInput: WorkflowDeleteRecordActionInput,
|
currentStepIndex,
|
||||||
): Promise<WorkflowActionResult> {
|
steps,
|
||||||
|
context,
|
||||||
|
}: WorkflowExecutorInput): Promise<WorkflowExecutorOutput> {
|
||||||
|
const step = steps[currentStepIndex];
|
||||||
|
|
||||||
|
if (!isWorkflowDeleteRecordAction(step)) {
|
||||||
|
throw new WorkflowStepExecutorException(
|
||||||
|
'Step is not a delete record action',
|
||||||
|
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowActionInput = resolveInput(
|
||||||
|
step.settings.input,
|
||||||
|
context,
|
||||||
|
) as WorkflowDeleteRecordActionInput;
|
||||||
|
|
||||||
const repository = await this.twentyORMManager.getRepository(
|
const repository = await this.twentyORMManager.getRepository(
|
||||||
workflowActionInput.objectName,
|
workflowActionInput.objectName,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import {
|
|||||||
ObjectRecordOrderBy,
|
ObjectRecordOrderBy,
|
||||||
OrderByDirection,
|
OrderByDirection,
|
||||||
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/workflow-executor.interface';
|
||||||
|
|
||||||
import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/constants/query-max-records.constant';
|
import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/constants/query-max-records.constant';
|
||||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||||
@ -21,15 +21,22 @@ import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.
|
|||||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||||
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||||
|
import {
|
||||||
|
WorkflowStepExecutorException,
|
||||||
|
WorkflowStepExecutorExceptionCode,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||||
|
import { WorkflowExecutorInput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-input';
|
||||||
|
import { WorkflowExecutorOutput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-output.type';
|
||||||
|
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||||
import {
|
import {
|
||||||
RecordCRUDActionException,
|
RecordCRUDActionException,
|
||||||
RecordCRUDActionExceptionCode,
|
RecordCRUDActionExceptionCode,
|
||||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
|
||||||
|
import { isWorkflowFindRecordsAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/guards/is-workflow-find-records-action.guard';
|
||||||
import { WorkflowFindRecordsActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
|
import { WorkflowFindRecordsActionInput } 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()
|
@Injectable()
|
||||||
export class FindRecordsWorflowAction implements WorkflowAction {
|
export class FindRecordsWorkflowAction implements WorkflowExecutor {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly twentyORMManager: TwentyORMManager,
|
private readonly twentyORMManager: TwentyORMManager,
|
||||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||||
@ -37,12 +44,29 @@ export class FindRecordsWorflowAction implements WorkflowAction {
|
|||||||
private readonly featureFlagService: FeatureFlagService,
|
private readonly featureFlagService: FeatureFlagService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(
|
async execute({
|
||||||
workflowActionInput: WorkflowFindRecordsActionInput,
|
currentStepIndex,
|
||||||
): Promise<WorkflowActionResult> {
|
steps,
|
||||||
|
context,
|
||||||
|
}: WorkflowExecutorInput): Promise<WorkflowExecutorOutput> {
|
||||||
|
const step = steps[currentStepIndex];
|
||||||
|
|
||||||
|
if (!isWorkflowFindRecordsAction(step)) {
|
||||||
|
throw new WorkflowStepExecutorException(
|
||||||
|
'Step is not a find records action',
|
||||||
|
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowActionInput = resolveInput(
|
||||||
|
step.settings.input,
|
||||||
|
context,
|
||||||
|
) as WorkflowFindRecordsActionInput;
|
||||||
|
|
||||||
const repository = await this.twentyORMManager.getRepository(
|
const repository = await this.twentyORMManager.getRepository(
|
||||||
workflowActionInput.objectName,
|
workflowActionInput.objectName,
|
||||||
);
|
);
|
||||||
|
|
||||||
const workspaceId = this.scopedWorkspaceContextFactory.create().workspaceId;
|
const workspaceId = this.scopedWorkspaceContextFactory.create().workspaceId;
|
||||||
|
|
||||||
if (!workspaceId) {
|
if (!workspaceId) {
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
WorkflowAction,
|
||||||
|
WorkflowActionType,
|
||||||
|
WorkflowCreateRecordAction,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
|
export const isWorkflowCreateRecordAction = (
|
||||||
|
action: WorkflowAction,
|
||||||
|
): action is WorkflowCreateRecordAction => {
|
||||||
|
return action.type === WorkflowActionType.CREATE_RECORD;
|
||||||
|
};
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
WorkflowAction,
|
||||||
|
WorkflowActionType,
|
||||||
|
WorkflowDeleteRecordAction,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
|
export const isWorkflowDeleteRecordAction = (
|
||||||
|
action: WorkflowAction,
|
||||||
|
): action is WorkflowDeleteRecordAction => {
|
||||||
|
return action.type === WorkflowActionType.DELETE_RECORD;
|
||||||
|
};
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
WorkflowAction,
|
||||||
|
WorkflowActionType,
|
||||||
|
WorkflowFindRecordsAction,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
|
export const isWorkflowFindRecordsAction = (
|
||||||
|
action: WorkflowAction,
|
||||||
|
): action is WorkflowFindRecordsAction => {
|
||||||
|
return action.type === WorkflowActionType.FIND_RECORDS;
|
||||||
|
};
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
WorkflowAction,
|
||||||
|
WorkflowActionType,
|
||||||
|
WorkflowUpdateRecordAction,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||||
|
|
||||||
|
export const isWorkflowUpdateRecordAction = (
|
||||||
|
action: WorkflowAction,
|
||||||
|
): action is WorkflowUpdateRecordAction => {
|
||||||
|
return action.type === WorkflowActionType.UPDATE_RECORD;
|
||||||
|
};
|
||||||
@ -8,7 +8,7 @@ import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/s
|
|||||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||||
import { CreateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action';
|
import { CreateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action';
|
||||||
import { DeleteRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action';
|
import { DeleteRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action';
|
||||||
import { FindRecordsWorflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action';
|
import { FindRecordsWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action';
|
||||||
import { UpdateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action';
|
import { UpdateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/update-record.workflow-action';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@ -22,13 +22,13 @@ import { UpdateRecordWorkflowAction } from 'src/modules/workflow/workflow-execut
|
|||||||
CreateRecordWorkflowAction,
|
CreateRecordWorkflowAction,
|
||||||
UpdateRecordWorkflowAction,
|
UpdateRecordWorkflowAction,
|
||||||
DeleteRecordWorkflowAction,
|
DeleteRecordWorkflowAction,
|
||||||
FindRecordsWorflowAction,
|
FindRecordsWorkflowAction,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
CreateRecordWorkflowAction,
|
CreateRecordWorkflowAction,
|
||||||
UpdateRecordWorkflowAction,
|
UpdateRecordWorkflowAction,
|
||||||
DeleteRecordWorkflowAction,
|
DeleteRecordWorkflowAction,
|
||||||
FindRecordsWorflowAction,
|
FindRecordsWorkflowAction,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class RecordCRUDActionModule {}
|
export class RecordCRUDActionModule {}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||||||
|
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/workflow-executor.interface';
|
||||||
|
|
||||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
@ -13,15 +13,22 @@ import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
|||||||
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
||||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||||
|
import {
|
||||||
|
WorkflowStepExecutorException,
|
||||||
|
WorkflowStepExecutorExceptionCode,
|
||||||
|
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||||
|
import { WorkflowExecutorInput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-input';
|
||||||
|
import { WorkflowExecutorOutput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-output.type';
|
||||||
|
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
||||||
import {
|
import {
|
||||||
RecordCRUDActionException,
|
RecordCRUDActionException,
|
||||||
RecordCRUDActionExceptionCode,
|
RecordCRUDActionExceptionCode,
|
||||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
|
} from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/exceptions/record-crud-action.exception';
|
||||||
|
import { isWorkflowUpdateRecordAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/guards/is-workflow-update-record-action.guard';
|
||||||
import { WorkflowUpdateRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
|
import { WorkflowUpdateRecordActionInput } 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()
|
@Injectable()
|
||||||
export class UpdateRecordWorkflowAction implements WorkflowAction {
|
export class UpdateRecordWorkflowAction implements WorkflowExecutor {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly twentyORMManager: TwentyORMManager,
|
private readonly twentyORMManager: TwentyORMManager,
|
||||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||||
@ -31,9 +38,25 @@ export class UpdateRecordWorkflowAction implements WorkflowAction {
|
|||||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(
|
async execute({
|
||||||
workflowActionInput: WorkflowUpdateRecordActionInput,
|
currentStepIndex,
|
||||||
): Promise<WorkflowActionResult> {
|
steps,
|
||||||
|
context,
|
||||||
|
}: WorkflowExecutorInput): Promise<WorkflowExecutorOutput> {
|
||||||
|
const step = steps[currentStepIndex];
|
||||||
|
|
||||||
|
if (!isWorkflowUpdateRecordAction(step)) {
|
||||||
|
throw new WorkflowStepExecutorException(
|
||||||
|
'Step is not an update record action',
|
||||||
|
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowActionInput = resolveInput(
|
||||||
|
step.settings.input,
|
||||||
|
context,
|
||||||
|
) as WorkflowUpdateRecordActionInput;
|
||||||
|
|
||||||
const repository = await this.twentyORMManager.getRepository(
|
const repository = await this.twentyORMManager.getRepository(
|
||||||
workflowActionInput.objectName,
|
workflowActionInput.objectName,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
type WorkflowActionError = {
|
|
||||||
errorType: string;
|
|
||||||
errorMessage: string;
|
|
||||||
stackTrace: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type WorkflowActionResult = {
|
|
||||||
result?: object;
|
|
||||||
error?: WorkflowActionError;
|
|
||||||
};
|
|
||||||
@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
|
|||||||
|
|
||||||
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 { 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 { WorkflowExecutorFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-executor.factory';
|
||||||
import { CodeActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/code/code-action.module';
|
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 { 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 { RecordCRUDActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud-action.module';
|
||||||
@ -20,7 +20,7 @@ import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow
|
|||||||
providers: [
|
providers: [
|
||||||
WorkflowExecutorWorkspaceService,
|
WorkflowExecutorWorkspaceService,
|
||||||
ScopedWorkspaceContextFactory,
|
ScopedWorkspaceContextFactory,
|
||||||
WorkflowActionFactory,
|
WorkflowExecutorFactory,
|
||||||
],
|
],
|
||||||
exports: [WorkflowExecutorWorkspaceService],
|
exports: [WorkflowExecutorWorkspaceService],
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/workflow-executor.interface';
|
||||||
|
|
||||||
import { BILLING_FEATURE_USED } from 'src/engine/core-modules/billing/constants/billing-feature-used.constant';
|
import { BILLING_FEATURE_USED } from 'src/engine/core-modules/billing/constants/billing-feature-used.constant';
|
||||||
import { BillingMeterEventName } from 'src/engine/core-modules/billing/enums/billing-meter-event-names';
|
import { BillingMeterEventName } from 'src/engine/core-modules/billing/enums/billing-meter-event-names';
|
||||||
import { BillingUsageEvent } from 'src/engine/core-modules/billing/types/billing-usage-event.type';
|
import { BillingUsageEvent } from 'src/engine/core-modules/billing/types/billing-usage-event.type';
|
||||||
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 { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||||
import {
|
import {
|
||||||
|
StepOutput,
|
||||||
WorkflowRunOutput,
|
WorkflowRunOutput,
|
||||||
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 { WorkflowExecutorFactory } from 'src/modules/workflow/workflow-executor/factories/workflow-executor.factory';
|
||||||
import { resolveInput } from 'src/modules/workflow/workflow-executor/utils/variable-resolver.util';
|
import { WorkflowExecutorInput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-input';
|
||||||
import { WorkflowActionResult } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-result.type';
|
import { WorkflowExecutorOutput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-output.type';
|
||||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
|
||||||
import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service';
|
import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service';
|
||||||
|
|
||||||
const MAX_RETRIES_ON_FAILURE = 3;
|
const MAX_RETRIES_ON_FAILURE = 3;
|
||||||
@ -23,9 +25,9 @@ export type WorkflowExecutorState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkflowExecutorWorkspaceService {
|
export class WorkflowExecutorWorkspaceService implements WorkflowExecutor {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly workflowActionFactory: WorkflowActionFactory,
|
private readonly workflowExecutorFactory: WorkflowExecutorFactory,
|
||||||
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
private readonly workspaceEventEmitter: WorkspaceEventEmitter,
|
||||||
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
private readonly scopedWorkspaceContextFactory: ScopedWorkspaceContextFactory,
|
||||||
private readonly workflowRunWorkspaceService: WorkflowRunWorkspaceService,
|
private readonly workflowRunWorkspaceService: WorkflowRunWorkspaceService,
|
||||||
@ -35,84 +37,55 @@ export class WorkflowExecutorWorkspaceService {
|
|||||||
currentStepIndex,
|
currentStepIndex,
|
||||||
steps,
|
steps,
|
||||||
context,
|
context,
|
||||||
workflowExecutorState,
|
|
||||||
attemptCount = 1,
|
attemptCount = 1,
|
||||||
workflowRunId,
|
workflowRunId,
|
||||||
}: {
|
}: WorkflowExecutorInput): Promise<WorkflowExecutorOutput> {
|
||||||
currentStepIndex: number;
|
|
||||||
steps: WorkflowAction[];
|
|
||||||
workflowExecutorState: WorkflowExecutorState;
|
|
||||||
context: Record<string, unknown>;
|
|
||||||
attemptCount?: number;
|
|
||||||
workflowRunId: string;
|
|
||||||
}): Promise<WorkflowExecutorState> {
|
|
||||||
if (currentStepIndex >= steps.length) {
|
if (currentStepIndex >= steps.length) {
|
||||||
return { ...workflowExecutorState, status: WorkflowRunStatus.COMPLETED };
|
return {
|
||||||
|
result: {
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const step = steps[currentStepIndex];
|
const step = steps[currentStepIndex];
|
||||||
|
|
||||||
const workflowAction = this.workflowActionFactory.get(step.type);
|
const workflowExecutor = this.workflowExecutorFactory.get(step.type);
|
||||||
|
|
||||||
const actionPayload = resolveInput(step.settings.input, context);
|
let actionOutput: WorkflowExecutorOutput;
|
||||||
|
|
||||||
let result: WorkflowActionResult;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = await workflowAction.execute(actionPayload);
|
actionOutput = await workflowExecutor.execute({
|
||||||
|
currentStepIndex,
|
||||||
|
steps,
|
||||||
|
context,
|
||||||
|
attemptCount,
|
||||||
|
workflowRunId,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
result = {
|
actionOutput = {
|
||||||
error: {
|
error: error.message ?? 'Execution result error, no data or error',
|
||||||
errorType: error.name,
|
|
||||||
errorMessage: error.message,
|
|
||||||
stackTrace: error.stack,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const stepOutput = workflowExecutorState.stepsOutput?.[step.id];
|
if (!actionOutput.error) {
|
||||||
|
|
||||||
const error =
|
|
||||||
result.error?.errorMessage ??
|
|
||||||
(result.result ? undefined : 'Execution result error, no data or error');
|
|
||||||
|
|
||||||
if (!error) {
|
|
||||||
this.sendWorkflowNodeRunEvent();
|
this.sendWorkflowNodeRunEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedStepOutput = {
|
const stepOutput: StepOutput = {
|
||||||
id: step.id,
|
id: step.id,
|
||||||
outputs: [
|
output: actionOutput,
|
||||||
...(stepOutput?.outputs ?? []),
|
|
||||||
{
|
|
||||||
attemptCount,
|
|
||||||
result: result.result,
|
|
||||||
error,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatedStepsOutput = {
|
if (actionOutput.result) {
|
||||||
...workflowExecutorState.stepsOutput,
|
|
||||||
[step.id]: updatedStepOutput,
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatedWorkflowExecutorState = {
|
|
||||||
...workflowExecutorState,
|
|
||||||
stepsOutput: updatedStepsOutput,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (result.result) {
|
|
||||||
const updatedContext = {
|
const updatedContext = {
|
||||||
...context,
|
...context,
|
||||||
[step.id]: result.result,
|
[step.id]: actionOutput.result,
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.workflowRunWorkspaceService.saveWorkflowRunState({
|
await this.workflowRunWorkspaceService.saveWorkflowRunState({
|
||||||
workflowRunId,
|
workflowRunId,
|
||||||
output: {
|
stepOutput,
|
||||||
stepsOutput: updatedStepsOutput,
|
|
||||||
},
|
|
||||||
context: updatedContext,
|
context: updatedContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,16 +94,13 @@ export class WorkflowExecutorWorkspaceService {
|
|||||||
currentStepIndex: currentStepIndex + 1,
|
currentStepIndex: currentStepIndex + 1,
|
||||||
steps,
|
steps,
|
||||||
context: updatedContext,
|
context: updatedContext,
|
||||||
workflowExecutorState: updatedWorkflowExecutorState,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (step.settings.errorHandlingOptions.continueOnFailure.value) {
|
if (step.settings.errorHandlingOptions.continueOnFailure.value) {
|
||||||
await this.workflowRunWorkspaceService.saveWorkflowRunState({
|
await this.workflowRunWorkspaceService.saveWorkflowRunState({
|
||||||
workflowRunId,
|
workflowRunId,
|
||||||
output: {
|
stepOutput,
|
||||||
stepsOutput: updatedStepsOutput,
|
|
||||||
},
|
|
||||||
context,
|
context,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -139,7 +109,6 @@ export class WorkflowExecutorWorkspaceService {
|
|||||||
currentStepIndex: currentStepIndex + 1,
|
currentStepIndex: currentStepIndex + 1,
|
||||||
steps,
|
steps,
|
||||||
context,
|
context,
|
||||||
workflowExecutorState: updatedWorkflowExecutorState,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,23 +121,17 @@ export class WorkflowExecutorWorkspaceService {
|
|||||||
currentStepIndex,
|
currentStepIndex,
|
||||||
steps,
|
steps,
|
||||||
context,
|
context,
|
||||||
workflowExecutorState: updatedWorkflowExecutorState,
|
|
||||||
attemptCount: attemptCount + 1,
|
attemptCount: attemptCount + 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.workflowRunWorkspaceService.saveWorkflowRunState({
|
await this.workflowRunWorkspaceService.saveWorkflowRunState({
|
||||||
workflowRunId,
|
workflowRunId,
|
||||||
output: {
|
stepOutput,
|
||||||
stepsOutput: updatedStepsOutput,
|
|
||||||
},
|
|
||||||
context,
|
context,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return actionOutput;
|
||||||
...updatedWorkflowExecutorState,
|
|
||||||
status: WorkflowRunStatus.FAILED,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendWorkflowNodeRunEvent() {
|
private sendWorkflowNodeRunEvent() {
|
||||||
|
|||||||
@ -67,20 +67,17 @@ export class RunWorkflowJob {
|
|||||||
|
|
||||||
await this.throttleExecution(workflowVersion.workflowId);
|
await this.throttleExecution(workflowVersion.workflowId);
|
||||||
|
|
||||||
const { status } = await this.workflowExecutorWorkspaceService.execute({
|
const { error } = await this.workflowExecutorWorkspaceService.execute({
|
||||||
workflowRunId,
|
workflowRunId,
|
||||||
currentStepIndex: 0,
|
currentStepIndex: 0,
|
||||||
steps: workflowVersion.steps ?? [],
|
steps: workflowVersion.steps,
|
||||||
context,
|
context,
|
||||||
workflowExecutorState: {
|
|
||||||
stepsOutput: {},
|
|
||||||
status: WorkflowRunStatus.RUNNING,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.workflowRunWorkspaceService.endWorkflowRun({
|
await this.workflowRunWorkspaceService.endWorkflowRun({
|
||||||
workflowRunId,
|
workflowRunId,
|
||||||
status,
|
status: error ? WorkflowRunStatus.FAILED : WorkflowRunStatus.COMPLETED,
|
||||||
|
error,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.workflowRunWorkspaceService.endWorkflowRun({
|
await this.workflowRunWorkspaceService.endWorkflowRun({
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { ActorMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
import { ActorMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||||
import {
|
import {
|
||||||
|
StepOutput,
|
||||||
WorkflowRunOutput,
|
WorkflowRunOutput,
|
||||||
WorkflowRunStatus,
|
WorkflowRunStatus,
|
||||||
WorkflowRunWorkspaceEntity,
|
WorkflowRunWorkspaceEntity,
|
||||||
@ -125,11 +126,11 @@ export class WorkflowRunWorkspaceService {
|
|||||||
|
|
||||||
async saveWorkflowRunState({
|
async saveWorkflowRunState({
|
||||||
workflowRunId,
|
workflowRunId,
|
||||||
output,
|
stepOutput,
|
||||||
context,
|
context,
|
||||||
}: {
|
}: {
|
||||||
workflowRunId: string;
|
workflowRunId: string;
|
||||||
output: Pick<WorkflowRunOutput, 'error' | 'stepsOutput'>;
|
stepOutput: StepOutput;
|
||||||
context: Record<string, any>;
|
context: Record<string, any>;
|
||||||
}) {
|
}) {
|
||||||
const workflowRunRepository =
|
const workflowRunRepository =
|
||||||
@ -154,7 +155,10 @@ export class WorkflowRunWorkspaceService {
|
|||||||
trigger: undefined,
|
trigger: undefined,
|
||||||
steps: [],
|
steps: [],
|
||||||
},
|
},
|
||||||
...output,
|
stepsOutput: {
|
||||||
|
...(workflowRunToUpdate.output?.stepsOutput ?? {}),
|
||||||
|
[stepOutput.id]: stepOutput.output,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
context,
|
context,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user