From b5535e4a6233d397060308ad1b8c254b28a2d57e Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 6 Jun 2022 09:17:35 +0200 Subject: [PATCH] feat(core): Run Error Workflow also on trigger activation error (#3470) * feat(core): Run Error Workflow also when workflow gets deactivated or could not be activated on startup because of error R# * :zap: Add missing file --- packages/cli/src/ActiveWorkflowRunner.ts | 32 ++++++++++ .../cli/src/WorkflowExecuteAdditionalData.ts | 7 +-- packages/core/src/ActiveWorkflows.ts | 61 ++++++++++++------- packages/workflow/src/Interfaces.ts | 7 ++- .../workflow/src/WorkflowActivationError.ts | 16 +++++ packages/workflow/src/index.ts | 1 + 6 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 packages/workflow/src/WorkflowActivationError.ts diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index 5f32136897..1c5947c3f5 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -13,6 +13,7 @@ import { ActiveWorkflows, NodeExecuteFunctions } from 'n8n-core'; import { + ExecutionError, IDeferredPromise, IExecuteData, IExecuteResponsePromiseData, @@ -22,11 +23,13 @@ import { INodeExecutionData, IRun, IRunExecutionData, + IWorkflowBase, IWorkflowExecuteAdditionalData as IWorkflowExecuteAdditionalDataWorkflow, NodeHelpers, WebhookHttpMethod, Workflow, WorkflowActivateMode, + WorkflowActivationError, WorkflowExecuteMode, LoggerProxy as Logger, } from 'n8n-workflow'; @@ -118,6 +121,7 @@ export class ActiveWorkflowRunner { workflowName: workflowData.name, workflowId: workflowData.id, }); + this.executeErrorWorkflow(error, workflowData, 'internal'); } } Logger.verbose('Finished initializing active workflows (startup)'); @@ -715,11 +719,39 @@ export class ActiveWorkflowRunner { message: error.message, }, }; + const activationError = new WorkflowActivationError( + 'There was a problem with the trigger, for that reason did the workflow had to be deactivated', + error, + node, + ); + + this.executeErrorWorkflow(activationError, workflowData, mode); }; return returnFunctions; }; } + executeErrorWorkflow( + error: ExecutionError, + workflowData: IWorkflowBase, + mode: WorkflowExecuteMode, + ): void { + const fullRunData: IRun = { + data: { + resultData: { + error, + runData: {}, + }, + }, + finished: false, + mode, + startedAt: new Date(), + stoppedAt: new Date(), + }; + + WorkflowExecuteAdditionalData.executeErrorWorkflow(workflowData, fullRunData, mode); + } + /** * Makes a workflow active * diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index d80994db28..92e4062769 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -27,7 +27,6 @@ import { IRun, IRunExecutionData, ITaskData, - IWorkflowCredentials, IWorkflowExecuteAdditionalData, IWorkflowExecuteHooks, IWorkflowHooksOptionalParameters, @@ -57,7 +56,6 @@ import { Push, ResponseHelper, WebhookHelpers, - WorkflowCredentials, WorkflowHelpers, } from '.'; import { @@ -66,7 +64,6 @@ import { getWorkflowOwner, } from './UserManagement/UserManagementHelper'; import { whereClause } from './WorkflowHelpers'; -import { RESPONSE_ERROR_MESSAGES } from './constants'; const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType'); @@ -79,7 +76,7 @@ const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType'); * @param {WorkflowExecuteMode} mode The mode in which the workflow got started in * @param {string} [executionId] The id the execution got saved as */ -function executeErrorWorkflow( +export function executeErrorWorkflow( workflowData: IWorkflowBase, fullRunData: IRun, mode: WorkflowExecuteMode, @@ -1028,7 +1025,7 @@ export function sendMessageToUI(source: string, messages: any[]) { * Returns the base additional data without webhooks * * @export - * @param {IWorkflowCredentials} credentials + * @param {userId} string * @param {INodeParameters} currentNodeParameters * @returns {Promise} */ diff --git a/packages/core/src/ActiveWorkflows.ts b/packages/core/src/ActiveWorkflows.ts index c92ca1b511..fbfb9ca916 100644 --- a/packages/core/src/ActiveWorkflows.ts +++ b/packages/core/src/ActiveWorkflows.ts @@ -13,6 +13,7 @@ import { LoggerProxy as Logger, Workflow, WorkflowActivateMode, + WorkflowActivationError, WorkflowExecuteMode, } from 'n8n-workflow'; @@ -82,17 +83,26 @@ export class ActiveWorkflows { let triggerResponse: ITriggerResponse | undefined; this.workflowData[id].triggerResponses = []; for (const triggerNode of triggerNodes) { - triggerResponse = await workflow.runTrigger( - triggerNode, - getTriggerFunctions, - additionalData, - mode, - activation, - ); - if (triggerResponse !== undefined) { - // If a response was given save it - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.workflowData[id].triggerResponses!.push(triggerResponse); + try { + triggerResponse = await workflow.runTrigger( + triggerNode, + getTriggerFunctions, + additionalData, + mode, + activation, + ); + if (triggerResponse !== undefined) { + // If a response was given save it + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.workflowData[id].triggerResponses!.push(triggerResponse); + } + } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + throw new WorkflowActivationError( + 'There was a problem activating the workflow', + error, + triggerNode, + ); } } @@ -100,17 +110,26 @@ export class ActiveWorkflows { if (pollNodes.length) { this.workflowData[id].pollResponses = []; for (const pollNode of pollNodes) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.workflowData[id].pollResponses!.push( - await this.activatePolling( + try { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.workflowData[id].pollResponses!.push( + await this.activatePolling( + pollNode, + workflow, + additionalData, + getPollFunctions, + mode, + activation, + ), + ); + } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + throw new WorkflowActivationError( + 'There was a problem activating the workflow', + error, pollNode, - workflow, - additionalData, - getPollFunctions, - mode, - activation, - ), - ); + ); + } } } } diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index be2d58fa1f..0e9d8888ea 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -9,6 +9,7 @@ import { URLSearchParams } from 'url'; import { IDeferredPromise } from './DeferredPromise'; import { Workflow } from './Workflow'; import { WorkflowHooks } from './WorkflowHooks'; +import { WorkflowActivationError } from './WorkflowActivationError'; import { WorkflowOperationError } from './WorkflowErrors'; import { NodeApiError, NodeOperationError } from './NodeErrors'; @@ -56,7 +57,11 @@ export interface IConnection { index: number; } -export type ExecutionError = WorkflowOperationError | NodeOperationError | NodeApiError; +export type ExecutionError = + | WorkflowActivationError + | WorkflowOperationError + | NodeOperationError + | NodeApiError; // Get used to gives nodes access to credentials export interface IGetCredentials { diff --git a/packages/workflow/src/WorkflowActivationError.ts b/packages/workflow/src/WorkflowActivationError.ts new file mode 100644 index 0000000000..04b02fc7f2 --- /dev/null +++ b/packages/workflow/src/WorkflowActivationError.ts @@ -0,0 +1,16 @@ +// eslint-disable-next-line import/no-cycle +import { ExecutionBaseError, INode } from '.'; + +/** + * Class for instantiating an workflow activation error + */ +export class WorkflowActivationError extends ExecutionBaseError { + node: INode | undefined; + + constructor(message: string, error: Error, node?: INode) { + super(error); + this.node = node; + this.cause = error; + this.message = message; + } +} diff --git a/packages/workflow/src/index.ts b/packages/workflow/src/index.ts index 6f24894277..5ae90bce09 100644 --- a/packages/workflow/src/index.ts +++ b/packages/workflow/src/index.ts @@ -11,6 +11,7 @@ export * from './NodeErrors'; export * as TelemetryHelpers from './TelemetryHelpers'; export * from './RoutingNode'; export * from './Workflow'; +export * from './WorkflowActivationError'; export * from './WorkflowDataProxy'; export * from './WorkflowErrors'; export * from './WorkflowHooks';