Run workflows in own independent subprocess

This commit is contained in:
Jan Oberhauser
2019-08-08 20:38:25 +02:00
parent abb0a52b08
commit d59a043e3f
21 changed files with 926 additions and 369 deletions

View File

@@ -1,172 +0,0 @@
import {
IRun,
IRunExecutionData,
Workflow,
WorkflowExecuteMode,
} from 'n8n-workflow';
import {
createDeferredPromise,
IExecutingWorkflowData,
IExecutionsCurrentSummary,
} from '.';
export class ActiveExecutions {
private nextId = 1;
private activeExecutions: {
[index: string]: IExecutingWorkflowData;
} = {};
private stopExecutions: string[] = [];
/**
* Add a new active execution
*
* @param {Workflow} workflow
* @param {IRunExecutionData} runExecutionData
* @param {WorkflowExecuteMode} mode
* @returns {string}
* @memberof ActiveExecutions
*/
add(workflow: Workflow, runExecutionData: IRunExecutionData, mode: WorkflowExecuteMode): string {
const executionId = this.nextId++;
this.activeExecutions[executionId] = {
runExecutionData,
startedAt: new Date(),
mode,
workflow,
postExecutePromises: [],
};
return executionId.toString();
}
/**
* Remove an active execution
*
* @param {string} executionId
* @param {IRun} fullRunData
* @returns {void}
* @memberof ActiveExecutions
*/
remove(executionId: string, fullRunData: IRun): void {
if (this.activeExecutions[executionId] === undefined) {
return;
}
// Resolve all the waiting promises
for (const promise of this.activeExecutions[executionId].postExecutePromises) {
promise.resolve(fullRunData);
}
// Remove from the list of active executions
delete this.activeExecutions[executionId];
const stopExecutionIndex = this.stopExecutions.indexOf(executionId);
if (stopExecutionIndex !== -1) {
// If it was on the stop-execution list remove it
this.stopExecutions.splice(stopExecutionIndex, 1);
}
}
/**
* Forces an execution to stop
*
* @param {string} executionId The id of the execution to stop
* @returns {(Promise<IRun | undefined>)}
* @memberof ActiveExecutions
*/
async stopExecution(executionId: string): Promise<IRun | undefined> {
if (this.activeExecutions[executionId] === undefined) {
// There is no execution running with that id
return;
}
if (!this.stopExecutions.includes(executionId)) {
// Add the execution to the stop list if it is not already on it
this.stopExecutions.push(executionId);
}
return this.getPostExecutePromise(executionId);
}
/**
* Returns a promise which will resolve with the data of the execution
* with the given id
*
* @param {string} executionId The id of the execution to wait for
* @returns {Promise<IRun>}
* @memberof ActiveExecutions
*/
async getPostExecutePromise(executionId: string): Promise<IRun> {
// Create the promise which will be resolved when the execution finished
const waitPromise = await createDeferredPromise<IRun>();
if (this.activeExecutions[executionId] === undefined) {
throw new Error(`There is no active execution with id "${executionId}".`);
}
this.activeExecutions[executionId].postExecutePromises.push(waitPromise);
return waitPromise.promise();
}
/**
* Returns if the execution should be stopped
*
* @param {string} executionId The execution id to check
* @returns {boolean}
* @memberof ActiveExecutions
*/
shouldBeStopped(executionId: string): boolean {
return this.stopExecutions.includes(executionId);
}
/**
* Returns all the currently active executions
*
* @returns {IExecutionsCurrentSummary[]}
* @memberof ActiveExecutions
*/
getActiveExecutions(): IExecutionsCurrentSummary[] {
const returnData: IExecutionsCurrentSummary[] = [];
let executionData;
for (const id of Object.keys(this.activeExecutions)) {
executionData = this.activeExecutions[id];
returnData.push(
{
id,
startedAt: executionData.startedAt,
mode: executionData.mode,
workflowId: executionData.workflow.id!,
}
);
}
return returnData;
}
}
let activeExecutionsInstance: ActiveExecutions | undefined;
export function getInstance(): ActiveExecutions {
if (activeExecutionsInstance === undefined) {
activeExecutionsInstance = new ActiveExecutions();
}
return activeExecutionsInstance;
}

View File

@@ -1,13 +1,10 @@
import {
IGetExecuteTriggerFunctions,
ITriggerResponse,
IWorkflowExecuteAdditionalData,
Workflow,
} from 'n8n-workflow';
import {
NodeExecuteFunctions,
} from './';
export interface WorkflowData {
workflow: Workflow;
@@ -65,7 +62,7 @@ export class ActiveWorkflows {
* @returns {Promise<void>}
* @memberof ActiveWorkflows
*/
async add(id: string, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData): Promise<void> {
async add(id: string, workflow: Workflow, additionalData: IWorkflowExecuteAdditionalData, getTriggerFunctions: IGetExecuteTriggerFunctions): Promise<void> {
console.log('ADD ID (active): ' + id);
this.workflowData[id] = {
@@ -75,7 +72,7 @@ export class ActiveWorkflows {
let triggerResponse: ITriggerResponse | undefined;
for (const triggerNode of triggerNodes) {
triggerResponse = await workflow.runTrigger(triggerNode, NodeExecuteFunctions, additionalData, 'trigger');
triggerResponse = await workflow.runTrigger(triggerNode, getTriggerFunctions, additionalData, 'trigger');
if (triggerResponse !== undefined) {
// If a response was given save it
this.workflowData[id].triggerResponse = triggerResponse;

View File

@@ -8,18 +8,12 @@ import {
ILoadOptionsFunctions as ILoadOptionsFunctionsBase,
INodeExecutionData,
INodeType,
IRun,
IRunExecutionData,
ITriggerFunctions as ITriggerFunctionsBase,
IWebhookFunctions as IWebhookFunctionsBase,
IWorkflowSettings as IWorkflowSettingsWorkflow,
Workflow,
WorkflowExecuteMode,
} from 'n8n-workflow';
import {
IDeferredPromise
} from '.';
import * as request from 'request';
import * as requestPromise from 'request-promise-native';
@@ -29,6 +23,12 @@ interface Constructable<T> {
}
export interface IProcessMessage {
data?: any; // tslint:disable-line:no-any
type: string;
}
export interface IExecuteFunctions extends IExecuteFunctionsBase {
helpers: {
prepareBinaryData(binaryData: Buffer, filePath?: string, mimeType?: string): Promise<IBinaryData>;
@@ -45,13 +45,6 @@ export interface IExecuteSingleFunctions extends IExecuteSingleFunctionsBase {
};
}
export interface IExecutingWorkflowData {
runExecutionData: IRunExecutionData;
startedAt: Date;
mode: WorkflowExecuteMode;
workflow: Workflow;
postExecutePromises: Array<IDeferredPromise<IRun>>;
}
export interface IExecutionsCurrentSummary {
id: string;

View File

@@ -314,33 +314,12 @@ export function getWebhookDescription(name: string, workflow: Workflow, node: IN
* @param {WorkflowExecuteMode} mode
* @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 {
return ((workflow: Workflow, node: INode) => {
return {
emit: (data: INodeExecutionData[][]): void => {
const workflowExecute = new WorkflowExecute(additionalData, mode);
const nodeExecutionStack: IExecuteData[] = [
{
node,
data: {
main: data,
}
}
];
const runExecutionData: IRunExecutionData = {
startData: {},
resultData: {
runData: {},
},
executionData: {
contextData: {},
nodeExecutionStack,
waitingExecution: {},
},
};
workflowExecute.runExecutionData(workflow, runExecutionData);
throw new Error('Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!');
},
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
return getCredentials(workflow, node, type, additionalData);

View File

@@ -1,5 +1,6 @@
import {
IConnection,
IDataObject,
IExecuteData,
IExecutionError,
INode,
@@ -16,21 +17,30 @@ import {
Workflow,
} from 'n8n-workflow';
import {
ActiveExecutions,
NodeExecuteFunctions,
} from './';
export class WorkflowExecute {
runExecutionData: IRunExecutionData;
private additionalData: IWorkflowExecuteAdditionalData;
private mode: WorkflowExecuteMode;
private activeExecutions: ActiveExecutions.ActiveExecutions;
private executionId: string | null = null;
constructor(additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode) {
constructor(additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, runExecutionData?: IRunExecutionData) {
this.additionalData = additionalData;
this.activeExecutions = ActiveExecutions.getInstance();
this.mode = mode;
this.runExecutionData = runExecutionData || {
startData: {
},
resultData: {
runData: {},
},
executionData: {
contextData: {},
nodeExecutionStack: [],
waitingExecution: {},
},
};
}
@@ -44,7 +54,7 @@ export class WorkflowExecute {
* @returns {(Promise<string>)}
* @memberof WorkflowExecute
*/
async run(workflow: Workflow, startNode?: INode, destinationNode?: string): Promise<string> {
async run(workflow: Workflow, startNode?: INode, destinationNode?: string): Promise<IRun> {
// Get the nodes to start workflow execution from
startNode = startNode || workflow.getStartNode(destinationNode);
@@ -75,7 +85,7 @@ export class WorkflowExecute {
}
];
const runExecutionData: IRunExecutionData = {
this.runExecutionData = {
startData: {
destinationNode,
runNodeFilter,
@@ -90,7 +100,7 @@ export class WorkflowExecute {
},
};
return this.runExecutionData(workflow, runExecutionData);
return this.processRunExecutionData(workflow);
}
@@ -105,8 +115,7 @@ export class WorkflowExecute {
* @returns {(Promise<string>)}
* @memberof WorkflowExecute
*/
async runPartialWorkflow(workflow: Workflow, runData: IRunData, startNodes: string[], destinationNode: string): Promise<string> {
async runPartialWorkflow(workflow: Workflow, runData: IRunData, startNodes: string[], destinationNode: string): Promise<IRun> {
let incomingNodeConnections: INodeConnections | undefined;
let connection: IConnection;
@@ -185,8 +194,7 @@ export class WorkflowExecute {
runNodeFilter = workflow.getParentNodes(destinationNode);
runNodeFilter.push(destinationNode);
const runExecutionData: IRunExecutionData = {
this.runExecutionData = {
startData: {
destinationNode,
runNodeFilter,
@@ -201,7 +209,7 @@ export class WorkflowExecute {
},
};
return await this.runExecutionData(workflow, runExecutionData);
return await this.processRunExecutionData(workflow);
}
@@ -240,7 +248,7 @@ export class WorkflowExecute {
}
addNodeToBeExecuted(workflow: Workflow, runExecutionData: IRunExecutionData, connectionData: IConnection, outputIndex: number, parentNodeName: string, nodeSuccessData: INodeExecutionData[][], runIndex: number): void {
addNodeToBeExecuted(workflow: Workflow, connectionData: IConnection, outputIndex: number, parentNodeName: string, nodeSuccessData: INodeExecutionData[][], runIndex: number): void {
let stillDataMissing = false;
// Check if node has multiple inputs as then we have to wait for all input data
@@ -250,33 +258,33 @@ export class WorkflowExecute {
let nodeWasWaiting = true;
// Check if there is already data for the node
if (runExecutionData.executionData!.waitingExecution[connectionData.node] === undefined) {
if (this.runExecutionData.executionData!.waitingExecution[connectionData.node] === undefined) {
// Node does not have data yet so create a new empty one
runExecutionData.executionData!.waitingExecution[connectionData.node] = {};
this.runExecutionData.executionData!.waitingExecution[connectionData.node] = {};
nodeWasWaiting = false;
}
if (runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] === undefined) {
if (this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] === undefined) {
// Node does not have data for runIndex yet so create also empty one and init it
runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] = {
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] = {
main: []
};
for (let i = 0; i < workflow.connectionsByDestinationNode[connectionData.node]['main'].length; i++) {
runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main.push(null);
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main.push(null);
}
}
// Add the new data
if (nodeSuccessData === null) {
runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main[connectionData.index] = null;
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main[connectionData.index] = null;
} else {
runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main[connectionData.index] = nodeSuccessData[outputIndex];
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main[connectionData.index] = nodeSuccessData[outputIndex];
}
// Check if all data exists now
let thisExecutionData: INodeExecutionData[] | null;
let allDataFound = true;
for (let i = 0; i < runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main.length; i++) {
thisExecutionData = runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main[i];
for (let i = 0; i < this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main.length; i++) {
thisExecutionData = this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex].main[i];
if (thisExecutionData === null) {
allDataFound = false;
break;
@@ -286,17 +294,17 @@ export class WorkflowExecute {
if (allDataFound === true) {
// All data exists for node to be executed
// So add it to the execution stack
runExecutionData.executionData!.nodeExecutionStack.push({
this.runExecutionData.executionData!.nodeExecutionStack.push({
node: workflow.nodes[connectionData.node],
data: runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex]
data: this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex]
});
// Remove the data from waiting
delete runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex];
delete this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex];
if (Object.keys(runExecutionData.executionData!.waitingExecution[connectionData.node]).length === 0) {
if (Object.keys(this.runExecutionData.executionData!.waitingExecution[connectionData.node]).length === 0) {
// No more data left for the node so also delete that one
delete runExecutionData.executionData!.waitingExecution[connectionData.node];
delete this.runExecutionData.executionData!.waitingExecution[connectionData.node];
}
return;
} else {
@@ -327,7 +335,7 @@ export class WorkflowExecute {
continue;
}
const executionStackNodes = runExecutionData.executionData!.nodeExecutionStack.map((stackData) => stackData.node.name);
const executionStackNodes = this.runExecutionData.executionData!.nodeExecutionStack.map((stackData) => stackData.node.name);
// Check if that node is also an output connection of the
// previously processed one
@@ -345,7 +353,7 @@ export class WorkflowExecute {
}
// Check if node got processed already
if (runExecutionData.resultData.runData[inputData.node] !== undefined) {
if (this.runExecutionData.resultData.runData[inputData.node] !== undefined) {
// Node got processed already so no need to add it
continue;
}
@@ -376,7 +384,7 @@ export class WorkflowExecute {
}
// Check if node got processed already
if (runExecutionData.resultData.runData[parentNode] !== undefined) {
if (this.runExecutionData.resultData.runData[parentNode] !== undefined) {
// Node got processed already so we can use the
// output data as input of this node
break;
@@ -393,7 +401,7 @@ export class WorkflowExecute {
if (workflow.connectionsByDestinationNode[nodeToAdd] === undefined) {
// Add only node if it does not have any inputs becuase else it will
// be added by its input node later anyway.
runExecutionData.executionData!.nodeExecutionStack.push(
this.runExecutionData.executionData!.nodeExecutionStack.push(
{
node: workflow.getNode(nodeToAdd) as INode,
data: {
@@ -428,15 +436,15 @@ export class WorkflowExecute {
if (stillDataMissing === true) {
// Additional data is needed to run node so add it to waiting
if (!runExecutionData.executionData!.waitingExecution.hasOwnProperty(connectionData.node)) {
runExecutionData.executionData!.waitingExecution[connectionData.node] = {};
if (!this.runExecutionData.executionData!.waitingExecution.hasOwnProperty(connectionData.node)) {
this.runExecutionData.executionData!.waitingExecution[connectionData.node] = {};
}
runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] = {
this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] = {
main: connectionDataArray
};
} else {
// All data is there so add it directly to stack
runExecutionData.executionData!.nodeExecutionStack.push({
this.runExecutionData.executionData!.nodeExecutionStack.push({
node: workflow.nodes[connectionData.node],
data: {
main: connectionDataArray
@@ -450,12 +458,11 @@ export class WorkflowExecute {
* Runs the given execution data.
*
* @param {Workflow} workflow
* @param {IRunExecutionData} runExecutionData
* @returns {Promise<string>}
* @memberof WorkflowExecute
*/
async runExecutionData(workflow: Workflow, runExecutionData: IRunExecutionData): Promise<string> {
const startedAt = new Date().getTime();
async processRunExecutionData(workflow: Workflow): Promise<IRun> {
const startedAt = new Date();
const workflowIssues = workflow.checkReadyForExecution();
if (workflowIssues !== null) {
@@ -471,39 +478,29 @@ export class WorkflowExecute {
let startTime: number;
let taskData: ITaskData;
if (runExecutionData.startData === undefined) {
runExecutionData.startData = {};
if (this.runExecutionData.startData === undefined) {
this.runExecutionData.startData = {};
}
this.executionId = this.activeExecutions.add(workflow, runExecutionData, this.mode);
this.executeHook('workflowExecuteBefore', [this.executionId]);
this.executeHook('workflowExecuteBefore', []);
let currentExecutionTry = '';
let lastExecutionTry = '';
// Wait for the next tick so that the executionId gets already returned.
// So it can directly be send to the editor-ui and is so aware of the
// executionId when the first push messages arrive.
process.nextTick(() => (async () => {
return (async () => {
executionLoop:
while (runExecutionData.executionData!.nodeExecutionStack.length !== 0) {
if (this.activeExecutions.shouldBeStopped(this.executionId!) === true) {
// The execution should be stopped
break;
}
while (this.runExecutionData.executionData!.nodeExecutionStack.length !== 0) {
nodeSuccessData = null;
executionError = undefined;
executionData = runExecutionData.executionData!.nodeExecutionStack.shift() as IExecuteData;
executionData = this.runExecutionData.executionData!.nodeExecutionStack.shift() as IExecuteData;
executionNode = executionData.node;
this.executeHook('nodeExecuteBefore', [this.executionId, executionNode.name]);
this.executeHook('nodeExecuteBefore', [executionNode.name]);
// Get the index of the current run
runIndex = 0;
if (runExecutionData.resultData.runData.hasOwnProperty(executionNode.name)) {
runIndex = runExecutionData.resultData.runData[executionNode.name].length;
if (this.runExecutionData.resultData.runData.hasOwnProperty(executionNode.name)) {
runIndex = this.runExecutionData.resultData.runData[executionNode.name].length;
}
currentExecutionTry = `${executionNode.name}:${runIndex}`;
@@ -512,7 +509,7 @@ export class WorkflowExecute {
throw new Error('Did stop execution because execution seems to be in endless loop.');
}
if (runExecutionData.startData!.runNodeFilter !== undefined && runExecutionData.startData!.runNodeFilter!.indexOf(executionNode.name) === -1) {
if (this.runExecutionData.startData!.runNodeFilter !== undefined && this.runExecutionData.startData!.runNodeFilter!.indexOf(executionNode.name) === -1) {
// If filter is set and node is not on filter skip it, that avoids the problem that it executes
// leafs that are parallel to a selected destinationNode. Normally it would execute them because
// they have the same parent and it executes all child nodes.
@@ -539,7 +536,7 @@ export class WorkflowExecute {
if (!executionData.data!.hasOwnProperty('main')) {
// ExecutionData does not even have the connection set up so can
// not have that data, so add it again to be executed later
runExecutionData.executionData!.nodeExecutionStack.push(executionData);
this.runExecutionData.executionData!.nodeExecutionStack.push(executionData);
lastExecutionTry = currentExecutionTry;
continue executionLoop;
}
@@ -549,7 +546,7 @@ export class WorkflowExecute {
// of both inputs has to be available to be able to process the node.
if (executionData.data!.main!.length < connectionIndex || executionData.data!.main![connectionIndex] === null) {
// Does not have the data of the connections so add back to stack
runExecutionData.executionData!.nodeExecutionStack.push(executionData);
this.runExecutionData.executionData!.nodeExecutionStack.push(executionData);
lastExecutionTry = currentExecutionTry;
continue executionLoop;
}
@@ -591,15 +588,8 @@ export class WorkflowExecute {
}
}
// Check again if the execution should be stopped else it
// could take forever to stop when each try takes a long time
if (this.activeExecutions.shouldBeStopped(this.executionId!) === true) {
// The execution should be stopped
break;
}
runExecutionData.resultData.lastNodeExecuted = executionData.node.name;
nodeSuccessData = await workflow.runNode(executionData.node, executionData.data, runExecutionData, runIndex, this.additionalData, NodeExecuteFunctions, this.mode);
this.runExecutionData.resultData.lastNodeExecuted = executionData.node.name;
nodeSuccessData = await workflow.runNode(executionData.node, executionData.data, this.runExecutionData, runIndex, this.additionalData, NodeExecuteFunctions, this.mode);
if (nodeSuccessData === null) {
// If null gets returned it means that the node did succeed
@@ -620,8 +610,8 @@ export class WorkflowExecute {
// Add the data to return to the user
// (currently does not get cloned as data does not get changed, maybe later we should do that?!?!)
if (!runExecutionData.resultData.runData.hasOwnProperty(executionNode.name)) {
runExecutionData.resultData.runData[executionNode.name] = [];
if (!this.runExecutionData.resultData.runData.hasOwnProperty(executionNode.name)) {
this.runExecutionData.resultData.runData[executionNode.name] = [];
}
taskData = {
startTime,
@@ -642,12 +632,12 @@ export class WorkflowExecute {
}
} else {
// Node execution did fail so add error and stop execution
runExecutionData.resultData.runData[executionNode.name].push(taskData);
this.runExecutionData.resultData.runData[executionNode.name].push(taskData);
// Add the execution data again so that it can get restarted
runExecutionData.executionData!.nodeExecutionStack.unshift(executionData);
this.runExecutionData.executionData!.nodeExecutionStack.unshift(executionData);
this.executeHook('nodeExecuteAfter', [this.executionId, executionNode.name, taskData]);
this.executeHook('nodeExecuteAfter', [executionNode.name, taskData]);
break;
}
@@ -658,11 +648,11 @@ export class WorkflowExecute {
'main': nodeSuccessData
} as ITaskDataConnections);
this.executeHook('nodeExecuteAfter', [this.executionId, executionNode.name, taskData]);
this.executeHook('nodeExecuteAfter', [executionNode.name, taskData]);
runExecutionData.resultData.runData[executionNode.name].push(taskData);
this.runExecutionData.resultData.runData[executionNode.name].push(taskData);
if (runExecutionData.startData && runExecutionData.startData.destinationNode && runExecutionData.startData.destinationNode === executionNode.name) {
if (this.runExecutionData.startData && this.runExecutionData.startData.destinationNode && this.runExecutionData.startData.destinationNode === executionNode.name) {
// If destination node is defined and got executed stop execution
continue;
}
@@ -686,7 +676,7 @@ export class WorkflowExecute {
return Promise.reject(new Error(`The node "${executionNode.name}" connects to not found node "${connectionData.node}"`));
}
this.addNodeToBeExecuted(workflow, runExecutionData, connectionData, parseInt(outputIndex, 10), executionNode.name, nodeSuccessData!, runIndex);
this.addNodeToBeExecuted(workflow, connectionData, parseInt(outputIndex, 10), executionNode.name, nodeSuccessData!, runIndex);
}
}
}
@@ -696,45 +686,61 @@ export class WorkflowExecute {
return Promise.resolve();
})()
.then(async () => {
const fullRunData: IRun = {
data: runExecutionData,
mode: this.mode,
startedAt: new Date(startedAt),
stoppedAt: new Date(),
};
if (executionError !== undefined) {
fullRunData.data.resultData.error = executionError;
} else {
fullRunData.finished = true;
}
this.activeExecutions.remove(this.executionId!, fullRunData);
await this.executeHook('workflowExecuteAfter', [fullRunData, this.executionId!]);
return fullRunData;
return this.processSuccessExecution(startedAt, workflow, executionError);
})
.catch(async (error) => {
const fullRunData: IRun = {
data: runExecutionData,
mode: this.mode,
startedAt: new Date(startedAt),
stoppedAt: new Date(),
};
const fullRunData = this.getFullRunData(startedAt);
fullRunData.data.resultData.error = {
message: error.message,
stack: error.stack,
};
this.activeExecutions.remove(this.executionId!, fullRunData);
// Check if static data changed
let newStaticData: IDataObject | undefined;
if (workflow.staticData.__dataChanged === true) {
// Static data of workflow changed
newStaticData = workflow.staticData;
}
await this.executeHook('workflowExecuteAfter', [fullRunData, this.executionId!]);
await this.executeHook('workflowExecuteAfter', [fullRunData, newStaticData]);
return fullRunData;
}));
});
return this.executionId;
}
async processSuccessExecution(startedAt: Date, workflow: Workflow, executionError?: IExecutionError): Promise<IRun> {
const fullRunData = this.getFullRunData(startedAt);
if (executionError !== undefined) {
fullRunData.data.resultData.error = executionError;
} else {
fullRunData.finished = true;
}
// Check if static data changed
let newStaticData: IDataObject | undefined;
if (workflow.staticData.__dataChanged === true) {
// Static data of workflow changed
newStaticData = workflow.staticData;
}
await this.executeHook('workflowExecuteAfter', [fullRunData, newStaticData]);
return fullRunData;
}
getFullRunData(startedAt: Date): IRun {
const fullRunData: IRun = {
data: this.runExecutionData,
mode: this.mode,
startedAt,
stoppedAt: new Date(),
};
return fullRunData;
}
}

View File

@@ -14,11 +14,9 @@ export * from './LoadNodeParameterOptions';
export * from './NodeExecuteFunctions';
export * from './WorkflowExecute';
import * as ActiveExecutions from './ActiveExecutions';
import * as NodeExecuteFunctions from './NodeExecuteFunctions';
import * as UserSettings from './UserSettings';
export {
ActiveExecutions,
NodeExecuteFunctions,
UserSettings,
};