@5minds/node-red-contrib-processcube 1.16.1-develop-c6444b-mhvwu80i → 1.16.1-develop-48cf80-mhvyy20p
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.
- package/externaltask-input.js +117 -1
- package/package.json +1 -1
package/externaltask-input.js
CHANGED
|
@@ -1,9 +1,110 @@
|
|
|
1
1
|
const EventEmitter = require('node:events');
|
|
2
2
|
|
|
3
|
+
class ExternalTaskNodeStates {
|
|
4
|
+
constructor(flowNodeInstanceId) {
|
|
5
|
+
this.flowNodeInstanceId = flowNodeInstanceId;
|
|
6
|
+
this.nodeStades = {}; // Track send calls per nodeId
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
markSended(nodeId) {
|
|
10
|
+
if (!this.nodeStades[nodeId]) {
|
|
11
|
+
this.nodeStades[nodeId] = { gotSend: false, gotCompleted: false };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
this.nodeStades[nodeId].gotSend = true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
markCompleted(nodeId) {
|
|
18
|
+
if (!this.nodeStades[nodeId]) {
|
|
19
|
+
this.nodeStades[nodeId] = { gotSend: false, gotCompleted: false };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.nodeStades[nodeId].gotCompleted = true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
checkIfCompletedWithoutSend(nodeId) {
|
|
26
|
+
const nodeState = this.nodeStades[nodeId];
|
|
27
|
+
const result = (nodeState && nodeState.gotCompleted && !nodeState.gotSend);
|
|
28
|
+
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
3
33
|
module.exports = function (RED) {
|
|
4
34
|
|
|
5
35
|
const os = require('os');
|
|
6
36
|
|
|
37
|
+
// Global dictionary for tracking external tasks by flowNodeInstanceId
|
|
38
|
+
const globalExternalTaskStates = {};
|
|
39
|
+
|
|
40
|
+
const raiseExternalTaskError = (flowNodeInstanceId, etwInputNodeId, nodeId) => {
|
|
41
|
+
const fullNode = RED.nodes.getNode(nodeId);
|
|
42
|
+
|
|
43
|
+
const wires = fullNode?.wires;
|
|
44
|
+
const hasConnectedOutputs = wires && wires.some(wireArray => wireArray && wireArray.length > 0);
|
|
45
|
+
|
|
46
|
+
if (hasConnectedOutputs) {
|
|
47
|
+
const inputNode = RED.nodes.getNode(etwInputNodeId);
|
|
48
|
+
|
|
49
|
+
if (inputNode && inputNode.eventEmitter) {
|
|
50
|
+
const errorMessage = `Node ${nodeId} (${fullNode.name || fullNode.type}) completed without sending output`;
|
|
51
|
+
const error = new Error(errorMessage);
|
|
52
|
+
error.errorCode = 'NODE_NO_OUTPUT';
|
|
53
|
+
error.errorDetails = RED.util.encodeObject({
|
|
54
|
+
flowNodeInstanceId: flowNodeInstanceId,
|
|
55
|
+
nodeId: nodeId,
|
|
56
|
+
nodeName: fullNode.name,
|
|
57
|
+
nodeType: fullNode.type
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
inputNode.eventEmitter.emit(`handle-${flowNodeInstanceId}`, error, true);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Example synchronous onSend hook
|
|
66
|
+
RED.hooks.add("onSend", (sendEvents) => {
|
|
67
|
+
for (const sendEvent of sendEvents) {
|
|
68
|
+
|
|
69
|
+
// Call send method on ExternalTaskState if this message has a flowNodeInstanceId
|
|
70
|
+
if (sendEvent.msg?.flowNodeInstanceId) {
|
|
71
|
+
let externalTaskNodeStates = globalExternalTaskStates[sendEvent.msg.flowNodeInstanceId];
|
|
72
|
+
|
|
73
|
+
if (!externalTaskNodeStates) {
|
|
74
|
+
externalTaskNodeStates = new ExternalTaskNodeStates(sendEvent.msg.flowNodeInstanceId);
|
|
75
|
+
globalExternalTaskStates[sendEvent.msg.flowNodeInstanceId] = externalTaskNodeStates;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
externalTaskNodeStates.markSended(sendEvent.source.node.id)
|
|
79
|
+
|
|
80
|
+
if (externalTaskNodeStates.checkIfCompletedWithoutSend(sendEvent.source.node.id)) {
|
|
81
|
+
raiseExternalTaskError(sendEvent.msg.flowNodeInstanceId, sendEvent.msg.etw_input_node_id, sendEvent.source.node.id);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const onCompleted = (completeEvent) => {
|
|
88
|
+
|
|
89
|
+
// Check if this is an external task message
|
|
90
|
+
if (completeEvent.msg?.flowNodeInstanceId) {
|
|
91
|
+
let externalTaskNodeStates = globalExternalTaskStates[completeEvent.msg.flowNodeInstanceId];
|
|
92
|
+
|
|
93
|
+
if (!externalTaskNodeStates) {
|
|
94
|
+
externalTaskNodeStates = new ExternalTaskNodeStates(completeEvent.msg.flowNodeInstanceId);
|
|
95
|
+
globalExternalTaskStates[completeEvent.msg.flowNodeInstanceId] = externalTaskNodeStates;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
externalTaskNodeStates.markCompleted(completeEvent.node.id);
|
|
99
|
+
|
|
100
|
+
if (externalTaskNodeStates.checkIfCompletedWithoutSend(completeEvent.node.id)) {
|
|
101
|
+
raiseExternalTaskError(completeEvent.msg.flowNodeInstanceId, completeEvent.msg.etw_input_node_id, completeEvent.node.id);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
RED.hooks.add("onComplete", onCompleted);
|
|
107
|
+
|
|
7
108
|
function ExternalTaskInput(config) {
|
|
8
109
|
RED.nodes.createNode(this, config);
|
|
9
110
|
var node = this;
|
|
@@ -357,15 +458,30 @@ module.exports = function (RED) {
|
|
|
357
458
|
return;
|
|
358
459
|
}
|
|
359
460
|
const etwCallback = async (payload, externalTask) => {
|
|
461
|
+
|
|
462
|
+
globalExternalTaskStates[externalTask.flowNodeInstanceId] = new ExternalTaskNodeStates(externalTask.flowNodeInstanceId);
|
|
463
|
+
|
|
360
464
|
const saveHandleCallback = (data, callback, msg) => {
|
|
361
465
|
try {
|
|
362
466
|
callback(data);
|
|
363
467
|
node.log(`send to engine *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}', topic '${node.topic}' and *processInstanceId* ${externalTask.processInstanceId}`);
|
|
468
|
+
|
|
469
|
+
// Remove ExternalTaskState from global dictionary
|
|
470
|
+
if (globalExternalTaskStates[externalTask.flowNodeInstanceId]) {
|
|
471
|
+
delete globalExternalTaskStates[externalTask.flowNodeInstanceId];
|
|
472
|
+
}
|
|
473
|
+
|
|
364
474
|
node.setFinishHandlingTaskStatus(externalTask);
|
|
365
475
|
} catch (error) {
|
|
476
|
+
// Remove ExternalTaskState from global dictionary on error as well
|
|
477
|
+
if (globalExternalTaskStates[externalTask.flowNodeInstanceId]) {
|
|
478
|
+
delete globalExternalTaskStates[externalTask.flowNodeInstanceId];
|
|
479
|
+
}
|
|
480
|
+
|
|
366
481
|
node.setErrorFinishHandlingTaskStatus(externalTask, error);
|
|
367
482
|
msg.error = error;
|
|
368
483
|
node.error(`failed send to engine *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}', topic '${node.topic}' and *processInstanceId* ${externalTask.processInstanceId}: ${error?.message}`, msg);
|
|
484
|
+
callback(error);
|
|
369
485
|
}
|
|
370
486
|
};
|
|
371
487
|
|
|
@@ -405,7 +521,7 @@ module.exports = function (RED) {
|
|
|
405
521
|
msg.etw_duration = new Date(msg.etw_finished_at) - new Date(msg.etw_started_at);
|
|
406
522
|
}
|
|
407
523
|
} catch (error) {
|
|
408
|
-
node.error(`failed to calculate duration: ${error?.message}`, msg);
|
|
524
|
+
node.error(`failed to calculate duration: ${error?.message}`, msg);
|
|
409
525
|
}
|
|
410
526
|
|
|
411
527
|
node.log(
|