@5minds/node-red-contrib-processcube 1.16.1-develop-36f966-mh90fmsz → 1.16.1-feature-91fab0-mhvqo2et

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.
@@ -1,9 +1,128 @@
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
+ console.log(`[DEBUG] markSended - flowNodeInstanceId: ${this.flowNodeInstanceId}, nodeId: ${nodeId}, before: ${JSON.stringify(this.nodeStades[nodeId])}`);
15
+ this.nodeStades[nodeId].gotSend = true;
16
+ console.log(`[DEBUG] markSended - after: ${JSON.stringify(this.nodeStades[nodeId])}`);
17
+ }
18
+
19
+ markCompleted(nodeId) {
20
+ if (!this.nodeStades[nodeId]) {
21
+ this.nodeStades[nodeId] = { gotSend: false, gotCompleted: false };
22
+ }
23
+
24
+ console.log(`[DEBUG] markCompleted - flowNodeInstanceId: ${this.flowNodeInstanceId}, nodeId: ${nodeId}, before: ${JSON.stringify(this.nodeStades[nodeId])}`);
25
+ this.nodeStades[nodeId].gotCompleted = true;
26
+ console.log(`[DEBUG] markCompleted - after: ${JSON.stringify(this.nodeStades[nodeId])}`);
27
+ }
28
+
29
+ checkIfCompletedWithoutSend(nodeId) {
30
+ const nodeState = this.nodeStades[nodeId];
31
+ const result = (nodeState && nodeState.gotCompleted && !nodeState.gotSend);
32
+
33
+ console.log(`[DEBUG] checkIfCompletedWithoutSend - flowNodeInstanceId: ${this.flowNodeInstanceId}, nodeId: ${nodeId}, nodeState: ${JSON.stringify(nodeState)}, result: ${result}`);
34
+ return result;
35
+ }
36
+ }
37
+
3
38
  module.exports = function (RED) {
4
39
 
5
40
  const os = require('os');
6
41
 
42
+ // Global dictionary for tracking external tasks by flowNodeInstanceId
43
+ const globalExternalTaskStates = {};
44
+
45
+ const raiseExternalTaskError = (flowNodeInstanceId, etwInputNodeId, nodeId) => {
46
+ const fullNode = RED.nodes.getNode(nodeId);
47
+
48
+ const wires = fullNode?.wires;
49
+ const hasConnectedOutputs = wires && wires.some(wireArray => wireArray && wireArray.length > 0);
50
+
51
+ console.log(`[DEBUG] raiseExternalTaskError called for flowNodeInstanceId: ${flowNodeInstanceId}, nodeId: ${nodeId}, hasConnectedOutputs: ${hasConnectedOutputs}`);
52
+
53
+ if (hasConnectedOutputs) {
54
+ const inputNode = RED.nodes.getNode(etwInputNodeId);
55
+
56
+ if (inputNode && inputNode.eventEmitter) {
57
+ const errorMessage = `Node ${nodeId} (${fullNode.name || fullNode.type}) completed without sending output`;
58
+ const error = new Error(errorMessage);
59
+ error.errorCode = 'NODE_NO_OUTPUT';
60
+ error.errorDetails = RED.util.encodeObject({
61
+ flowNodeInstanceId: flowNodeInstanceId,
62
+ nodeId: nodeId,
63
+ nodeName: fullNode.name,
64
+ nodeType: fullNode.type
65
+ });
66
+
67
+ console.log(`[DEBUG] Emitting error event for flowNodeInstanceId: ${flowNodeInstanceId}, error: ${errorMessage}`);
68
+ inputNode.eventEmitter.emit(`handle-${flowNodeInstanceId}`, error, true);
69
+ } else {
70
+ console.log(`[DEBUG] Cannot raise error - inputNode or eventEmitter not found for etwInputNodeId: ${etwInputNodeId}`);
71
+ }
72
+ }
73
+ };
74
+
75
+ // Example synchronous onSend hook
76
+ RED.hooks.add("onSend", (sendEvents) => {
77
+ for (const sendEvent of sendEvents) {
78
+
79
+ // Call send method on ExternalTaskState if this message has a flowNodeInstanceId
80
+ if (sendEvent.msg?.flowNodeInstanceId) {
81
+ let externalTaskNodeStates = globalExternalTaskStates[sendEvent.msg.flowNodeInstanceId];
82
+
83
+ console.log(`[DEBUG] onSend - flowNodeInstanceId: ${sendEvent.msg.flowNodeInstanceId}, nodeId: ${sendEvent.source.node.id}, stateExists: ${!!externalTaskNodeStates}`);
84
+
85
+ if (!externalTaskNodeStates) {
86
+ console.log(`[DEBUG] onSend - Creating NEW ExternalTaskNodeStates for flowNodeInstanceId: ${sendEvent.msg.flowNodeInstanceId}`);
87
+ externalTaskNodeStates = new ExternalTaskNodeStates(sendEvent.msg.flowNodeInstanceId);
88
+ globalExternalTaskStates[sendEvent.msg.flowNodeInstanceId] = externalTaskNodeStates;
89
+ }
90
+
91
+ externalTaskNodeStates.markSended(sendEvent.source.node.id)
92
+
93
+ if (externalTaskNodeStates.checkIfCompletedWithoutSend(sendEvent.source.node.id)) {
94
+ console.log(`[DEBUG] onSend - Node completed without send detected! Raising error for nodeId: ${sendEvent.source.node.id}`);
95
+ raiseExternalTaskError(sendEvent.msg.flowNodeInstanceId, sendEvent.msg.etw_input_node_id, sendEvent.source.node.id);
96
+ }
97
+ }
98
+ }
99
+ });
100
+
101
+ const onCompleted = (completeEvent) => {
102
+
103
+ // Check if this is an external task message
104
+ if (completeEvent.msg?.flowNodeInstanceId) {
105
+ let externalTaskNodeStates = globalExternalTaskStates[completeEvent.msg.flowNodeInstanceId];
106
+
107
+ console.log(`[DEBUG] onComplete - flowNodeInstanceId: ${completeEvent.msg.flowNodeInstanceId}, nodeId: ${completeEvent.node.id}, stateExists: ${!!externalTaskNodeStates}`);
108
+
109
+ if (!externalTaskNodeStates) {
110
+ console.log(`[DEBUG] onComplete - Creating NEW ExternalTaskNodeStates for flowNodeInstanceId: ${completeEvent.msg.flowNodeInstanceId}`);
111
+ externalTaskNodeStates = new ExternalTaskNodeStates(completeEvent.msg.flowNodeInstanceId);
112
+ globalExternalTaskStates[completeEvent.msg.flowNodeInstanceId] = externalTaskNodeStates;
113
+ }
114
+
115
+ externalTaskNodeStates.markCompleted(completeEvent.node.id);
116
+
117
+ if (externalTaskNodeStates.checkIfCompletedWithoutSend(completeEvent.node.id)) {
118
+ console.log(`[DEBUG] onComplete - Node completed without send detected! Raising error for nodeId: ${completeEvent.node.id}`);
119
+ raiseExternalTaskError(completeEvent.msg.flowNodeInstanceId, completeEvent.msg.etw_input_node_id, completeEvent.node.id);
120
+ }
121
+ }
122
+ }
123
+
124
+ RED.hooks.add("onComplete", onCompleted);
125
+
7
126
  function ExternalTaskInput(config) {
8
127
  RED.nodes.createNode(this, config);
9
128
  var node = this;
@@ -349,15 +468,34 @@ module.exports = function (RED) {
349
468
  return;
350
469
  }
351
470
  const etwCallback = async (payload, externalTask) => {
471
+
472
+ console.log(`[DEBUG] etwCallback - NEW External Task received! flowNodeInstanceId: ${externalTask.flowNodeInstanceId}, processInstanceId: ${externalTask.processInstanceId}`);
473
+ console.log(`[DEBUG] etwCallback - Creating NEW ExternalTaskNodeStates for flowNodeInstanceId: ${externalTask.flowNodeInstanceId}`);
474
+ globalExternalTaskStates[externalTask.flowNodeInstanceId] = new ExternalTaskNodeStates(externalTask.flowNodeInstanceId);
475
+
352
476
  const saveHandleCallback = (data, callback, msg) => {
353
477
  try {
354
478
  callback(data);
355
479
  node.log(`send to engine *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}', topic '${node.topic}' and *processInstanceId* ${externalTask.processInstanceId}`);
480
+
481
+ // Remove ExternalTaskState from global dictionary
482
+ if (globalExternalTaskStates[externalTask.flowNodeInstanceId]) {
483
+ console.log(`[DEBUG] saveHandleCallback SUCCESS - Deleting ExternalTaskNodeStates for flowNodeInstanceId: ${externalTask.flowNodeInstanceId}`);
484
+ delete globalExternalTaskStates[externalTask.flowNodeInstanceId];
485
+ }
486
+
356
487
  node.setFinishHandlingTaskStatus(externalTask);
357
488
  } catch (error) {
489
+ // Remove ExternalTaskState from global dictionary on error as well
490
+ if (globalExternalTaskStates[externalTask.flowNodeInstanceId]) {
491
+ console.log(`[DEBUG] saveHandleCallback ERROR - Deleting ExternalTaskNodeStates for flowNodeInstanceId: ${externalTask.flowNodeInstanceId}, error: ${error?.message}`);
492
+ delete globalExternalTaskStates[externalTask.flowNodeInstanceId];
493
+ }
494
+
358
495
  node.setErrorFinishHandlingTaskStatus(externalTask, error);
359
496
  msg.error = error;
360
497
  node.error(`failed send to engine *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}', topic '${node.topic}' and *processInstanceId* ${externalTask.processInstanceId}: ${error?.message}`, msg);
498
+ callback(error);
361
499
  }
362
500
  };
363
501
 
@@ -378,6 +516,7 @@ module.exports = function (RED) {
378
516
  };
379
517
 
380
518
  const handleErrorTask = (error) => {
519
+ console.log(`[DEBUG] handleErrorTask - flowNodeInstanceId: ${externalTask.flowNodeInstanceId}, errorCode: ${error?.errorCode}, errorMessage: ${error?.message}`);
381
520
  node.log(
382
521
  `handle error event for *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}' on *msg._msgid* '${error.errorDetails?._msgid}'.`
383
522
  );
@@ -390,6 +529,8 @@ module.exports = function (RED) {
390
529
  };
391
530
 
392
531
  node.eventEmitter.once(`handle-${externalTask.flowNodeInstanceId}`, (msg, isError = false) => {
532
+ console.log(`[DEBUG] eventEmitter handle event - flowNodeInstanceId: ${externalTask.flowNodeInstanceId}, isError: ${isError}, msgId: ${msg._msgid}`);
533
+
393
534
  try {
394
535
  msg.etw_finished_at = new Date().toISOString();
395
536
 
@@ -397,7 +538,7 @@ module.exports = function (RED) {
397
538
  msg.etw_duration = new Date(msg.etw_finished_at) - new Date(msg.etw_started_at);
398
539
  }
399
540
  } catch (error) {
400
- node.error(`failed to calculate duration: ${error?.message}`, msg);
541
+ node.error(`failed to calculate duration: ${error?.message}`, msg);
401
542
  }
402
543
 
403
544
  node.log(
@@ -406,8 +547,10 @@ module.exports = function (RED) {
406
547
 
407
548
 
408
549
  if (isError) {
550
+ console.log(`[DEBUG] Routing to handleErrorTask`);
409
551
  handleErrorTask(msg);
410
552
  } else {
553
+ console.log(`[DEBUG] Routing to handleFinishTask`);
411
554
  handleFinishTask(msg);
412
555
  }
413
556
  });
@@ -429,6 +572,7 @@ module.exports = function (RED) {
429
572
  );
430
573
 
431
574
  node.send(msg);
575
+ console.log(`[DEBUG] etwCallback - Sent message for flowNodeInstanceId: ${externalTask.flowNodeInstanceId}, msgId: ${msg._msgid}`);
432
576
  });
433
577
  };
434
578
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@5minds/node-red-contrib-processcube",
3
- "version": "1.16.1-develop-36f966-mh90fmsz",
3
+ "version": "1.16.1-feature-91fab0-mhvqo2et",
4
4
  "license": "MIT",
5
5
  "description": "Node-RED nodes for ProcessCube",
6
6
  "scripts": {