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#

*  Add missing file
This commit is contained in:
Jan Oberhauser 2022-06-06 09:17:35 +02:00 committed by GitHub
parent ff95de0bdd
commit b5535e4a62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 97 additions and 27 deletions

View file

@ -13,6 +13,7 @@
import { ActiveWorkflows, NodeExecuteFunctions } from 'n8n-core'; import { ActiveWorkflows, NodeExecuteFunctions } from 'n8n-core';
import { import {
ExecutionError,
IDeferredPromise, IDeferredPromise,
IExecuteData, IExecuteData,
IExecuteResponsePromiseData, IExecuteResponsePromiseData,
@ -22,11 +23,13 @@ import {
INodeExecutionData, INodeExecutionData,
IRun, IRun,
IRunExecutionData, IRunExecutionData,
IWorkflowBase,
IWorkflowExecuteAdditionalData as IWorkflowExecuteAdditionalDataWorkflow, IWorkflowExecuteAdditionalData as IWorkflowExecuteAdditionalDataWorkflow,
NodeHelpers, NodeHelpers,
WebhookHttpMethod, WebhookHttpMethod,
Workflow, Workflow,
WorkflowActivateMode, WorkflowActivateMode,
WorkflowActivationError,
WorkflowExecuteMode, WorkflowExecuteMode,
LoggerProxy as Logger, LoggerProxy as Logger,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -118,6 +121,7 @@ export class ActiveWorkflowRunner {
workflowName: workflowData.name, workflowName: workflowData.name,
workflowId: workflowData.id, workflowId: workflowData.id,
}); });
this.executeErrorWorkflow(error, workflowData, 'internal');
} }
} }
Logger.verbose('Finished initializing active workflows (startup)'); Logger.verbose('Finished initializing active workflows (startup)');
@ -715,11 +719,39 @@ export class ActiveWorkflowRunner {
message: error.message, 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; 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 * Makes a workflow active
* *

View file

@ -27,7 +27,6 @@ import {
IRun, IRun,
IRunExecutionData, IRunExecutionData,
ITaskData, ITaskData,
IWorkflowCredentials,
IWorkflowExecuteAdditionalData, IWorkflowExecuteAdditionalData,
IWorkflowExecuteHooks, IWorkflowExecuteHooks,
IWorkflowHooksOptionalParameters, IWorkflowHooksOptionalParameters,
@ -57,7 +56,6 @@ import {
Push, Push,
ResponseHelper, ResponseHelper,
WebhookHelpers, WebhookHelpers,
WorkflowCredentials,
WorkflowHelpers, WorkflowHelpers,
} from '.'; } from '.';
import { import {
@ -66,7 +64,6 @@ import {
getWorkflowOwner, getWorkflowOwner,
} from './UserManagement/UserManagementHelper'; } from './UserManagement/UserManagementHelper';
import { whereClause } from './WorkflowHelpers'; import { whereClause } from './WorkflowHelpers';
import { RESPONSE_ERROR_MESSAGES } from './constants';
const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType'); 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 {WorkflowExecuteMode} mode The mode in which the workflow got started in
* @param {string} [executionId] The id the execution got saved as * @param {string} [executionId] The id the execution got saved as
*/ */
function executeErrorWorkflow( export function executeErrorWorkflow(
workflowData: IWorkflowBase, workflowData: IWorkflowBase,
fullRunData: IRun, fullRunData: IRun,
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
@ -1028,7 +1025,7 @@ export function sendMessageToUI(source: string, messages: any[]) {
* Returns the base additional data without webhooks * Returns the base additional data without webhooks
* *
* @export * @export
* @param {IWorkflowCredentials} credentials * @param {userId} string
* @param {INodeParameters} currentNodeParameters * @param {INodeParameters} currentNodeParameters
* @returns {Promise<IWorkflowExecuteAdditionalData>} * @returns {Promise<IWorkflowExecuteAdditionalData>}
*/ */

View file

@ -13,6 +13,7 @@ import {
LoggerProxy as Logger, LoggerProxy as Logger,
Workflow, Workflow,
WorkflowActivateMode, WorkflowActivateMode,
WorkflowActivationError,
WorkflowExecuteMode, WorkflowExecuteMode,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -82,17 +83,26 @@ export class ActiveWorkflows {
let triggerResponse: ITriggerResponse | undefined; let triggerResponse: ITriggerResponse | undefined;
this.workflowData[id].triggerResponses = []; this.workflowData[id].triggerResponses = [];
for (const triggerNode of triggerNodes) { for (const triggerNode of triggerNodes) {
triggerResponse = await workflow.runTrigger( try {
triggerNode, triggerResponse = await workflow.runTrigger(
getTriggerFunctions, triggerNode,
additionalData, getTriggerFunctions,
mode, additionalData,
activation, mode,
); activation,
if (triggerResponse !== undefined) { );
// If a response was given save it if (triggerResponse !== undefined) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // If a response was given save it
this.workflowData[id].triggerResponses!.push(triggerResponse); // 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) { if (pollNodes.length) {
this.workflowData[id].pollResponses = []; this.workflowData[id].pollResponses = [];
for (const pollNode of pollNodes) { for (const pollNode of pollNodes) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion try {
this.workflowData[id].pollResponses!.push( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await this.activatePolling( 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, pollNode,
workflow, );
additionalData, }
getPollFunctions,
mode,
activation,
),
);
} }
} }
} }

View file

@ -9,6 +9,7 @@ import { URLSearchParams } from 'url';
import { IDeferredPromise } from './DeferredPromise'; import { IDeferredPromise } from './DeferredPromise';
import { Workflow } from './Workflow'; import { Workflow } from './Workflow';
import { WorkflowHooks } from './WorkflowHooks'; import { WorkflowHooks } from './WorkflowHooks';
import { WorkflowActivationError } from './WorkflowActivationError';
import { WorkflowOperationError } from './WorkflowErrors'; import { WorkflowOperationError } from './WorkflowErrors';
import { NodeApiError, NodeOperationError } from './NodeErrors'; import { NodeApiError, NodeOperationError } from './NodeErrors';
@ -56,7 +57,11 @@ export interface IConnection {
index: number; index: number;
} }
export type ExecutionError = WorkflowOperationError | NodeOperationError | NodeApiError; export type ExecutionError =
| WorkflowActivationError
| WorkflowOperationError
| NodeOperationError
| NodeApiError;
// Get used to gives nodes access to credentials // Get used to gives nodes access to credentials
export interface IGetCredentials { export interface IGetCredentials {

View file

@ -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;
}
}

View file

@ -11,6 +11,7 @@ export * from './NodeErrors';
export * as TelemetryHelpers from './TelemetryHelpers'; export * as TelemetryHelpers from './TelemetryHelpers';
export * from './RoutingNode'; export * from './RoutingNode';
export * from './Workflow'; export * from './Workflow';
export * from './WorkflowActivationError';
export * from './WorkflowDataProxy'; export * from './WorkflowDataProxy';
export * from './WorkflowErrors'; export * from './WorkflowErrors';
export * from './WorkflowHooks'; export * from './WorkflowHooks';