@fsai-flow/core 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/dist/index.d.ts +17 -0
  2. package/dist/index.js +61 -0
  3. package/dist/{src/lib → lib}/ActiveWebhooks.d.ts +1 -1
  4. package/dist/lib/ActiveWebhooks.js +177 -0
  5. package/dist/{src/lib → lib}/ActiveWorkflows.d.ts +3 -3
  6. package/dist/lib/ActiveWorkflows.js +465 -0
  7. package/dist/{src/lib → lib}/BinaryDataManager/FileSystem.d.ts +1 -1
  8. package/dist/lib/BinaryDataManager/FileSystem.js +180 -0
  9. package/dist/{src/lib → lib}/BinaryDataManager/index.d.ts +2 -2
  10. package/dist/lib/BinaryDataManager/index.js +129 -0
  11. package/dist/{src/lib → lib}/ChangeCase.js +11 -11
  12. package/dist/lib/Constants.js +18 -0
  13. package/dist/{src/lib → lib}/Credentials.d.ts +1 -1
  14. package/dist/{src/lib → lib}/Credentials.js +7 -8
  15. package/dist/{src/lib → lib}/FileSystem.d.ts +1 -1
  16. package/dist/lib/FileSystem.js +180 -0
  17. package/dist/{src/lib → lib}/InputConnectionDataLegacy.d.ts +1 -1
  18. package/dist/lib/InputConnectionDataLegacy.js +72 -0
  19. package/dist/{src/lib → lib}/Interfaces.d.ts +47 -48
  20. package/dist/{src/lib → lib}/Interfaces.js +0 -1
  21. package/dist/{src/lib → lib}/LoadNodeParameterOptions.d.ts +1 -1
  22. package/dist/lib/LoadNodeParameterOptions.js +152 -0
  23. package/dist/{src/lib → lib}/NodeExecuteFunctions.d.ts +9 -10
  24. package/dist/lib/NodeExecuteFunctions.js +2467 -0
  25. package/dist/{src/lib → lib}/NodesLoader/constants.d.ts +1 -1
  26. package/dist/lib/NodesLoader/constants.js +105 -0
  27. package/dist/{src/lib → lib}/NodesLoader/custom-directory-loader.d.ts +1 -1
  28. package/dist/lib/NodesLoader/custom-directory-loader.js +35 -0
  29. package/dist/{src/lib → lib}/NodesLoader/directory-loader.d.ts +1 -1
  30. package/dist/{src/lib → lib}/NodesLoader/directory-loader.js +80 -38
  31. package/dist/lib/NodesLoader/index.d.ts +5 -0
  32. package/dist/{src/lib → lib}/NodesLoader/index.js +5 -6
  33. package/dist/{src/lib → lib}/NodesLoader/lazy-package-directory-loader.d.ts +1 -1
  34. package/dist/lib/NodesLoader/lazy-package-directory-loader.js +44 -0
  35. package/dist/{src/lib → lib}/NodesLoader/load-class-in-isolation.js +6 -11
  36. package/dist/{src/lib → lib}/NodesLoader/package-directory-loader.d.ts +2 -2
  37. package/dist/{src/lib → lib}/NodesLoader/package-directory-loader.js +28 -36
  38. package/dist/{src/lib → lib}/NodesLoader/types.js +0 -1
  39. package/dist/{src/lib → lib}/RedisLeaderElectionManager.d.ts +1 -1
  40. package/dist/lib/RedisLeaderElectionManager.js +279 -0
  41. package/dist/lib/RequestTypes.d.ts +58 -0
  42. package/dist/lib/RequestTypes.js +8 -0
  43. package/dist/{src/lib → lib}/UserSettings.d.ts +1 -1
  44. package/dist/lib/UserSettings.js +269 -0
  45. package/dist/{src/lib → lib}/WorkflowExecute.d.ts +4 -4
  46. package/dist/{src/lib → lib}/WorkflowExecute.js +230 -178
  47. package/dist/{src/lib → lib}/index.d.ts +2 -2
  48. package/dist/lib/index.js +129 -0
  49. package/dist/{src/utils → utils}/crypto.js +2 -3
  50. package/package.json +59 -52
  51. package/dist/README.md +0 -31
  52. package/dist/package.json +0 -54
  53. package/dist/src/index.d.ts +0 -16
  54. package/dist/src/index.js +0 -30
  55. package/dist/src/index.js.map +0 -1
  56. package/dist/src/lib/ActiveWebhooks.js +0 -184
  57. package/dist/src/lib/ActiveWebhooks.js.map +0 -1
  58. package/dist/src/lib/ActiveWorkflows.js +0 -456
  59. package/dist/src/lib/ActiveWorkflows.js.map +0 -1
  60. package/dist/src/lib/BinaryDataManager/FileSystem.js +0 -179
  61. package/dist/src/lib/BinaryDataManager/FileSystem.js.map +0 -1
  62. package/dist/src/lib/BinaryDataManager/index.js +0 -146
  63. package/dist/src/lib/BinaryDataManager/index.js.map +0 -1
  64. package/dist/src/lib/ChangeCase.js.map +0 -1
  65. package/dist/src/lib/Constants.js +0 -19
  66. package/dist/src/lib/Constants.js.map +0 -1
  67. package/dist/src/lib/Credentials.js.map +0 -1
  68. package/dist/src/lib/FileSystem.js +0 -179
  69. package/dist/src/lib/FileSystem.js.map +0 -1
  70. package/dist/src/lib/InputConnectionDataLegacy.js +0 -79
  71. package/dist/src/lib/InputConnectionDataLegacy.js.map +0 -1
  72. package/dist/src/lib/Interfaces.js.map +0 -1
  73. package/dist/src/lib/LoadNodeParameterOptions.js +0 -150
  74. package/dist/src/lib/LoadNodeParameterOptions.js.map +0 -1
  75. package/dist/src/lib/NodeExecuteFunctions.js +0 -2479
  76. package/dist/src/lib/NodeExecuteFunctions.js.map +0 -1
  77. package/dist/src/lib/NodesLoader/constants.js +0 -106
  78. package/dist/src/lib/NodesLoader/constants.js.map +0 -1
  79. package/dist/src/lib/NodesLoader/custom-directory-loader.js +0 -36
  80. package/dist/src/lib/NodesLoader/custom-directory-loader.js.map +0 -1
  81. package/dist/src/lib/NodesLoader/directory-loader.js.map +0 -1
  82. package/dist/src/lib/NodesLoader/index.d.ts +0 -5
  83. package/dist/src/lib/NodesLoader/index.js.map +0 -1
  84. package/dist/src/lib/NodesLoader/lazy-package-directory-loader.js +0 -52
  85. package/dist/src/lib/NodesLoader/lazy-package-directory-loader.js.map +0 -1
  86. package/dist/src/lib/NodesLoader/load-class-in-isolation.js.map +0 -1
  87. package/dist/src/lib/NodesLoader/package-directory-loader.js.map +0 -1
  88. package/dist/src/lib/NodesLoader/types.js.map +0 -1
  89. package/dist/src/lib/RedisLeaderElectionManager.js +0 -294
  90. package/dist/src/lib/RedisLeaderElectionManager.js.map +0 -1
  91. package/dist/src/lib/UserSettings.js +0 -261
  92. package/dist/src/lib/UserSettings.js.map +0 -1
  93. package/dist/src/lib/WorkflowExecute.js.map +0 -1
  94. package/dist/src/lib/index.js +0 -146
  95. package/dist/src/lib/index.js.map +0 -1
  96. package/dist/src/utils/crypto.js.map +0 -1
  97. package/eslint.config.js +0 -19
  98. package/jest.config.ts +0 -10
  99. package/project.json +0 -19
  100. package/src/index.ts +0 -28
  101. package/src/lib/ActiveWebhooks.ts +0 -245
  102. package/src/lib/ActiveWorkflows.ts +0 -575
  103. package/src/lib/BinaryDataManager/FileSystem.ts +0 -214
  104. package/src/lib/BinaryDataManager/index.ts +0 -187
  105. package/src/lib/ChangeCase.ts +0 -45
  106. package/src/lib/Constants.ts +0 -16
  107. package/src/lib/Credentials.ts +0 -108
  108. package/src/lib/FileSystem.ts +0 -214
  109. package/src/lib/InputConnectionDataLegacy.ts +0 -123
  110. package/src/lib/Interfaces.ts +0 -338
  111. package/src/lib/LoadNodeParameterOptions.ts +0 -235
  112. package/src/lib/NodeExecuteFunctions.ts +0 -3700
  113. package/src/lib/NodesLoader/constants.ts +0 -112
  114. package/src/lib/NodesLoader/custom-directory-loader.ts +0 -31
  115. package/src/lib/NodesLoader/directory-loader.ts +0 -458
  116. package/src/lib/NodesLoader/index.ts +0 -5
  117. package/src/lib/NodesLoader/lazy-package-directory-loader.ts +0 -55
  118. package/src/lib/NodesLoader/load-class-in-isolation.ts +0 -19
  119. package/src/lib/NodesLoader/package-directory-loader.ts +0 -107
  120. package/src/lib/NodesLoader/types.ts +0 -14
  121. package/src/lib/RedisLeaderElectionManager.ts +0 -334
  122. package/src/lib/UserSettings.ts +0 -292
  123. package/src/lib/WorkflowExecute.ts +0 -1128
  124. package/src/lib/index.ts +0 -187
  125. package/src/utils/crypto.ts +0 -5
  126. package/tests/Credentials.test.ts +0 -88
  127. package/tests/Helpers.ts +0 -808
  128. package/tests/WorkflowExecute.test.ts +0 -1242
  129. package/tsconfig.json +0 -41
  130. package/tsconfig.lib.json +0 -10
  131. package/tsconfig.spec.json +0 -14
  132. /package/dist/{src/lib → lib}/ChangeCase.d.ts +0 -0
  133. /package/dist/{src/lib → lib}/Constants.d.ts +0 -0
  134. /package/dist/{src/lib → lib}/NodesLoader/load-class-in-isolation.d.ts +0 -0
  135. /package/dist/{src/lib → lib}/NodesLoader/types.d.ts +0 -0
  136. /package/dist/{src/utils → utils}/crypto.d.ts +0 -0
@@ -1,1128 +0,0 @@
1
- import PCancelable = require('p-cancelable');
2
-
3
- import {
4
- ExecutionError,
5
- IConnection,
6
- IDataObject,
7
- IExecuteData,
8
- INode,
9
- INodeConnections,
10
- INodeExecutionData,
11
- IRun,
12
- IRunData,
13
- IRunExecutionData,
14
- ITaskData,
15
- ITaskDataConnections,
16
- IWaitingForExecution,
17
- IWorkflowExecuteAdditionalData,
18
- LoggerProxy as Logger,
19
- NodeApiError,
20
- NodeOperationError,
21
- Workflow,
22
- WorkflowExecuteMode,
23
- WorkflowOperationError,
24
- } from '@fsai-flow/workflow';
25
- import { get } from 'lodash';
26
- import { NodeExecuteFunctions } from '..';
27
-
28
- export class WorkflowExecute {
29
- runExecutionData: IRunExecutionData;
30
-
31
- private additionalData: IWorkflowExecuteAdditionalData;
32
-
33
- private mode: WorkflowExecuteMode;
34
-
35
- constructor(
36
- additionalData: IWorkflowExecuteAdditionalData,
37
- mode: WorkflowExecuteMode,
38
- runExecutionData?: IRunExecutionData,
39
- ) {
40
- this.additionalData = additionalData;
41
- this.mode = mode;
42
- this.runExecutionData = runExecutionData || {
43
- startData: {},
44
- resultData: {
45
- runData: {},
46
- },
47
- executionData: {
48
- contextData: {},
49
- nodeExecutionStack: [],
50
- waitingExecution: {},
51
- },
52
- };
53
- }
54
-
55
- /**
56
- * Executes the given workflow.
57
- *
58
- * @param {Workflow} workflow The workflow to execute
59
- * @param {INode[]} [startNodes] Node to start execution from
60
- * @param {string} [destinationNode] Node to stop execution at
61
- * @returns {(Promise<string>)}
62
- * @memberof WorkflowExecute
63
- */
64
- // IMPORTANT: Do not add "async" to this function, it will then convert the
65
- // PCancelable to a regular Promise and does so not allow canceling
66
- // active executions anymore
67
- // eslint-disable-next-line @typescript-eslint/promise-function-async
68
- run(workflow: Workflow, startNode?: INode, destinationNode?: string): PCancelable<IRun> {
69
- // Get the nodes to start workflow execution from
70
- startNode = startNode || workflow.getStartNode(destinationNode);
71
-
72
- if (startNode === undefined) {
73
- throw new Error('No node to start the workflow from could be found!');
74
- }
75
-
76
- // If a destination node is given we only run the direct parent nodes and no others
77
- let runNodeFilter: string[] | undefined;
78
- if (destinationNode) {
79
- runNodeFilter = workflow.getParentNodes(destinationNode);
80
- runNodeFilter.push(destinationNode);
81
- }
82
-
83
- // Initialize the data of the start nodes
84
- const nodeExecutionStack: IExecuteData[] = [
85
- {
86
- node: startNode,
87
- data: {
88
- main: [
89
- [
90
- {
91
- json: {},
92
- },
93
- ],
94
- ],
95
- },
96
- },
97
- ];
98
-
99
- this.runExecutionData = {
100
- startData: {
101
- destinationNode,
102
- runNodeFilter,
103
- },
104
- resultData: {
105
- runData: {},
106
- },
107
- executionData: {
108
- contextData: {},
109
- nodeExecutionStack,
110
- waitingExecution: {},
111
- },
112
- };
113
-
114
- return this.processRunExecutionData(workflow);
115
- }
116
-
117
- /**
118
- * Executes the given workflow but only
119
- *
120
- * @param {Workflow} workflow The workflow to execute
121
- * @param {IRunData} runData
122
- * @param {string[]} startNodes Nodes to start execution from
123
- * @param {string} destinationNode Node to stop execution at
124
- * @returns {(Promise<string>)}
125
- * @memberof WorkflowExecute
126
- */
127
- // IMPORTANT: Do not add "async" to this function, it will then convert the
128
- // PCancelable to a regular Promise and does so not allow canceling
129
- // active executions anymore
130
- // eslint-disable-next-line @typescript-eslint/promise-function-async
131
- runPartialWorkflow(
132
- workflow: Workflow,
133
- runData: IRunData,
134
- startNodes: string[],
135
- destinationNode: string,
136
- ): PCancelable<IRun> {
137
- let incomingNodeConnections: INodeConnections | undefined;
138
- let connection: IConnection;
139
-
140
- const runIndex = 0;
141
-
142
- // Initialize the nodeExecutionStack and waitingExecution with
143
- // the data from runData
144
- const nodeExecutionStack: IExecuteData[] = [];
145
- const waitingExecution: IWaitingForExecution = {};
146
- for (const startNode of startNodes) {
147
- incomingNodeConnections = workflow.connectionsByDestinationNode[startNode];
148
-
149
- const incomingData: INodeExecutionData[][] = [];
150
-
151
- if (incomingNodeConnections === undefined) {
152
- // If it has no incoming data add the default empty data
153
- incomingData.push([
154
- {
155
- json: {},
156
- },
157
- ]);
158
- } else {
159
- // Get the data of the incoming connections
160
- for (const connections of incomingNodeConnections['main']) {
161
- for (let inputIndex = 0; inputIndex < connections.length; inputIndex++) {
162
- connection = connections[inputIndex];
163
- incomingData.push(
164
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
165
- runData[connection.node][runIndex].data![connection.type][connection.index]!,
166
- );
167
- }
168
- }
169
- }
170
-
171
- const executeData: IExecuteData = {
172
- node: workflow.getNode(startNode) as INode,
173
- data: {
174
- main: incomingData,
175
- },
176
- };
177
-
178
- nodeExecutionStack.push(executeData);
179
-
180
- // Check if the destinationNode has to be added as waiting
181
- // because some input data is already fully available
182
- incomingNodeConnections = workflow.connectionsByDestinationNode[destinationNode];
183
- if (incomingNodeConnections !== undefined) {
184
- for (const connections of incomingNodeConnections['main']) {
185
- for (let inputIndex = 0; inputIndex < connections.length; inputIndex++) {
186
- connection = connections[inputIndex];
187
-
188
- if (waitingExecution[destinationNode] === undefined) {
189
- waitingExecution[destinationNode] = {};
190
- }
191
- if (waitingExecution[destinationNode][runIndex] === undefined) {
192
- waitingExecution[destinationNode][runIndex] = {};
193
- }
194
- if (waitingExecution[destinationNode][runIndex][connection.type] === undefined) {
195
- waitingExecution[destinationNode][runIndex][connection.type] = [];
196
- }
197
-
198
- if (runData[connection.node] !== undefined) {
199
- // Input data exists so add as waiting
200
- // incomingDataDestination.push(runData[connection.node!][runIndex].data![connection.type][connection.index]);
201
- waitingExecution[destinationNode][runIndex][connection.type].push(
202
- runData[connection.node][runIndex].data![connection.type][connection.index],
203
- );
204
- } else {
205
- waitingExecution[destinationNode][runIndex][connection.type].push(null);
206
- }
207
- }
208
- }
209
- }
210
- }
211
-
212
- // Only run the parent nodes and no others
213
- let runNodeFilter: string[] | undefined;
214
- // eslint-disable-next-line prefer-const
215
- runNodeFilter = workflow.getParentNodes(destinationNode);
216
- runNodeFilter.push(destinationNode);
217
-
218
- this.runExecutionData = {
219
- startData: {
220
- destinationNode,
221
- runNodeFilter,
222
- },
223
- resultData: {
224
- runData,
225
- },
226
- executionData: {
227
- contextData: {},
228
- nodeExecutionStack,
229
- waitingExecution,
230
- },
231
- };
232
-
233
- return this.processRunExecutionData(workflow);
234
- }
235
-
236
- /**
237
- * Executes the hook with the given name
238
- *
239
- * @param {string} hookName
240
- * @param {any[]} parameters
241
- * @returns {Promise<IRun>}
242
- * @memberof WorkflowExecute
243
- */
244
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
245
- async executeHook(hookName: string, parameters: any[]): Promise<void> {
246
- // tslint:disable-line:no-any
247
- if (this.additionalData.hooks === undefined) {
248
- return;
249
- }
250
-
251
- // eslint-disable-next-line consistent-return
252
- return this.additionalData.hooks.executeHookFunctions(hookName, parameters);
253
- }
254
-
255
- /**
256
- * Checks the incoming connection does not receive any data
257
- */
258
- incomingConnectionIsEmpty(
259
- runData: IRunData,
260
- inputConnections: IConnection[],
261
- runIndex: number,
262
- ): boolean {
263
- // for (const inputConnection of workflow.connectionsByDestinationNode[nodeToAdd].main[0]) {
264
- for (const inputConnection of inputConnections) {
265
- const nodeIncomingData = get(
266
- runData,
267
- `[${inputConnection.node}][${runIndex}].data.main[${inputConnection.index}]`,
268
- );
269
- if (nodeIncomingData !== undefined && (nodeIncomingData as object[]).length !== 0) {
270
- return false;
271
- }
272
- }
273
- return true;
274
- }
275
-
276
- addNodeToBeExecuted(
277
- workflow: Workflow,
278
- connectionData: IConnection,
279
- outputIndex: number,
280
- parentNodeName: string,
281
- nodeSuccessData: INodeExecutionData[][],
282
- runIndex: number,
283
- ): void {
284
- let stillDataMissing = false;
285
-
286
- // Check if node has multiple inputs as then we have to wait for all input data
287
- // to be present before we can add it to the node-execution-stack
288
- if (workflow.connectionsByDestinationNode[connectionData.node]['main'].length > 1) {
289
- // Node has multiple inputs
290
- let nodeWasWaiting = true;
291
-
292
- // Check if there is already data for the node
293
- if (
294
- this.runExecutionData.executionData!.waitingExecution[connectionData.node] === undefined
295
- ) {
296
- // Node does not have data yet so create a new empty one
297
- this.runExecutionData.executionData!.waitingExecution[connectionData.node] = {};
298
- nodeWasWaiting = false;
299
- }
300
- if (
301
- this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] ===
302
- undefined
303
- ) {
304
- // Node does not have data for runIndex yet so create also empty one and init it
305
- this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] = {
306
- main: [],
307
- };
308
- for (
309
- let i = 0;
310
- i < workflow.connectionsByDestinationNode[connectionData.node]['main'].length;
311
- i++
312
- ) {
313
- this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex]['main'].push(null);
314
- }
315
- }
316
-
317
- // Add the new data
318
- if (nodeSuccessData === null) {
319
- this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex]['main'][
320
- connectionData.index
321
- ] = null;
322
- } else {
323
- this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex]['main'][
324
- connectionData.index
325
- ] = nodeSuccessData[outputIndex];
326
- }
327
-
328
- // Check if all data exists now
329
- let thisExecutionData: INodeExecutionData[] | null;
330
- let allDataFound = true;
331
- for (
332
- let i = 0;
333
- i <
334
- this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex]['main']
335
- .length;
336
- i++
337
- ) {
338
- thisExecutionData =
339
- this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex]['main'][
340
- i
341
- ];
342
- if (thisExecutionData === null) {
343
- allDataFound = false;
344
- break;
345
- }
346
- }
347
-
348
- if (allDataFound) {
349
- // All data exists for node to be executed
350
- // So add it to the execution stack
351
- this.runExecutionData.executionData!.nodeExecutionStack.push({
352
- node: workflow.nodes[connectionData.node],
353
- data: this.runExecutionData.executionData!.waitingExecution[connectionData.node][
354
- runIndex
355
- ],
356
- });
357
-
358
- // Remove the data from waiting
359
- delete this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex];
360
-
361
- if (
362
- Object.keys(this.runExecutionData.executionData!.waitingExecution[connectionData.node])
363
- .length === 0
364
- ) {
365
- // No more data left for the node so also delete that one
366
- delete this.runExecutionData.executionData!.waitingExecution[connectionData.node];
367
- }
368
- return;
369
- }
370
- stillDataMissing = true;
371
-
372
- if (!nodeWasWaiting) {
373
- // Get a list of all the output nodes that we can check for siblings easier
374
- const checkOutputNodes = [];
375
- // eslint-disable-next-line @typescript-eslint/no-for-in-array
376
- for (const outputIndexParent in workflow.connectionsBySourceNode[parentNodeName]['main']) {
377
- if (
378
- // eslint-disable-next-line no-prototype-builtins
379
- !workflow.connectionsBySourceNode[parentNodeName]['main'].hasOwnProperty(outputIndexParent)
380
- ) {
381
- continue;
382
- }
383
- for (const connectionDataCheck of workflow.connectionsBySourceNode[parentNodeName]['main'][
384
- outputIndexParent
385
- ]) {
386
- checkOutputNodes.push(connectionDataCheck.node);
387
- }
388
- }
389
-
390
- // Node was not on "waitingExecution" so it is the first time it gets
391
- // checked. So we have to go through all the inputs and check if they
392
- // are already on the list to be processed.
393
- // If that is not the case add it.
394
- for (
395
- let inputIndex = 0;
396
- inputIndex < workflow.connectionsByDestinationNode[connectionData.node]['main'].length;
397
- inputIndex++
398
- ) {
399
- for (const inputData of workflow.connectionsByDestinationNode[connectionData.node]['main'][
400
- inputIndex
401
- ]) {
402
- if (inputData.node === parentNodeName) {
403
- // Is the node we come from so its data will be available for sure
404
- continue;
405
- }
406
-
407
- const executionStackNodes = this.runExecutionData.executionData!.nodeExecutionStack.map(
408
- (stackData) => stackData.node.name,
409
- );
410
-
411
- // Check if that node is also an output connection of the
412
- // previously processed one
413
- if (inputData.node !== parentNodeName && checkOutputNodes.includes(inputData.node)) {
414
- // So the parent node will be added anyway which
415
- // will then process this node next. So nothing to do
416
- // unless the incoming data of the node is empty
417
- // because then it would not be executed
418
- if (
419
- !this.incomingConnectionIsEmpty(
420
- this.runExecutionData.resultData.runData,
421
- workflow.connectionsByDestinationNode[inputData.node]['main'][0],
422
- runIndex,
423
- )
424
- ) {
425
- continue;
426
- }
427
- }
428
-
429
- // Check if it is already in the execution stack
430
- if (executionStackNodes.includes(inputData.node)) {
431
- // Node is already on the list to be executed
432
- // so there is nothing to do
433
- continue;
434
- }
435
-
436
- // Check if node got processed already
437
- if (this.runExecutionData.resultData.runData[inputData.node] !== undefined) {
438
- // Node got processed already so no need to add it
439
- continue;
440
- }
441
-
442
- // Check if any of the parent nodes does not have any inputs. That
443
- // would mean that it has to get added to the list of nodes to process.
444
- const parentNodes = workflow.getParentNodes(inputData.node, 'main', -1);
445
- let nodeToAdd: string | undefined = inputData.node;
446
- parentNodes.push(inputData.node);
447
- parentNodes.reverse();
448
-
449
- for (const parentNode of parentNodes) {
450
- // Check if that node is also an output connection of the
451
- // previously processed one
452
- if (inputData.node !== parentNode && checkOutputNodes.includes(parentNode)) {
453
- // So the parent node will be added anyway which
454
- // will then process this node next. So nothing to do.
455
- nodeToAdd = undefined;
456
- break;
457
- }
458
-
459
- // Check if it is already in the execution stack
460
- if (executionStackNodes.includes(parentNode)) {
461
- // Node is already on the list to be executed
462
- // so there is nothing to do
463
- nodeToAdd = undefined;
464
- break;
465
- }
466
-
467
- // Check if node got processed already
468
- if (this.runExecutionData.resultData.runData[parentNode] !== undefined) {
469
- // Node got processed already so we can use the
470
- // output data as input of this node
471
- break;
472
- }
473
-
474
- nodeToAdd = parentNode;
475
- }
476
- const parentNodesNodeToAdd = workflow.getParentNodes(nodeToAdd as string);
477
- if (
478
- parentNodesNodeToAdd.includes(parentNodeName) &&
479
- nodeSuccessData[outputIndex].length === 0
480
- ) {
481
- // We do not add the node if there is no input data and the node that should be connected
482
- // is a child of the parent node. Because else it would run a node even though it should be
483
- // specifically not run, as it did not receive any data.
484
- nodeToAdd = undefined;
485
- }
486
-
487
- if (nodeToAdd === undefined) {
488
- // No node has to get added so process
489
- continue;
490
- }
491
-
492
- let addEmptyItem = false;
493
-
494
- if (workflow.connectionsByDestinationNode[nodeToAdd] === undefined) {
495
- // Add empty item if the node does not have any input connections
496
- addEmptyItem = true;
497
- } else if (
498
- this.incomingConnectionIsEmpty(
499
- this.runExecutionData.resultData.runData,
500
- workflow.connectionsByDestinationNode[nodeToAdd]['main'][0],
501
- runIndex,
502
- )
503
- ) {
504
- // Add empty item also if the input data is empty
505
- addEmptyItem = true;
506
- }
507
-
508
- if (addEmptyItem) {
509
- // Add only node if it does not have any inputs because else it will
510
- // be added by its input node later anyway.
511
- this.runExecutionData.executionData!.nodeExecutionStack.push({
512
- node: workflow.getNode(nodeToAdd) as INode,
513
- data: {
514
- main: [
515
- [
516
- {
517
- json: {},
518
- },
519
- ],
520
- ],
521
- },
522
- });
523
- }
524
- }
525
- }
526
- }
527
- }
528
-
529
- // Make sure the array has all the values
530
- const connectionDataArray: Array<INodeExecutionData[] | null> = [];
531
- for (let i: number = connectionData.index; i >= 0; i--) {
532
- connectionDataArray[i] = null;
533
- }
534
-
535
- // Add the data of the current execution
536
- if (nodeSuccessData === null) {
537
- connectionDataArray[connectionData.index] = null;
538
- } else {
539
- connectionDataArray[connectionData.index] = nodeSuccessData[outputIndex];
540
- }
541
-
542
- if (stillDataMissing) {
543
- // Additional data is needed to run node so add it to waiting
544
- if (
545
- // eslint-disable-next-line no-prototype-builtins
546
- !this.runExecutionData.executionData!.waitingExecution.hasOwnProperty(connectionData.node)
547
- ) {
548
- this.runExecutionData.executionData!.waitingExecution[connectionData.node] = {};
549
- }
550
- this.runExecutionData.executionData!.waitingExecution[connectionData.node][runIndex] = {
551
- main: connectionDataArray,
552
- };
553
- } else {
554
- // All data is there so add it directly to stack
555
- this.runExecutionData.executionData!.nodeExecutionStack.push({
556
- node: workflow.nodes[connectionData.node],
557
- data: {
558
- main: connectionDataArray,
559
- },
560
- });
561
- }
562
- }
563
-
564
- /**
565
- * Runs the given execution data.
566
- *
567
- * @param {Workflow} workflow
568
- * @returns {Promise<string>}
569
- * @memberof WorkflowExecute
570
- */
571
- // IMPORTANT: Do not add "async" to this function, it will then convert the
572
- // PCancelable to a regular Promise and does so not allow canceling
573
- // active executions anymore
574
- // eslint-disable-next-line @typescript-eslint/promise-function-async
575
- processRunExecutionData(workflow: Workflow): PCancelable<IRun> {
576
- Logger.verbose('Workflow execution started', { workflowId: workflow.id });
577
- console.log(`Workflow execution started: ID: ${workflow.id} - Name: ${workflow.name} - Active: ${workflow.active ? 'active' : 'inactive'}`);
578
-
579
- const startedAt = new Date();
580
- const startNode = this.runExecutionData.executionData!.nodeExecutionStack[0].node.name;
581
-
582
- let destinationNode: string | undefined;
583
- if (this.runExecutionData.startData && this.runExecutionData.startData.destinationNode) {
584
- destinationNode = this.runExecutionData.startData.destinationNode;
585
- }
586
-
587
- const workflowIssues = workflow.checkReadyForExecution({ startNode, destinationNode });
588
- if (workflowIssues !== null) {
589
- throw new Error(
590
- 'The workflow has issues and can for that reason not be executed. Please fix them first.',
591
- );
592
- }
593
-
594
- // Variables which hold temporary data for each node-execution
595
- let executionData: IExecuteData;
596
- let executionError: ExecutionError | undefined;
597
- let executionNode: INode;
598
- let nodeSuccessData: INodeExecutionData[][] | null | undefined;
599
- let runIndex: number;
600
- let startTime: number;
601
- let taskData: ITaskData;
602
-
603
- if (this.runExecutionData.startData === undefined) {
604
- this.runExecutionData.startData = {};
605
- }
606
-
607
- if (this.runExecutionData.waitTill) {
608
- const lastNodeExecuted = this.runExecutionData.resultData.lastNodeExecuted as string;
609
- this.runExecutionData.executionData!.nodeExecutionStack[0].node.disabled = true;
610
- this.runExecutionData.waitTill = undefined;
611
- this.runExecutionData.resultData.runData[lastNodeExecuted].pop();
612
- }
613
-
614
- let currentExecutionTry = '';
615
- let lastExecutionTry = '';
616
-
617
- return new PCancelable((resolve: (value?: IRun | PromiseLike<IRun>) => void, reject: (reason?: unknown) => void, onCancel: (callback: () => void) => void) => {
618
- let gotCancel = false;
619
-
620
- (onCancel as any).shouldReject = false;
621
- onCancel(() => {
622
- gotCancel = true;
623
- });
624
-
625
- const returnPromise = (async () => {
626
- try {
627
- await this.executeHook('workflowExecuteBefore', [workflow]);
628
- } catch (error) {
629
- // Set the error that it can be saved correctly
630
- executionError = {
631
- ...(error as NodeOperationError | NodeApiError),
632
- message: (error as NodeOperationError | NodeApiError).message,
633
- stack: (error as NodeOperationError | NodeApiError).stack,
634
- };
635
-
636
- // Set the incoming data of the node that it can be saved correctly
637
- // eslint-disable-next-line prefer-destructuring
638
- executionData = this.runExecutionData.executionData!.nodeExecutionStack[0];
639
- this.runExecutionData.resultData = {
640
- runData: {
641
- [executionData.node.name]: [
642
- {
643
- startTime,
644
- executionTime: new Date().getTime() - startTime,
645
- data: {
646
- main: executionData.data['main'],
647
- } as ITaskDataConnections,
648
- },
649
- ],
650
- },
651
- lastNodeExecuted: executionData.node.name,
652
- error: executionError,
653
- };
654
-
655
- throw error;
656
- }
657
-
658
- executionLoop: while (
659
- this.runExecutionData.executionData!.nodeExecutionStack.length !== 0
660
- ) {
661
- if (
662
- this.additionalData.executionTimeoutTimestamp !== undefined &&
663
- Date.now() >= this.additionalData.executionTimeoutTimestamp
664
- ) {
665
- gotCancel = true;
666
- }
667
-
668
- if (gotCancel) {
669
- return Promise.resolve();
670
- }
671
-
672
- nodeSuccessData = null;
673
- executionError = undefined;
674
- executionData =
675
- this.runExecutionData.executionData!.nodeExecutionStack.shift() as IExecuteData;
676
- executionNode = executionData.node;
677
-
678
- Logger.debug(`Start processing node "${executionNode.name}"`, {
679
- node: executionNode.name,
680
- workflowId: workflow.id,
681
- });
682
- await this.executeHook('nodeExecuteBefore', [executionNode.name]);
683
-
684
- // Get the index of the current run
685
- runIndex = 0;
686
- // eslint-disable-next-line no-prototype-builtins
687
- if (this.runExecutionData.resultData.runData.hasOwnProperty(executionNode.name)) {
688
- runIndex = this.runExecutionData.resultData.runData[executionNode.name].length;
689
- }
690
-
691
- currentExecutionTry = `${executionNode.name}:${runIndex}`;
692
-
693
- if (currentExecutionTry === lastExecutionTry) {
694
- throw new Error('Did stop execution because execution seems to be in endless loop.');
695
- }
696
-
697
- if (
698
- this.runExecutionData.startData!.runNodeFilter !== undefined &&
699
- this.runExecutionData.startData!.runNodeFilter.indexOf(executionNode.name) === -1
700
- ) {
701
- // If filter is set and node is not on filter skip it, that avoids the problem that it executes
702
- // leafs that are parallel to a selected destinationNode. Normally it would execute them because
703
- // they have the same parent and it executes all child nodes.
704
- continue;
705
- }
706
-
707
- // Check if all the data which is needed to run the node is available
708
- if (Object.prototype.hasOwnProperty.call(workflow.connectionsByDestinationNode, executionNode.name)) {
709
- // Check if the node has incoming connections
710
- if (Object.prototype.hasOwnProperty.call(workflow.connectionsByDestinationNode[executionNode.name], 'main')) {
711
- let inputConnections: IConnection[][];
712
- let connectionIndex: number;
713
-
714
- // eslint-disable-next-line prefer-const
715
- inputConnections = workflow.connectionsByDestinationNode[executionNode.name]['main'];
716
-
717
- for (
718
- connectionIndex = 0;
719
- connectionIndex < inputConnections.length;
720
- connectionIndex++
721
- ) {
722
- if (
723
- workflow.getHighestNode(executionNode.name, 'main', connectionIndex).length === 0
724
- ) {
725
- // If there is no valid incoming node (if all are disabled)
726
- // then ignore that it has inputs and simply execute it as it is without
727
- // any data
728
- continue;
729
- }
730
-
731
- if (!Object.prototype.hasOwnProperty.call(executionData.data, 'main')) {
732
- // ExecutionData does not even have the connection set up so can
733
- // not have that data, so add it again to be executed later
734
- this.runExecutionData.executionData!.nodeExecutionStack.push(executionData);
735
- lastExecutionTry = currentExecutionTry;
736
- continue executionLoop;
737
- }
738
-
739
- // Check if it has the data for all the inputs
740
- // The most nodes just have one but merge node for example has two and data
741
- // of both inputs has to be available to be able to process the node.
742
- if (
743
- executionData.data['main']!.length < connectionIndex ||
744
- executionData.data['main']![connectionIndex] === null
745
- ) {
746
- // Does not have the data of the connections so add back to stack
747
- this.runExecutionData.executionData!.nodeExecutionStack.push(executionData);
748
- lastExecutionTry = currentExecutionTry;
749
- continue executionLoop;
750
- }
751
- }
752
- }
753
- }
754
-
755
- // Clone input data that nodes can not mess up data of parallel nodes which receive the same data
756
- // TODO: Should only clone if multiple nodes get the same data or when it gets returned to frontned
757
- // is very slow so only do if needed
758
- startTime = new Date().getTime();
759
-
760
- let maxTries = 1;
761
- if (executionData.node.retryOnFail === true) {
762
- // TODO: Remove the hardcoded default-values here and also in NodeSettings.vue
763
- maxTries = Math.min(5, Math.max(2, executionData.node.maxTries || 3));
764
- }
765
-
766
- let waitBetweenTries = 0;
767
- if (executionData.node.retryOnFail === true) {
768
- // TODO: Remove the hardcoded default-values here and also in NodeSettings.vue
769
- waitBetweenTries = Math.min(
770
- 5000,
771
- Math.max(0, executionData.node.waitBetweenTries || 1000),
772
- );
773
- }
774
-
775
- for (let tryIndex = 0; tryIndex < maxTries; tryIndex++) {
776
- if (gotCancel) {
777
- return Promise.resolve();
778
- }
779
- try {
780
- if (tryIndex !== 0) {
781
- // Reset executionError from previous error try
782
- executionError = undefined;
783
- if (waitBetweenTries !== 0) {
784
- // TODO: Improve that in the future and check if other nodes can
785
- // be executed in the meantime
786
- // eslint-disable-next-line @typescript-eslint/no-shadow
787
- await new Promise((resolve) => {
788
- setTimeout(() => {
789
- resolve(undefined);
790
- }, waitBetweenTries);
791
- });
792
- }
793
- }
794
-
795
- Logger.debug(`Running node "${executionNode.name}" started`, {
796
- node: executionNode.name,
797
- workflowId: workflow.id,
798
- });
799
- nodeSuccessData = await workflow.runNode(
800
- executionData.node,
801
- executionData.data,
802
- this.runExecutionData,
803
- runIndex,
804
- this.additionalData,
805
- NodeExecuteFunctions,
806
- this.mode,
807
- );
808
- Logger.debug(`Running node "${executionNode.name}" finished successfully`, {
809
- node: executionNode.name,
810
- workflowId: workflow.id,
811
- });
812
-
813
- if (nodeSuccessData === undefined) {
814
- // Node did not get executed
815
- nodeSuccessData = null;
816
- } else {
817
- this.runExecutionData.resultData.lastNodeExecuted = executionData.node.name;
818
- }
819
-
820
- if (nodeSuccessData === null || nodeSuccessData[0][0] === undefined) {
821
- if (executionData.node.alwaysOutputData === true) {
822
- nodeSuccessData = nodeSuccessData || [];
823
- nodeSuccessData[0] = [
824
- {
825
- json: {},
826
- },
827
- ];
828
- }
829
- }
830
-
831
- if (nodeSuccessData === null && !this.runExecutionData.waitTill!) {
832
- // If null gets returned it means that the node did succeed
833
- // but did not have any data. So the branch should end
834
- // (meaning the nodes afterwards should not be processed)
835
- continue executionLoop;
836
- }
837
-
838
- break;
839
- } catch (error: any) {
840
- this.runExecutionData.resultData.lastNodeExecuted = executionData.node.name;
841
-
842
- executionError = {
843
- ...(error as NodeOperationError | NodeApiError),
844
- message: (error as NodeOperationError | NodeApiError).message,
845
- stack: (error as NodeOperationError | NodeApiError).stack,
846
- };
847
-
848
- // Check if it's a critical database connection error that should fail immediately
849
- const isCriticalConnectionError = error.message?.includes('too many clients') ||
850
- error.message?.includes('Connection terminated') ||
851
- error.message?.includes('connection is closed') ||
852
- error.message?.includes('ECONNRESET') ||
853
- error.message?.includes('ENOTFOUND') ||
854
- error.code === 'ECONNRESET' ||
855
- error.code === 'ENOTFOUND';
856
-
857
- if (isCriticalConnectionError) {
858
- Logger.error(`Critical database connection error in node "${executionNode.name}": ${error.message}`, {
859
- node: executionNode.name,
860
- workflowId: workflow.id,
861
- error: error.message
862
- });
863
-
864
- // For critical connection errors, break out of retry loop immediately
865
- // This will cause the workflow to fail on this node
866
- break;
867
- }
868
-
869
- Logger.debug(`Running node "${executionNode.name}" finished with error`, {
870
- node: executionNode.name,
871
- workflowId: workflow.id,
872
- });
873
- }
874
- }
875
-
876
- // Add the data to return to the user
877
- // (currently does not get cloned as data does not get changed, maybe later we should do that?!?!)
878
-
879
- if (!Object.prototype.hasOwnProperty.call(this.runExecutionData.resultData.runData, executionNode.name)) {
880
- this.runExecutionData.resultData.runData[executionNode.name] = [];
881
- }
882
- taskData = {
883
- startTime,
884
- executionTime: new Date().getTime() - startTime,
885
- };
886
-
887
- if (executionError !== undefined) {
888
- taskData.error = executionError;
889
-
890
- if (executionData.node.continueOnFail === true) {
891
- // Workflow should continue running even if node errors
892
- if (Object.prototype.hasOwnProperty.call(executionData.data, 'main') && executionData.data['main'].length > 0) {
893
- // Simply get the input data of the node if it has any and pass it through
894
- // to the next node
895
- if (executionData.data['main'][0] !== null) {
896
- nodeSuccessData = [executionData.data['main'][0]];
897
- }
898
- }
899
- } else {
900
- // Node execution did fail so add error and stop execution
901
- this.runExecutionData.resultData.runData[executionNode.name].push(taskData);
902
-
903
- // Add the execution data again so that it can get restarted
904
- this.runExecutionData.executionData!.nodeExecutionStack.unshift(executionData);
905
-
906
- await this.executeHook('nodeExecuteAfter', [
907
- executionNode.name,
908
- taskData,
909
- this.runExecutionData,
910
- ]);
911
-
912
- break;
913
- }
914
- }
915
-
916
- // Merge error information to default output for now
917
- // As the new nodes can report the errors in
918
- // the `error` property.
919
- for (const execution of nodeSuccessData!) {
920
- for (const lineResult of execution) {
921
- if (
922
- lineResult.json !== undefined &&
923
- lineResult.json['$error'] !== undefined &&
924
- lineResult.json['$error'] !== undefined
925
- ) {
926
- lineResult.error = lineResult.json['$error'] as NodeApiError | NodeOperationError;
927
- lineResult.json = {
928
- error: (lineResult.json['$error'] as NodeApiError | NodeOperationError).message,
929
- };
930
- } else if (lineResult.error !== undefined) {
931
- lineResult.json = { error: lineResult.error.message };
932
- }
933
- }
934
- }
935
-
936
- // Node executed successfully. So add data and go on.
937
- taskData.data = {
938
- main: nodeSuccessData,
939
- } as ITaskDataConnections;
940
-
941
- this.runExecutionData.resultData.runData[executionNode.name].push(taskData);
942
-
943
- if (this.runExecutionData.waitTill!) {
944
- await this.executeHook('nodeExecuteAfter', [
945
- executionNode.name,
946
- taskData,
947
- this.runExecutionData,
948
- ]);
949
-
950
- // Add the node back to the stack that the workflow can start to execute again from that node
951
- this.runExecutionData.executionData!.nodeExecutionStack.unshift(executionData);
952
-
953
- break;
954
- }
955
-
956
- if (
957
- this.runExecutionData.startData &&
958
- this.runExecutionData.startData.destinationNode &&
959
- this.runExecutionData.startData.destinationNode === executionNode.name
960
- ) {
961
- // Before stopping, make sure we are executing hooks so
962
- // That frontend is notified for example for manual executions.
963
- await this.executeHook('nodeExecuteAfter', [
964
- executionNode.name,
965
- taskData,
966
- this.runExecutionData,
967
- ]);
968
-
969
- // If destination node is defined and got executed stop execution
970
- continue;
971
- }
972
-
973
- // Add the nodes to which the current node has an output connection to that they can
974
- // be executed next
975
- if (Object.prototype.hasOwnProperty.call(workflow.connectionsBySourceNode, executionNode.name)) {
976
- if (Object.prototype.hasOwnProperty.call(workflow.connectionsBySourceNode[executionNode.name], 'main')) {
977
- let outputIndex: string;
978
- let connectionData: IConnection;
979
- // Iterate over all the outputs
980
-
981
- // Add the nodes to be executed
982
- // eslint-disable-next-line @typescript-eslint/no-for-in-array
983
- for (outputIndex in workflow.connectionsBySourceNode[executionNode.name]['main']) {
984
- if (
985
- !Object.prototype.hasOwnProperty.call(workflow.connectionsBySourceNode[executionNode.name]['main'],
986
- outputIndex,
987
- )
988
- ) {
989
- continue;
990
- }
991
-
992
- // Iterate over all the different connections of this output
993
- for (connectionData of workflow.connectionsBySourceNode[executionNode.name]['main'][
994
- outputIndex
995
- ]) {
996
- if (!Object.prototype.hasOwnProperty.call(workflow.nodes, connectionData.node)) {
997
- return Promise.reject(
998
- new Error(
999
- `The node "${executionNode.name}" connects to not found node "${connectionData.node}"`,
1000
- ),
1001
- );
1002
- }
1003
-
1004
- if (
1005
- nodeSuccessData![outputIndex] &&
1006
- (nodeSuccessData![outputIndex].length !== 0 || connectionData.index > 0)
1007
- ) {
1008
- // Add the node only if it did execute or if connected to second "optional" input
1009
- this.addNodeToBeExecuted(
1010
- workflow,
1011
- connectionData,
1012
- parseInt(outputIndex, 10),
1013
- executionNode.name,
1014
- nodeSuccessData!,
1015
- runIndex,
1016
- );
1017
- }
1018
- }
1019
- }
1020
- }
1021
- }
1022
-
1023
- // If we got here, it means that we did not stop executing from manual executions / destination.
1024
- // Execute hooks now to make sure that all hooks are executed properly
1025
- // Await is needed to make sure that we don't fall into concurrency problems
1026
- // When saving node execution data
1027
- await this.executeHook('nodeExecuteAfter', [
1028
- executionNode.name,
1029
- taskData,
1030
- this.runExecutionData,
1031
- ]);
1032
- }
1033
-
1034
- return Promise.resolve();
1035
- })()
1036
- .then(async () => {
1037
- if (gotCancel && executionError === undefined) {
1038
- return this.processSuccessExecution(
1039
- startedAt,
1040
- workflow,
1041
- new WorkflowOperationError('Workflow has been canceled or timed out!'),
1042
- );
1043
- }
1044
- return this.processSuccessExecution(startedAt, workflow, executionError);
1045
- })
1046
- .catch(async (error) => {
1047
- const fullRunData = this.getFullRunData(startedAt);
1048
-
1049
- fullRunData.data.resultData.error = {
1050
- ...error,
1051
- message: error.message,
1052
- stack: error.stack,
1053
- };
1054
-
1055
- // Check if static data changed
1056
- let newStaticData: IDataObject | undefined;
1057
- // eslint-disable-next-line no-underscore-dangle
1058
- if (workflow.staticData['__dataChanged'] === true) {
1059
- // Static data of workflow changed
1060
- newStaticData = workflow.staticData;
1061
- }
1062
- await this.executeHook('workflowExecuteAfter', [fullRunData, newStaticData]).catch(
1063
- // eslint-disable-next-line @typescript-eslint/no-shadow
1064
- (error) => {
1065
- // eslint-disable-next-line no-console
1066
- console.error('There was a problem running hook "workflowExecuteAfter"', error);
1067
- },
1068
- );
1069
-
1070
- return fullRunData;
1071
- });
1072
-
1073
- return returnPromise.then(resolve);
1074
- });
1075
- }
1076
-
1077
- async processSuccessExecution(
1078
- startedAt: Date,
1079
- workflow: Workflow,
1080
- executionError?: ExecutionError,
1081
- ): Promise<IRun> {
1082
- const fullRunData = this.getFullRunData(startedAt);
1083
-
1084
- if (executionError !== undefined) {
1085
- Logger.verbose(`Workflow execution finished with error`, {
1086
- error: executionError,
1087
- workflowId: workflow.id,
1088
- });
1089
- fullRunData.data.resultData.error = {
1090
- ...executionError,
1091
- message: executionError.message,
1092
- stack: executionError.stack,
1093
- } as ExecutionError;
1094
- } else if (this.runExecutionData.waitTill!) {
1095
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
1096
- Logger.verbose(`Workflow execution will wait until ${this.runExecutionData.waitTill}`, {
1097
- workflowId: workflow.id,
1098
- });
1099
- fullRunData.waitTill = this.runExecutionData.waitTill;
1100
- } else {
1101
- Logger.verbose(`Workflow execution finished successfully`, { workflowId: workflow.id });
1102
- fullRunData.finished = true;
1103
- }
1104
-
1105
- // Check if static data changed
1106
- let newStaticData: IDataObject | undefined;
1107
- // eslint-disable-next-line no-underscore-dangle
1108
- if (workflow.staticData['__dataChanged'] === true) {
1109
- // Static data of workflow changed
1110
- newStaticData = workflow.staticData;
1111
- }
1112
-
1113
- await this.executeHook('workflowExecuteAfter', [fullRunData, newStaticData]);
1114
-
1115
- return fullRunData;
1116
- }
1117
-
1118
- getFullRunData(startedAt: Date): IRun {
1119
- const fullRunData: IRun = {
1120
- data: this.runExecutionData,
1121
- mode: this.mode,
1122
- startedAt,
1123
- stoppedAt: new Date(),
1124
- };
1125
-
1126
- return fullRunData;
1127
- }
1128
- }