@5minds/node-red-contrib-processcube 1.16.1-feature-5e019f-mhojvcvu → 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.
- package/externaltask-error.js +3 -3
- package/externaltask-input.js +145 -1
- package/package.json +1 -2
- package/externaltask-inject.html +0 -123
- package/externaltask-inject.js +0 -98
package/externaltask-error.js
CHANGED
|
@@ -3,10 +3,10 @@ module.exports = function (RED) {
|
|
|
3
3
|
RED.nodes.createNode(this, config);
|
|
4
4
|
var node = this;
|
|
5
5
|
|
|
6
|
-
node.on('input', function (msg) {
|
|
6
|
+
node.on('input', function (msg) {
|
|
7
7
|
const flowNodeInstanceId = msg.flowNodeInstanceId;
|
|
8
8
|
const etw_input_node_id = msg.etw_input_node_id;
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
if (!etw_input_node_id) {
|
|
11
11
|
node.error('Error: The message did not contain the required etw_input_node_id.');
|
|
12
12
|
} else {
|
|
@@ -24,7 +24,7 @@ module.exports = function (RED) {
|
|
|
24
24
|
if (msgError.code) {
|
|
25
25
|
errorCode = msgError.code;
|
|
26
26
|
errorMessage = msgError.message;
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
28
|
} else if (msg.errorCode) {
|
|
29
29
|
errorCode = msg.errorCode;
|
|
30
30
|
errorMessage = msg.errorMessage;
|
package/externaltask-input.js
CHANGED
|
@@ -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-feature-
|
|
3
|
+
"version": "1.16.1-feature-91fab0-mhvqo2et",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Node-RED nodes for ProcessCube",
|
|
6
6
|
"scripts": {
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"DataobjectInstanceQuery": "dataobject-instance-query.js",
|
|
40
40
|
"EndEventFinishedListener": "endevent-finished-listener.js",
|
|
41
41
|
"externaltaskInput": "externaltask-input.js",
|
|
42
|
-
"externaltaskInject": "externaltask-inject.js",
|
|
43
42
|
"externaltaskOutput": "externaltask-output.js",
|
|
44
43
|
"externaltaskError": "externaltask-error.js",
|
|
45
44
|
"externaltaskEventListener": "externaltask-event-listener.js",
|
package/externaltask-inject.html
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
<script type="text/javascript">
|
|
2
|
-
RED.nodes.registerType('externaltask-inject', {
|
|
3
|
-
category: 'ProcessCube',
|
|
4
|
-
color: '#02AFD6',
|
|
5
|
-
defaults: {
|
|
6
|
-
name: { value: '' },
|
|
7
|
-
payloadType: { value: 'json' },
|
|
8
|
-
payload: { value: '{}' },
|
|
9
|
-
taskType: { value: 'json' },
|
|
10
|
-
task: { value: '{}' }
|
|
11
|
-
},
|
|
12
|
-
inputs: 0,
|
|
13
|
-
outputs: 1,
|
|
14
|
-
icon: 'inject.svg',
|
|
15
|
-
label: function () {
|
|
16
|
-
return this.name || 'externaltask-inject';
|
|
17
|
-
},
|
|
18
|
-
button: {
|
|
19
|
-
onclick: function() {
|
|
20
|
-
var node = this;
|
|
21
|
-
|
|
22
|
-
$.ajax({
|
|
23
|
-
type: "POST",
|
|
24
|
-
url: "externaltask-inject/trigger/" + node.id,
|
|
25
|
-
success: function(resp) {
|
|
26
|
-
RED.notify("Message injected", "success");
|
|
27
|
-
},
|
|
28
|
-
error: function(xhr, status, err) {
|
|
29
|
-
RED.notify("Error injecting message: " + err, "error");
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
oneditprepare: function () {
|
|
35
|
-
// Initialize the typed input for payload
|
|
36
|
-
$('#node-input-payload').typedInput({
|
|
37
|
-
default: 'json',
|
|
38
|
-
types: ['str', 'num', 'bool', 'json', 'jsonata', 'flow', 'global']
|
|
39
|
-
});
|
|
40
|
-
// Restore the saved type for payload from hidden input
|
|
41
|
-
var payloadType = $('#node-input-payloadType').val();
|
|
42
|
-
if (payloadType) {
|
|
43
|
-
$('#node-input-payload').typedInput('type', payloadType);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Initialize the typed input for task
|
|
47
|
-
$('#node-input-task').typedInput({
|
|
48
|
-
default: 'json',
|
|
49
|
-
types: ['str', 'num', 'bool', 'json', 'jsonata', 'flow', 'global']
|
|
50
|
-
});
|
|
51
|
-
// Restore the saved type for task from hidden input
|
|
52
|
-
var taskType = $('#node-input-taskType').val();
|
|
53
|
-
if (taskType) {
|
|
54
|
-
$('#node-input-task').typedInput('type', taskType);
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
oneditsave: function () {
|
|
58
|
-
var payloadValue = $('#node-input-payload').typedInput('value');
|
|
59
|
-
var payloadType = $('#node-input-payload').typedInput('type');
|
|
60
|
-
this.payload = payloadValue;
|
|
61
|
-
this.payloadType = payloadType;
|
|
62
|
-
// Persist type to hidden input field
|
|
63
|
-
$('#node-input-payloadType').val(payloadType);
|
|
64
|
-
|
|
65
|
-
var taskValue = $('#node-input-task').typedInput('value');
|
|
66
|
-
var taskType = $('#node-input-task').typedInput('type');
|
|
67
|
-
this.task = taskValue;
|
|
68
|
-
this.taskType = taskType;
|
|
69
|
-
// Persist type to hidden input field
|
|
70
|
-
$('#node-input-taskType').val(taskType);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
</script>
|
|
74
|
-
|
|
75
|
-
<script type="text/html" data-template-name="externaltask-inject">
|
|
76
|
-
<div class="form-row">
|
|
77
|
-
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
78
|
-
<input type="text" id="node-input-name" placeholder="Name" />
|
|
79
|
-
</div>
|
|
80
|
-
<div class="form-row">
|
|
81
|
-
<label for="node-input-payload">msg.payload</label>
|
|
82
|
-
<input type="hidden" id="node-input-payloadType">
|
|
83
|
-
<input type="text" id="node-input-payload" style="width: 70%;">
|
|
84
|
-
</div>
|
|
85
|
-
<div class="form-row">
|
|
86
|
-
<label for="node-input-task">msg.task</label>
|
|
87
|
-
<input type="hidden" id="node-input-taskType">
|
|
88
|
-
<input type="text" id="node-input-task" style="width: 70%;">
|
|
89
|
-
</div>
|
|
90
|
-
</script>
|
|
91
|
-
|
|
92
|
-
<script type="text/markdown" data-help-name="externaltask-inject">
|
|
93
|
-
A lightweight alternative to the standard Node-RED inject node for testing external task workflows. Use this node to inject test messages directly into an external task workflow without needing a real external task from the ProcessCube engine.
|
|
94
|
-
|
|
95
|
-
This node is ideal for small-scale testing and debugging of external task workflows. For larger-scale testing with test cases or CI/CD integration, use the **externaltask-testing** node instead.
|
|
96
|
-
|
|
97
|
-
## Features
|
|
98
|
-
|
|
99
|
-
- Injects messages with configurable payload and task metadata
|
|
100
|
-
- Automatically generates `flowNodeInstanceId` and `processInstanceId` if not provided
|
|
101
|
-
- Sets required fields for compatibility with externaltask-output and externaltask-error nodes
|
|
102
|
-
- Supports multiple value types (JSON, string, number, boolean, JSONata, flow/global context)
|
|
103
|
-
|
|
104
|
-
## Configs
|
|
105
|
-
|
|
106
|
-
: name (string) : The name of the node
|
|
107
|
-
: payload (string | {}) : The payload to inject into the workflow
|
|
108
|
-
: task (string | {}) : The task object to inject
|
|
109
|
-
|
|
110
|
-
## Outputs
|
|
111
|
-
|
|
112
|
-
: payload (string | {}) : The injected payload with metadata (flowNodeInstanceId, processInstanceId, task)
|
|
113
|
-
: task (object) : The injected task object
|
|
114
|
-
: flowNodeInstanceId (string) : Unique identifier for the external task
|
|
115
|
-
: processInstanceId (string) : Reference to the process instance
|
|
116
|
-
: etw_input_node_id (string) : Reference to the injecting node
|
|
117
|
-
: etw_started_at (string) : ISO timestamp of when the task was injected
|
|
118
|
-
|
|
119
|
-
## References
|
|
120
|
-
|
|
121
|
-
- [The ProcessCube© Developer Network](https://processcube.io) - All documentation for the ProcessCube© platform
|
|
122
|
-
- [ProcessCube© LowCode Integration](https://processcube.io/docs/node-red) - LowCode integration in ProcessCube©
|
|
123
|
-
</script>
|
package/externaltask-inject.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
const { v4: uuidv4 } = require('uuid');
|
|
2
|
-
const EventEmitter = require('node:events');
|
|
3
|
-
|
|
4
|
-
module.exports = function (RED) {
|
|
5
|
-
function ExternalTaskInject(config) {
|
|
6
|
-
RED.nodes.createNode(this, config);
|
|
7
|
-
var node = this;
|
|
8
|
-
node.config = config;
|
|
9
|
-
node.eventEmitter = new EventEmitter();
|
|
10
|
-
|
|
11
|
-
node.on('input', function (msg, send, done) {
|
|
12
|
-
msg.flowNodeInstanceId = msg.flowNodeInstanceId ?? uuidv4();
|
|
13
|
-
msg.processInstanceId = msg.processInstanceId ?? uuidv4();
|
|
14
|
-
|
|
15
|
-
if (!msg.task) {
|
|
16
|
-
// Use configured task if available, otherwise create default
|
|
17
|
-
if (config.task) {
|
|
18
|
-
msg.task = config.task;
|
|
19
|
-
} else {
|
|
20
|
-
msg.task = {
|
|
21
|
-
flowNodeInstanceId: msg.flowNodeInstanceId,
|
|
22
|
-
processInstanceId: msg.processInstanceId,
|
|
23
|
-
task: {}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
msg.etw_started_at = new Date().toISOString();
|
|
28
|
-
msg.etw_input_node_id = node.id;
|
|
29
|
-
|
|
30
|
-
send(msg);
|
|
31
|
-
if (done) done();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
RED.nodes.registerType('externaltask-inject', ExternalTaskInject);
|
|
37
|
-
|
|
38
|
-
// Helper function to parse typed values
|
|
39
|
-
function parseTypedValue(value, type) {
|
|
40
|
-
switch(type) {
|
|
41
|
-
case 'str':
|
|
42
|
-
return value || '';
|
|
43
|
-
case 'num':
|
|
44
|
-
return Number(value) || 0;
|
|
45
|
-
case 'bool':
|
|
46
|
-
return value === 'true' || value === true;
|
|
47
|
-
case 'json':
|
|
48
|
-
try {
|
|
49
|
-
return JSON.parse(value || '{}');
|
|
50
|
-
} catch(parseErr) {
|
|
51
|
-
console.error("Invalid JSON: " + parseErr.message);
|
|
52
|
-
return {};
|
|
53
|
-
}
|
|
54
|
-
case 'jsonata':
|
|
55
|
-
// JSONata expressions would need to be evaluated in Node-RED context
|
|
56
|
-
// For now, treat as string
|
|
57
|
-
return value || '';
|
|
58
|
-
case 'flow':
|
|
59
|
-
case 'global':
|
|
60
|
-
// These are context references - store as string for now
|
|
61
|
-
return value || '';
|
|
62
|
-
default:
|
|
63
|
-
return value || '';
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// HTTP endpoint to handle button clicks
|
|
68
|
-
RED.httpAdmin.post('/externaltask-inject/trigger/:id', function(req, res) {
|
|
69
|
-
var node = RED.nodes.getNode(req.params.id);
|
|
70
|
-
if (node !== null && typeof node !== 'undefined') {
|
|
71
|
-
try {
|
|
72
|
-
var payloadValue = node.config.payload;
|
|
73
|
-
var payloadType = node.config.payloadType || 'json';
|
|
74
|
-
var payloadData = parseTypedValue(payloadValue, payloadType);
|
|
75
|
-
|
|
76
|
-
var taskValue = node.config.task;
|
|
77
|
-
var taskType = node.config.taskType || 'json';
|
|
78
|
-
var taskData = parseTypedValue(taskValue, taskType);
|
|
79
|
-
|
|
80
|
-
// Create a message with the configured payload and task
|
|
81
|
-
var msg = {
|
|
82
|
-
payload: payloadData,
|
|
83
|
-
task: taskData,
|
|
84
|
-
_msgid: uuidv4()
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
// Trigger the node's input handler with the configured message
|
|
88
|
-
node.receive(msg);
|
|
89
|
-
res.sendStatus(200);
|
|
90
|
-
} catch(err) {
|
|
91
|
-
res.sendStatus(500);
|
|
92
|
-
node.error("Error injecting message: " + err.message);
|
|
93
|
-
}
|
|
94
|
-
} else {
|
|
95
|
-
res.sendStatus(404);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
};
|