From ae423f5e7509d04353c1be11034ed72584dc6ada Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 1 Aug 2024 11:38:31 +0200 Subject: [PATCH] Improve typing definition (#6481) - added typing for workflow-runner results - fix workflow typing - add a `workflow-action-runner` submodule that contains factories for action runners - added code-action-runner - simplified code --- .../common/types/workflow-action.type.ts | 16 +++- .../common/types/workflow-result.type.ts | 10 ++ .../common/types/workflow-settings.type.ts | 12 +-- .../workflow-action-runner.factory.ts | 23 +++++ .../workflow-action-runner.interface.ts | 14 +++ .../workflow-action-runner.module.ts | 12 +++ .../code-workflow-action-runner.ts | 31 ++++++ .../workflow-runner/workflow-runner.module.ts | 4 +- .../workflow-runner.service.ts | 94 +++++++++---------- 9 files changed, 151 insertions(+), 65 deletions(-) create mode 100644 packages/twenty-server/src/modules/workflow/common/types/workflow-result.type.ts create mode 100644 packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.factory.ts create mode 100644 packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.interface.ts create mode 100644 packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.module.ts create mode 100644 packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runners/code-workflow-action-runner.ts diff --git a/packages/twenty-server/src/modules/workflow/common/types/workflow-action.type.ts b/packages/twenty-server/src/modules/workflow/common/types/workflow-action.type.ts index c630ab0a2..3c380a490 100644 --- a/packages/twenty-server/src/modules/workflow/common/types/workflow-action.type.ts +++ b/packages/twenty-server/src/modules/workflow/common/types/workflow-action.type.ts @@ -1,14 +1,20 @@ -import { WorkflowSettingsType } from 'src/modules/workflow/common/types/workflow-settings.type'; +import { WorkflowCodeSettingsType } from 'src/modules/workflow/common/types/workflow-settings.type'; export enum WorkflowActionType { CODE = 'CODE', } -export type WorkflowAction = { +type CommonWorkflowAction = { name: string; displayName: string; - type: WorkflowActionType; valid: boolean; - settings: WorkflowSettingsType; - nextAction?: WorkflowAction; +}; + +type WorkflowCodeAction = CommonWorkflowAction & { + type: WorkflowActionType.CODE; + settings: WorkflowCodeSettingsType; +}; + +export type WorkflowAction = WorkflowCodeAction & { + nextAction: WorkflowAction; }; diff --git a/packages/twenty-server/src/modules/workflow/common/types/workflow-result.type.ts b/packages/twenty-server/src/modules/workflow/common/types/workflow-result.type.ts new file mode 100644 index 000000000..613ad5a99 --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/common/types/workflow-result.type.ts @@ -0,0 +1,10 @@ +type WorkflowError = { + errorType: string; + errorMessage: string; + stackTrace: string; +}; + +export type WorkflowResult = { + data: object | null; + error?: WorkflowError; +}; diff --git a/packages/twenty-server/src/modules/workflow/common/types/workflow-settings.type.ts b/packages/twenty-server/src/modules/workflow/common/types/workflow-settings.type.ts index 344f4b01a..55c4d5957 100644 --- a/packages/twenty-server/src/modules/workflow/common/types/workflow-settings.type.ts +++ b/packages/twenty-server/src/modules/workflow/common/types/workflow-settings.type.ts @@ -1,8 +1,4 @@ -export type WorkflowCodeSettingsType = { - serverlessFunctionId: string; -}; - -export type WorkflowSettingsType = { +type WorkflowBaseSettingsType = { errorHandlingOptions: { retryOnFailure: { value: boolean; @@ -11,4 +7,8 @@ export type WorkflowSettingsType = { value: boolean; }; }; -} & WorkflowCodeSettingsType; +}; + +export type WorkflowCodeSettingsType = WorkflowBaseSettingsType & { + serverlessFunctionId: string; +}; diff --git a/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.factory.ts b/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.factory.ts new file mode 100644 index 000000000..5ab4d2a42 --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.factory.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common'; + +import { CodeWorkflowActionRunner } from 'src/modules/workflow/workflow-action-runner/workflow-action-runners/code-workflow-action-runner'; +import { WorkflowActionType } from 'src/modules/workflow/common/types/workflow-action.type'; +import { WorkflowActionRunner } from 'src/modules/workflow/workflow-action-runner/workflow-action-runner.interface'; + +@Injectable() +export class WorkflowActionRunnerFactory { + constructor( + private readonly codeWorkflowActionRunner: CodeWorkflowActionRunner, + ) {} + + get(actionType: WorkflowActionType): WorkflowActionRunner { + switch (actionType) { + case WorkflowActionType.CODE: + return this.codeWorkflowActionRunner; + default: + throw new Error( + `Workflow action executor not found for action type '${actionType}'`, + ); + } + } +} diff --git a/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.interface.ts b/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.interface.ts new file mode 100644 index 000000000..b443e7c9c --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.interface.ts @@ -0,0 +1,14 @@ +import { WorkflowResult } from 'src/modules/workflow/common/types/workflow-result.type'; +import { WorkflowAction } from 'src/modules/workflow/common/types/workflow-action.type'; + +export interface WorkflowActionRunner { + execute({ + action, + workspaceId, + payload, + }: { + action: WorkflowAction; + workspaceId: string; + payload?: object; + }): Promise; +} diff --git a/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.module.ts b/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.module.ts new file mode 100644 index 000000000..890b68850 --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runner.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; + +import { WorkflowActionRunnerFactory } from 'src/modules/workflow/workflow-action-runner/workflow-action-runner.factory'; +import { CodeWorkflowActionRunner } from 'src/modules/workflow/workflow-action-runner/workflow-action-runners/code-workflow-action-runner'; +import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module'; + +@Module({ + imports: [ServerlessFunctionModule], + providers: [WorkflowActionRunnerFactory, CodeWorkflowActionRunner], + exports: [WorkflowActionRunnerFactory], +}) +export class WorkflowActionRunnerModule {} diff --git a/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runners/code-workflow-action-runner.ts b/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runners/code-workflow-action-runner.ts new file mode 100644 index 000000000..3be348283 --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-action-runner/workflow-action-runners/code-workflow-action-runner.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@nestjs/common'; + +import { WorkflowActionRunner } from 'src/modules/workflow/workflow-action-runner/workflow-action-runner.interface'; +import { WorkflowAction } from 'src/modules/workflow/common/types/workflow-action.type'; +import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; +import { WorkflowResult } from 'src/modules/workflow/common/types/workflow-result.type'; + +@Injectable() +export class CodeWorkflowActionRunner implements WorkflowActionRunner { + constructor( + private readonly serverlessFunctionService: ServerlessFunctionService, + ) {} + + async execute({ + action, + workspaceId, + payload, + }: { + action: WorkflowAction; + workspaceId: string; + payload?: object; + }): Promise { + const result = await this.serverlessFunctionService.executeOne( + action.settings.serverlessFunctionId, + workspaceId, + payload, + ); + + return { data: result.data, ...(result.error && { error: result.error }) }; + } +} diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-runner.module.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-runner.module.ts index 1ba558f73..a56984250 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-runner.module.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-runner.module.ts @@ -3,10 +3,10 @@ import { Module } from '@nestjs/common'; import { WorkflowRunnerService } from 'src/modules/workflow/workflow-runner/workflow-runner.service'; import { WorkflowRunnerJob } from 'src/modules/workflow/workflow-runner/workflow-runner.job'; import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module'; -import { ServerlessFunctionModule } from 'src/engine/metadata-modules/serverless-function/serverless-function.module'; +import { WorkflowActionRunnerModule } from 'src/modules/workflow/workflow-action-runner/workflow-action-runner.module'; @Module({ - imports: [WorkflowCommonModule, ServerlessFunctionModule], + imports: [WorkflowCommonModule, WorkflowActionRunnerModule], providers: [WorkflowRunnerService, WorkflowRunnerJob], exports: [WorkflowRunnerService], }) diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-runner.service.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-runner.service.ts index af2f9027c..9b79d2053 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-runner.service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-runner.service.ts @@ -1,21 +1,14 @@ import { Injectable } from '@nestjs/common'; -import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; -import { - WorkflowTriggerException, - WorkflowTriggerExceptionCode, -} from 'src/modules/workflow/workflow-trigger/workflow-trigger.exception'; -import { - WorkflowAction, - WorkflowActionType, -} from 'src/modules/workflow/common/types/workflow-action.type'; +import { WorkflowAction } from 'src/modules/workflow/common/types/workflow-action.type'; +import { WorkflowActionRunnerFactory } from 'src/modules/workflow/workflow-action-runner/workflow-action-runner.factory'; const MAX_RETRIES_ON_FAILURE = 3; @Injectable() export class WorkflowRunnerService { constructor( - private readonly serverlessFunctionService: ServerlessFunctionService, + private readonly workflowActionRunnerFactory: WorkflowActionRunnerFactory, ) {} async run({ @@ -33,51 +26,48 @@ export class WorkflowRunnerService { return payload; } - let result: object | undefined = undefined; + const workflowActionRunner = this.workflowActionRunnerFactory.get( + action.type, + ); - switch (action.type) { - case WorkflowActionType.CODE: { - const executionResult = await this.serverlessFunctionService.executeOne( - action.settings.serverlessFunctionId, - workspaceId, - payload, - ); + const result = await workflowActionRunner.execute({ + action, + workspaceId, + payload, + }); - if (executionResult.data) { - result = executionResult.data; - break; - } - if (!executionResult.error) { - throw new Error('Execution result error, no data or error'); - } - if (action.settings.errorHandlingOptions.continueOnFailure.value) { - result = payload; - break; - } else if ( - action.settings.errorHandlingOptions.retryOnFailure.value && - attemptCount < MAX_RETRIES_ON_FAILURE - ) { - return await this.run({ - action, - workspaceId, - payload, - attemptCount: attemptCount + 1, - }); - } else { - return executionResult.error; - } - } - default: - throw new WorkflowTriggerException( - `Unknown action type '${action.type}'`, - WorkflowTriggerExceptionCode.INVALID_ACTION_TYPE, - ); + if (result.data) { + return await this.run({ + action: action.nextAction, + workspaceId, + payload: result.data, + }); } - return await this.run({ - action: action.nextAction, - workspaceId, - payload: result, - }); + if (!result.error) { + throw new Error('Execution result error, no data or error'); + } + + if (action.settings.errorHandlingOptions.continueOnFailure.value) { + return await this.run({ + action: action.nextAction, + workspaceId, + payload, + }); + } + + if ( + action.settings.errorHandlingOptions.retryOnFailure.value && + attemptCount < MAX_RETRIES_ON_FAILURE + ) { + return await this.run({ + action, + workspaceId, + payload, + attemptCount: attemptCount + 1, + }); + } + + return result.error; } }