From 726a99bf698a61d23d93d1636c4bca85d0dda789 Mon Sep 17 00:00:00 2001 From: Omar Ajoue Date: Tue, 23 Mar 2021 19:08:47 +0100 Subject: [PATCH] :sparkles: Add Activation Trigger (#1570) * :sparkles: n8n start trigger node * first declaration of WorkflowActivationMode * implement first WorkflowActivationMode: 'init', 'create', 'update', 'activate' * fix Server missing id * add activation infos to triggers * remove WorkflowActivationMode from webhook execution function * add some missing activation and add manual activation * clean up and fix some code * fix UnhandledPromiseRejectionWarning: Error: Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function! * fix spaces * use a better name for the node * fix ident in package.json * Contributions to lublak's PR #1287 * Fixed linting issues and change the way parameters are displayed * :zap: Fix name and minor improvements Co-authored-by: lublak Co-authored-by: lublak <44057030+lublak@users.noreply.github.com> Co-authored-by: Jan Oberhauser --- packages/cli/src/ActiveWorkflowRunner.ts | 29 ++++---- packages/cli/src/Server.ts | 5 +- packages/cli/src/TestWebhooks.ts | 5 +- packages/core/src/ActiveWebhooks.ts | 9 ++- packages/core/src/ActiveWorkflows.ts | 14 ++-- packages/core/src/NodeExecuteFunctions.ts | 16 +++- .../nodes/ActivationTrigger.node.ts | 73 +++++++++++++++++++ packages/nodes-base/package.json | 1 + packages/workflow/src/Interfaces.ts | 10 ++- packages/workflow/src/Workflow.ts | 13 ++-- 10 files changed, 134 insertions(+), 41 deletions(-) create mode 100644 packages/nodes-base/nodes/ActivationTrigger.node.ts diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index 3b5ce249ef..3ab8c932ed 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -31,6 +31,7 @@ import { NodeHelpers, WebhookHttpMethod, Workflow, + WorkflowActivateMode, WorkflowExecuteMode, } from 'n8n-workflow'; @@ -66,7 +67,7 @@ export class ActiveWorkflowRunner { for (const workflowData of workflowsData) { console.log(` - ${workflowData.name}`); try { - await this.add(workflowData.id.toString(), workflowData); + await this.add(workflowData.id.toString(), 'init', workflowData); console.log(` => Started`); } catch (error) { console.log(` => ERROR: Workflow could not be activated:`); @@ -273,7 +274,7 @@ export class ActiveWorkflowRunner { * @returns {Promise} * @memberof ActiveWorkflowRunner */ - async addWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflowExecuteAdditionalDataWorkflow, mode: WorkflowExecuteMode): Promise { + async addWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflowExecuteAdditionalDataWorkflow, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): Promise { const webhooks = WebhookHelpers.getWorkflowWebhooks(workflow, additionalData); let path = '' as string | undefined; @@ -319,10 +320,10 @@ export class ActiveWorkflowRunner { await Db.collections.Webhook?.insert(webhook); - const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, false); + const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, activation, false); if (webhookExists !== true) { // If webhook does not exist yet create it - await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, false); + await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, activation, false); } } catch (error) { @@ -378,7 +379,7 @@ export class ActiveWorkflowRunner { const webhooks = WebhookHelpers.getWorkflowWebhooks(workflow, additionalData); for (const webhookData of webhooks) { - await workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, false); + await workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, 'update', false); } await WorkflowHelpers.saveStaticData(workflow); @@ -446,9 +447,9 @@ export class ActiveWorkflowRunner { * @returns {IGetExecutePollFunctions} * @memberof ActiveWorkflowRunner */ - getExecutePollFunctions(workflowData: IWorkflowDb, additionalData: IWorkflowExecuteAdditionalDataWorkflow, mode: WorkflowExecuteMode): IGetExecutePollFunctions { + getExecutePollFunctions(workflowData: IWorkflowDb, additionalData: IWorkflowExecuteAdditionalDataWorkflow, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): IGetExecutePollFunctions { return ((workflow: Workflow, node: INode) => { - const returnFunctions = NodeExecuteFunctions.getExecutePollFunctions(workflow, node, additionalData, mode); + const returnFunctions = NodeExecuteFunctions.getExecutePollFunctions(workflow, node, additionalData, mode, activation); returnFunctions.__emit = (data: INodeExecutionData[][]): void => { this.runWorkflow(workflowData, node, data, additionalData, mode); }; @@ -467,9 +468,9 @@ export class ActiveWorkflowRunner { * @returns {IGetExecuteTriggerFunctions} * @memberof ActiveWorkflowRunner */ - getExecuteTriggerFunctions(workflowData: IWorkflowDb, additionalData: IWorkflowExecuteAdditionalDataWorkflow, mode: WorkflowExecuteMode): IGetExecuteTriggerFunctions{ + getExecuteTriggerFunctions(workflowData: IWorkflowDb, additionalData: IWorkflowExecuteAdditionalDataWorkflow, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): IGetExecuteTriggerFunctions{ return ((workflow: Workflow, node: INode) => { - const returnFunctions = NodeExecuteFunctions.getExecuteTriggerFunctions(workflow, node, additionalData, mode); + const returnFunctions = NodeExecuteFunctions.getExecuteTriggerFunctions(workflow, node, additionalData, mode, activation); returnFunctions.emit = (data: INodeExecutionData[][]): void => { WorkflowHelpers.saveStaticData(workflow); this.runWorkflow(workflowData, node, data, additionalData, mode).catch((err) => console.error(err)); @@ -486,7 +487,7 @@ export class ActiveWorkflowRunner { * @returns {Promise} * @memberof ActiveWorkflowRunner */ - async add(workflowId: string, workflowData?: IWorkflowDb): Promise { + async add(workflowId: string, activation: WorkflowActivateMode, workflowData?: IWorkflowDb): Promise { if (this.activeWorkflows === null) { throw new Error(`The "activeWorkflows" instance did not get initialized yet.`); } @@ -511,15 +512,15 @@ export class ActiveWorkflowRunner { const mode = 'trigger'; const credentials = await WorkflowCredentials(workflowData.nodes); const additionalData = await WorkflowExecuteAdditionalData.getBase(credentials); - const getTriggerFunctions = this.getExecuteTriggerFunctions(workflowData, additionalData, mode); - const getPollFunctions = this.getExecutePollFunctions(workflowData, additionalData, mode); + const getTriggerFunctions = this.getExecuteTriggerFunctions(workflowData, additionalData, mode, activation); + const getPollFunctions = this.getExecutePollFunctions(workflowData, additionalData, mode, activation); // Add the workflows which have webhooks defined - await this.addWorkflowWebhooks(workflowInstance, additionalData, mode); + await this.addWorkflowWebhooks(workflowInstance, additionalData, mode, activation); if (workflowInstance.getTriggerNodes().length !== 0 || workflowInstance.getPollNodes().length !== 0) { - await this.activeWorkflows.add(workflowId, workflowInstance, additionalData, getTriggerFunctions, getPollFunctions); + await this.activeWorkflows.add(workflowId, workflowInstance, additionalData, mode, activation, getTriggerFunctions, getPollFunctions); } if (this.activationErrors[workflowId] !== undefined) { diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 187fccb340..2e68824f62 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -624,7 +624,7 @@ class App { try { await this.externalHooks.run('workflow.activate', [responseData]); - await this.activeWorkflowRunner.add(id); + await this.activeWorkflowRunner.add(id, isActive ? 'update' : 'activate'); } catch (error) { // If workflow could not be activated set it again to inactive newWorkflowData.active = false; @@ -670,6 +670,7 @@ class App { const startNodes: string[] | undefined = req.body.startNodes; const destinationNode: string | undefined = req.body.destinationNode; const executionMode = 'manual'; + const activationMode = 'manual'; const sessionId = GenericHelpers.getSessionId(req); @@ -679,7 +680,7 @@ class App { const additionalData = await WorkflowExecuteAdditionalData.getBase(credentials); const nodeTypes = NodeTypes(); const workflowInstance = new Workflow({ id: workflowData.id, name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: false, nodeTypes, staticData: undefined, settings: workflowData.settings }); - const needsWebhook = await this.testWebhooks.needsWebhookData(workflowData, workflowInstance, additionalData, executionMode, sessionId, destinationNode); + const needsWebhook = await this.testWebhooks.needsWebhookData(workflowData, workflowInstance, additionalData, executionMode, activationMode, sessionId, destinationNode); if (needsWebhook === true) { return { waitingForWebhook: true, diff --git a/packages/cli/src/TestWebhooks.ts b/packages/cli/src/TestWebhooks.ts index e5caef9d93..b9dcf09fcb 100644 --- a/packages/cli/src/TestWebhooks.ts +++ b/packages/cli/src/TestWebhooks.ts @@ -17,6 +17,7 @@ import { IWorkflowExecuteAdditionalData, WebhookHttpMethod, Workflow, + WorkflowActivateMode, WorkflowExecuteMode, } from 'n8n-workflow'; @@ -161,7 +162,7 @@ export class TestWebhooks { * @returns {(Promise)} * @memberof TestWebhooks */ - async needsWebhookData(workflowData: IWorkflowDb, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, sessionId?: string, destinationNode?: string): Promise { + async needsWebhookData(workflowData: IWorkflowDb, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode, sessionId?: string, destinationNode?: string): Promise { const webhooks = WebhookHelpers.getWorkflowWebhooks(workflow, additionalData, destinationNode); if (webhooks.length === 0) { @@ -193,7 +194,7 @@ export class TestWebhooks { }; try { - await this.activeWebhooks!.add(workflow, webhookData, mode); + await this.activeWebhooks!.add(workflow, webhookData, mode, activation); } catch (error) { activatedKey.forEach(deleteKey => delete this.testWebhookData[deleteKey] ); await this.activeWebhooks!.removeWorkflow(workflow); diff --git a/packages/core/src/ActiveWebhooks.ts b/packages/core/src/ActiveWebhooks.ts index 86a1c0f690..95b7f9dc3c 100644 --- a/packages/core/src/ActiveWebhooks.ts +++ b/packages/core/src/ActiveWebhooks.ts @@ -2,6 +2,7 @@ import { IWebhookData, WebhookHttpMethod, Workflow, + WorkflowActivateMode, WorkflowExecuteMode, } from 'n8n-workflow'; @@ -30,7 +31,7 @@ export class ActiveWebhooks { * @returns {Promise} * @memberof ActiveWebhooks */ - async add(workflow: Workflow, webhookData: IWebhookData, mode: WorkflowExecuteMode): Promise { + async add(workflow: Workflow, webhookData: IWebhookData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): Promise { if (workflow.id === undefined) { throw new Error('Webhooks can only be added for saved workflows as an id is needed!'); } @@ -57,10 +58,10 @@ export class ActiveWebhooks { this.webhookUrls[webhookKey].push(webhookData); try { - const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); + const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, activation, this.testWebhooks); if (webhookExists !== true) { // If webhook does not exist yet create it - await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); + await workflow.runWebhookMethod('create', webhookData, NodeExecuteFunctions, mode, activation, this.testWebhooks); } } catch (error) { @@ -183,7 +184,7 @@ export class ActiveWebhooks { // Go through all the registered webhooks of the workflow and remove them for (const webhookData of webhooks) { - await workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, this.testWebhooks); + await workflow.runWebhookMethod('delete', webhookData, NodeExecuteFunctions, mode, 'update', this.testWebhooks); delete this.webhookUrls[this.getWebhookKey(webhookData.httpMethod, webhookData.path, webhookData.webhookId)]; } diff --git a/packages/core/src/ActiveWorkflows.ts b/packages/core/src/ActiveWorkflows.ts index 86a88c617b..320e9fa73f 100644 --- a/packages/core/src/ActiveWorkflows.ts +++ b/packages/core/src/ActiveWorkflows.ts @@ -8,6 +8,8 @@ import { ITriggerResponse, IWorkflowExecuteAdditionalData, Workflow, + WorkflowActivateMode, + WorkflowExecuteMode, } from 'n8n-workflow'; import { @@ -66,14 +68,14 @@ export class ActiveWorkflows { * @returns {Promise} * @memberof ActiveWorkflows */ - async add(id: string, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, getTriggerFunctions: IGetExecuteTriggerFunctions, getPollFunctions: IGetExecutePollFunctions): Promise { + async add(id: string, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode, getTriggerFunctions: IGetExecuteTriggerFunctions, getPollFunctions: IGetExecutePollFunctions): Promise { this.workflowData[id] = {}; const triggerNodes = workflow.getTriggerNodes(); let triggerResponse: ITriggerResponse | undefined; this.workflowData[id].triggerResponses = []; for (const triggerNode of triggerNodes) { - triggerResponse = await workflow.runTrigger(triggerNode, getTriggerFunctions, additionalData, 'trigger'); + triggerResponse = await workflow.runTrigger(triggerNode, getTriggerFunctions, additionalData, mode, activation); if (triggerResponse !== undefined) { // If a response was given save it this.workflowData[id].triggerResponses!.push(triggerResponse); @@ -84,7 +86,7 @@ export class ActiveWorkflows { if (pollNodes.length) { this.workflowData[id].pollResponses = []; for (const pollNode of pollNodes) { - this.workflowData[id].pollResponses!.push(await this.activatePolling(pollNode, workflow, additionalData, getPollFunctions)); + this.workflowData[id].pollResponses!.push(await this.activatePolling(pollNode, workflow, additionalData, getPollFunctions, mode, activation)); } } } @@ -100,10 +102,8 @@ export class ActiveWorkflows { * @returns {Promise} * @memberof ActiveWorkflows */ - async activatePolling(node: INode, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, getPollFunctions: IGetExecutePollFunctions): Promise { - const mode = 'trigger'; - - const pollFunctions = getPollFunctions(workflow, node, additionalData, mode); + async activatePolling(node: INode, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, getPollFunctions: IGetExecutePollFunctions, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): Promise { + const pollFunctions = getPollFunctions(workflow, node, additionalData, mode, activation); const pollTimes = pollFunctions.getNodeParameter('pollTimes') as unknown as { item: ITriggerTime[]; diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index b65b102495..2646fbb7b7 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -34,6 +34,7 @@ import { NodeHelpers, NodeParameterValue, Workflow, + WorkflowActivateMode, WorkflowDataProxy, WorkflowExecuteMode, } from 'n8n-workflow'; @@ -535,7 +536,7 @@ export function getWorkflowMetadata(workflow: Workflow): IWorkflowMetadata { * @returns {ITriggerFunctions} */ // TODO: Check if I can get rid of: additionalData, and so then maybe also at ActiveWorkflowRunner.add -export function getExecutePollFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): IPollFunctions { +export function getExecutePollFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): IPollFunctions { return ((workflow: Workflow, node: INode) => { return { __emit: (data: INodeExecutionData[][]): void => { @@ -547,6 +548,9 @@ export function getExecutePollFunctions(workflow: Workflow, node: INode, additio getMode: (): WorkflowExecuteMode => { return mode; }, + getActivationMode: (): WorkflowActivateMode => { + return activation; + }, getNode: () => { return getNode(node); }, @@ -598,7 +602,7 @@ export function getExecutePollFunctions(workflow: Workflow, node: INode, additio * @returns {ITriggerFunctions} */ // TODO: Check if I can get rid of: additionalData, and so then maybe also at ActiveWorkflowRunner.add -export function getExecuteTriggerFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): ITriggerFunctions { +export function getExecuteTriggerFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): ITriggerFunctions { return ((workflow: Workflow, node: INode) => { return { emit: (data: INodeExecutionData[][]): void => { @@ -613,6 +617,9 @@ export function getExecuteTriggerFunctions(workflow: Workflow, node: INode, addi getMode: (): WorkflowExecuteMode => { return mode; }, + getActivationMode: (): WorkflowActivateMode => { + return activation; + }, getNodeParameter: (parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any const runExecutionData: IRunExecutionData | null = null; const itemIndex = 0; @@ -910,7 +917,7 @@ export function getLoadOptionsFunctions(workflow: Workflow, node: INode, additio * @param {WorkflowExecuteMode} mode * @returns {IHookFunctions} */ -export function getExecuteHookFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, isTest?: boolean, webhookData?: IWebhookData): IHookFunctions { +export function getExecuteHookFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode, isTest?: boolean, webhookData?: IWebhookData): IHookFunctions { return ((workflow: Workflow, node: INode) => { const that = { getCredentials(type: string): ICredentialDataDecryptedObject | undefined { @@ -919,6 +926,9 @@ export function getExecuteHookFunctions(workflow: Workflow, node: INode, additio getMode: (): WorkflowExecuteMode => { return mode; }, + getActivationMode: (): WorkflowActivateMode => { + return activation; + }, getNode: () => { return getNode(node); }, diff --git a/packages/nodes-base/nodes/ActivationTrigger.node.ts b/packages/nodes-base/nodes/ActivationTrigger.node.ts new file mode 100644 index 0000000000..83c656ade1 --- /dev/null +++ b/packages/nodes-base/nodes/ActivationTrigger.node.ts @@ -0,0 +1,73 @@ +import { ITriggerFunctions } from 'n8n-core'; +import { + INodeType, + INodeTypeDescription, + ITriggerResponse, +} from 'n8n-workflow'; + +export class ActivationTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Activation Trigger', + name: 'activationTrigger', + icon: 'fa:play-circle', + group: ['trigger'], + version: 1, + description: 'Executes whenever the workflow becomes active.', + defaults: { + name: 'Activation Trigger', + color: '#00e000', + }, + inputs: [], + outputs: ['main'], + properties: [ + { + displayName: 'Events', + name: 'events', + type: 'multiOptions', + required: true, + default: [], + description: 'Specifies under which conditions an execution should happen:
' + + '- Activation: Workflow gets activated
' + + '- Update: Workflow gets saved while active
' + + '- Start: n8n starts or restarts', + options: [ + { + name: 'Activation', + value: 'activate', + description: 'Run when workflow gets activated', + }, + { + name: 'Start', + value: 'init', + description: 'Run when n8n starts or restarts', + }, + { + name: 'Update', + value: 'update', + description: 'Run when workflow gets saved while it is active', + }, + ], + }, + ], + }; + + + async trigger(this: ITriggerFunctions): Promise { + const events = this.getNodeParameter('events', []) as string[]; + + const activationMode = this.getActivationMode(); + + if (events.includes(activationMode)) { + this.emit([this.helpers.returnJsonArray([{ activation: activationMode }])]); + } + + const self = this; + async function manualTriggerFunction() { + self.emit([self.helpers.returnJsonArray([{ activation: 'manual' }])]); + } + + return { + manualTriggerFunction, + }; + } +} diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 7660234d24..cafa140270 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -264,6 +264,7 @@ "dist/credentials/ZulipApi.credentials.js" ], "nodes": [ + "dist/nodes/ActivationTrigger.node.js", "dist/nodes/ActiveCampaign/ActiveCampaign.node.js", "dist/nodes/ActiveCampaign/ActiveCampaignTrigger.node.js", "dist/nodes/AgileCrm/AgileCrm.node.js", diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 6f22b903d6..b57837379e 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -165,11 +165,11 @@ export interface IDataObject { export interface IGetExecutePollFunctions { - (workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): IPollFunctions; + (workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): IPollFunctions; } export interface IGetExecuteTriggerFunctions { - (workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): ITriggerFunctions; + (workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): ITriggerFunctions; } @@ -184,7 +184,7 @@ export interface IGetExecuteSingleFunctions { export interface IGetExecuteHookFunctions { - (workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, isTest?: boolean, webhookData?: IWebhookData): IHookFunctions; + (workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode, isTest?: boolean, webhookData?: IWebhookData): IHookFunctions; } @@ -271,6 +271,7 @@ export interface ILoadOptionsFunctions { export interface IHookFunctions { getCredentials(type: string): ICredentialDataDecryptedObject | undefined; getMode(): WorkflowExecuteMode; + getActivationMode(): WorkflowActivateMode; getNode(): INode; getNodeWebhookUrl: (name: string) => string | undefined; getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any @@ -288,6 +289,7 @@ export interface IPollFunctions { __emit(data: INodeExecutionData[][]): void; getCredentials(type: string): ICredentialDataDecryptedObject | undefined; getMode(): WorkflowExecuteMode; + getActivationMode(): WorkflowActivateMode; getNode(): INode; getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any getRestApiUrl(): string; @@ -303,6 +305,7 @@ export interface ITriggerFunctions { emit(data: INodeExecutionData[][]): void; getCredentials(type: string): ICredentialDataDecryptedObject | undefined; getMode(): WorkflowExecuteMode; + getActivationMode(): WorkflowActivateMode; getNode(): INode; getNodeParameter(parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object; //tslint:disable-line:no-any getRestApiUrl(): string; @@ -750,6 +753,7 @@ export interface IWorkflowExecuteAdditionalData { } export type WorkflowExecuteMode = 'cli' | 'error' | 'integrated' | 'internal' | 'manual' | 'retry' | 'trigger' | 'webhook'; +export type WorkflowActivateMode = 'init' | 'create' | 'update' | 'activate' | 'manual'; export interface IWorkflowHooksOptionalParameters { parentProcessMode?: string; diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index d221ba28de..026fa80086 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -24,6 +24,7 @@ import { NodeParameterValue, ObservableObject, WebhookSetupMethodNames, + WorkflowActivateMode, WorkflowExecuteMode, } from './'; @@ -769,7 +770,7 @@ export class Workflow { * @returns {(Promise)} * @memberof Workflow */ - async runWebhookMethod(method: WebhookSetupMethodNames, webhookData: IWebhookData, nodeExecuteFunctions: INodeExecuteFunctions, mode: WorkflowExecuteMode, isTest?: boolean): Promise { + async runWebhookMethod(method: WebhookSetupMethodNames, webhookData: IWebhookData, nodeExecuteFunctions: INodeExecuteFunctions, mode: WorkflowExecuteMode, activation: WorkflowActivateMode, isTest?: boolean): Promise { const node = this.getNode(webhookData.node) as INode; const nodeType = this.nodeTypes.getByName(node.type) as INodeType; @@ -786,7 +787,7 @@ export class Workflow { return; } - const thisArgs = nodeExecuteFunctions.getExecuteHookFunctions(this, node, webhookData.workflowExecuteAdditionalData, mode, isTest, webhookData); + const thisArgs = nodeExecuteFunctions.getExecuteHookFunctions(this, node, webhookData.workflowExecuteAdditionalData, mode, activation, isTest, webhookData); return nodeType.webhookMethods[webhookData.webhookDescription.name][method]!.call(thisArgs); } @@ -802,8 +803,8 @@ export class Workflow { * @returns {(Promise)} * @memberof Workflow */ - async runTrigger(node: INode, getTriggerFunctions: IGetExecuteTriggerFunctions, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode): Promise { - const triggerFunctions = getTriggerFunctions(this, node, additionalData, mode); + async runTrigger(node: INode, getTriggerFunctions: IGetExecuteTriggerFunctions, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode): Promise { + const triggerFunctions = getTriggerFunctions(this, node, additionalData, mode, activation); const nodeType = this.nodeTypes.getByName(node.type); @@ -982,7 +983,7 @@ export class Workflow { } else if (nodeType.poll) { if (mode === 'manual') { // In manual mode run the poll function - const thisArgs = nodeExecuteFunctions.getExecutePollFunctions(this, node, additionalData, mode); + const thisArgs = nodeExecuteFunctions.getExecutePollFunctions(this, node, additionalData, mode, 'manual'); return nodeType.poll.call(thisArgs); } else { // In any other mode pass data through as it already contains the result of the poll @@ -991,7 +992,7 @@ export class Workflow { } else if (nodeType.trigger) { if (mode === 'manual') { // In manual mode start the trigger - const triggerResponse = await this.runTrigger(node, nodeExecuteFunctions.getExecuteTriggerFunctions, additionalData, mode); + const triggerResponse = await this.runTrigger(node, nodeExecuteFunctions.getExecuteTriggerFunctions, additionalData, mode, 'manual'); if (triggerResponse === undefined) { return null;