@5minds/node-red-contrib-processcube 0.14.0-develop-7fe136-lygsbbpi → 0.14.0-error-handling-in-process-instance-query-and-add-prettier-8b2f33-lz08cr3v

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. package/.prettierrc.json +6 -0
  2. package/Dockerfile +1 -1
  3. package/docker-compose.yml +12 -0
  4. package/examples/External-Task-Sample.json +252 -0
  5. package/externaltask-error.html +26 -12
  6. package/externaltask-error.js +19 -22
  7. package/externaltask-input.html +31 -14
  8. package/externaltask-input.js +114 -57
  9. package/externaltask-output.html +20 -10
  10. package/externaltask-output.js +11 -12
  11. package/message-event-trigger.html +14 -14
  12. package/message-event-trigger.js +25 -33
  13. package/nodered/node-red-contrib-processcube-flows.json +216 -132
  14. package/nodered/package.json +1 -0
  15. package/nodered/settings.js +82 -92
  16. package/package.json +5 -2
  17. package/process-start.html +19 -23
  18. package/process-start.js +22 -27
  19. package/processcube-engine-config.html +10 -10
  20. package/processcube-engine-config.js +39 -17
  21. package/processdefinition-query.html +29 -23
  22. package/processdefinition-query.js +13 -25
  23. package/processes/External-Task-Sample.bpmn +94 -0
  24. package/processinstance-query.html +19 -20
  25. package/processinstance-query.js +17 -21
  26. package/signal-event-trigger.html +14 -14
  27. package/signal-event-trigger.js +25 -33
  28. package/usertask-finished-listener.html +17 -12
  29. package/usertask-finished-listener.js +34 -24
  30. package/usertask-input.html +40 -27
  31. package/usertask-input.js +34 -29
  32. package/usertask-new-listener.html +17 -12
  33. package/usertask-new-listener.js +35 -26
  34. package/usertask-output.html +23 -24
  35. package/usertask-output.js +13 -21
  36. package/processes/CheckError.bpmn +0 -78
  37. package/processes/NodeRedExternalTask.bpmn +0 -77
  38. package/processes/SampleUserTask.bpmn +0 -95
@@ -1,44 +1,24 @@
1
- const process = require('process');
2
1
  const EventEmitter = require('node:events');
3
2
 
4
- const engine_client = require('@5minds/processcube_engine_client');
5
-
6
3
  function showStatus(node, msgCounter) {
7
4
  if (msgCounter >= 1) {
8
- node.status({fill: "blue", shape: "dot", text: `handling tasks ${msgCounter}`});
5
+ node.status({ fill: 'blue', shape: 'dot', text: `handling tasks ${msgCounter}.` });
9
6
  } else {
10
- node.status({fill: "blue", shape: "ring", text: `subcribed ${msgCounter}`});
7
+ node.status({ fill: 'blue', shape: 'ring', text: `subcribed.` });
11
8
  }
12
9
  }
13
10
 
14
- function decrCounter(msgCounter) {
15
- msgCounter--;
16
-
17
- if (msgCounter < 0) {
18
- msgCounter = 0;
19
- }
20
-
21
- return msgCounter;
22
- }
11
+ const started_external_tasks = {};
23
12
 
24
- module.exports = function(RED) {
13
+ module.exports = function (RED) {
25
14
  function ExternalTaskInput(config) {
26
- RED.nodes.createNode(this,config);
15
+ RED.nodes.createNode(this, config);
27
16
  var node = this;
28
- var msgCounter = 0;
29
17
  var flowContext = node.context().flow;
30
- var nodeContext = node.context();
31
18
 
32
19
  this.engine = this.server = RED.nodes.getNode(config.engine);
33
20
 
34
- const engineUrl = this.engine?.url || process.env.ENGINE_URL || 'http://engine:8000';
35
-
36
- var client = nodeContext.get('client');
37
-
38
- if (!client) {
39
- nodeContext.set('client', new engine_client.EngineClient(engineUrl));
40
- client = nodeContext.get('client');
41
- }
21
+ const client = this.engine.getEngineClient();
42
22
 
43
23
  var eventEmitter = flowContext.get('emitter');
44
24
 
@@ -47,53 +27,130 @@ module.exports = function(RED) {
47
27
  eventEmitter = flowContext.get('emitter');
48
28
  }
49
29
 
50
- client.externalTasks.subscribeToExternalTaskTopic(
51
- config.topic,
52
- async (payload, externalTask) => {
53
- msgCounter++;
30
+ client.externalTasks
31
+ .subscribeToExternalTaskTopic(config.topic, async (payload, externalTask) => {
32
+ const saveHandleCallback = (data, callback) => {
33
+ try {
34
+ callback(data);
35
+ } catch (error) {
36
+ node.error(`Error in callback 'saveHandleCallback': ${error.message}`);
37
+ }
38
+ };
54
39
 
55
40
  return await new Promise((resolve, reject) => {
56
-
57
- // TODO: once ist 2x gebunden
58
- eventEmitter.once(`finish-${externalTask.flowNodeInstanceId}`, (result) => {
59
- msgCounter = decrCounter(msgCounter);
60
-
61
- showStatus(node, msgCounter);
62
- resolve(result);
41
+ const handleFinishTask = (msg) => {
42
+ let result = RED.util.encodeObject(msg.payload);
43
+
44
+ node.log(
45
+ `handle event for *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* ${externalTask.processInstanceId} with result ${result} on msg._msgid ${msg._msgid}.`,
46
+ );
47
+
48
+ if (externalTask.flowNodeInstanceId) {
49
+ delete started_external_tasks[externalTask.flowNodeInstanceId];
50
+ }
51
+
52
+ showStatus(node, Object.keys(started_external_tasks).length);
53
+
54
+ //resolve(result);
55
+ saveHandleCallback(result, resolve);
56
+ };
57
+
58
+ const handleErrorTask = (msg) => {
59
+ node.log(
60
+ `handle error event for *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}' on *msg._msgid* '${msg._msgid}'.`,
61
+ );
62
+
63
+ if (externalTask.flowNodeInstanceId) {
64
+ delete started_external_tasks[externalTask.flowNodeInstanceId];
65
+ }
66
+
67
+ showStatus(node, Object.keys(started_external_tasks).length);
68
+
69
+ // TODO: with reject, the default error handling is proceed
70
+ // SEE: https://github.com/5minds/ProcessCube.Engine.Client.ts/blob/develop/src/ExternalTaskWorker.ts#L180
71
+ // reject(result);
72
+ //resolve(msg);
73
+ saveHandleCallback(msg, resolve);
74
+ };
75
+
76
+ eventEmitter.once(`handle-${externalTask.flowNodeInstanceId}`, (msg, isError = false) => {
77
+ node.log(
78
+ `handle event for *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}' with *msg._msgid* '${msg._msgid}' and *isError* '${isError}'`,
79
+ );
80
+
81
+ if (isError) {
82
+ handleErrorTask(msg);
83
+ } else {
84
+ handleFinishTask(msg);
85
+ }
63
86
  });
64
87
 
65
- eventEmitter.once(`error-${externalTask.flowNodeInstanceId}`, (msg) => {
66
- msgCounter = decrCounter(msgCounter);
67
- showStatus(node, msgCounter);
88
+ started_external_tasks[externalTask.flowNodeInstanceId] = externalTask;
68
89
 
69
- var result = msg.payload ? msg.payload : msg;
90
+ showStatus(node, Object.keys(started_external_tasks).length);
70
91
 
71
- reject(result);
72
- });
92
+ let msg = {
93
+ _msgid: RED.util.generateId(),
94
+ task: RED.util.encodeObject(externalTask),
95
+ payload: payload,
96
+ flowNodeInstanceId: externalTask.flowNodeInstanceId,
97
+ };
98
+
99
+ node.log(
100
+ `Received *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}' with *msg._msgid* '${msg._msgid}'`,
101
+ );
73
102
 
74
- showStatus(node, msgCounter);
75
-
76
- node.send({topic: externalTask.topic, payload: payload, externalTaskId: externalTask.flowNodeInstanceId});
103
+ node.send(msg);
77
104
  });
78
- },
79
- ).then(async externalTaskWorker => {
80
- node.status({fill: "blue", shape: "ring", text: "subcribed"});
105
+ })
106
+ .then(async (externalTaskWorker) => {
107
+ node.status({ fill: 'blue', shape: 'ring', text: 'subcribed' });
81
108
 
82
109
  externalTaskWorker.identity = node.server.identity;
83
110
  node.server.registerOnIdentityChanged((identity) => {
84
111
  externalTaskWorker.identity = identity;
85
- });
86
- await externalTaskWorker.start();
112
+ });
87
113
 
88
- node.on("close", async () => {
114
+ // export type WorkerErrorHandler = (errorType: 'fetchAndLock' | 'extendLock' | 'processExternalTask' | 'finishExternalTask', error: Error, externalTask?: ExternalTask<any>) => void;
115
+ externalTaskWorker.onWorkerError((errorType, error, externalTask) => {
116
+ switch (errorType) {
117
+ case 'extendLock':
118
+ case 'finishExternalTask':
119
+ case 'processExternalTask':
120
+ node.error(
121
+ `Worker error ${errorType} for *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}': ${error.message}`,
122
+ );
123
+
124
+ if (externalTask) {
125
+ delete started_external_tasks[externalTask.flowNodeInstanceId];
126
+ }
127
+
128
+ showStatus(node, Object.keys(started_external_tasks).length);
129
+ break;
130
+ default:
131
+ node.error(`Worker error ${errorType}: ${error.message}`);
132
+ break;
133
+ }
134
+ });
135
+
136
+ try {
137
+ externalTaskWorker.start();
138
+ } catch (error) {
139
+ node.error(`Worker start 'externalTaskWorker.start' failed: ${error.message}`);
140
+ }
141
+
142
+ node.on('close', () => {
89
143
  try {
90
144
  externalTaskWorker.stop();
91
145
  } catch {
92
- console.warn('Client close failed');
146
+ node.error('Client close failed');
93
147
  }
94
148
  });
95
- }
96
- );
149
+ })
150
+ .catch((error) => {
151
+ node.error(`Error in subscribeToExternalTaskTopic: ${error.message}`);
152
+ });
97
153
  }
98
- RED.nodes.registerType("externaltask-input", ExternalTaskInput);
99
- }
154
+
155
+ RED.nodes.registerType('externaltask-input', ExternalTaskInput);
156
+ };
@@ -1,26 +1,36 @@
1
1
  <script type="text/javascript">
2
- RED.nodes.registerType('externaltask-output',{
2
+ RED.nodes.registerType('externaltask-output', {
3
3
  category: 'ProcessCube',
4
4
  color: '#02AFD6',
5
5
  defaults: {
6
- name: {value:""}
6
+ name: { value: '' },
7
7
  },
8
8
  inputs: 1,
9
9
  outputs: 0,
10
- icon: "font-awesome/fa-envelope",
11
- label: function() {
12
- return this.name||"externaltask-output";
13
- }
10
+ icon: 'font-awesome/fa-envelope',
11
+ label: function () {
12
+ return this.name || 'externaltask-output';
13
+ },
14
14
  });
15
15
  </script>
16
16
 
17
17
  <script type="text/html" data-template-name="externaltask-output">
18
18
  <div class="form-row">
19
19
  <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
20
- <input type="text" id="node-input-name" placeholder="Name">
20
+ <input type="text" id="node-input-name" placeholder="Name" />
21
21
  </div>
22
22
  </script>
23
23
 
24
- <script type="text/html" data-help-name="externaltask-output">
25
- <p>A node which response to an External Task of https://processcube.io</p>
26
- </script>
24
+ <script type="text/markdown" data-help-name="externaltask-output">
25
+ Used to complete the external task. The `msg.payload` is returned to the ProcessCube
26
+ engine as the result of the external task.
27
+
28
+ ## Inputs
29
+
30
+ : payload (Object) : Returned to the ProcessCube engine as the result of the external task
31
+
32
+ ### References
33
+
34
+ - [The ProcessCube Developer Network](https://processcube.io) - All documentation for the ProcessCube&copy; platform
35
+ - [Node-RED Integration in ProcessCube&copy;](https://processcube.io/docs/node-red) - Node-RED integration in ProcessCube&copy;
36
+ </script>
@@ -1,21 +1,20 @@
1
- module.exports = function(RED) {
1
+ module.exports = function (RED) {
2
2
  function ExternalTaskOutput(config) {
3
- RED.nodes.createNode(this,config);
3
+ RED.nodes.createNode(this, config);
4
4
  var node = this;
5
5
 
6
6
  var flowContext = node.context().flow;
7
7
  var eventEmitter = flowContext.get('emitter');
8
8
 
9
- node.on('input', function(msg) {
10
-
11
- const externalTaskId = msg.externalTaskId;
9
+ node.on('input', function (msg) {
10
+ const flowNodeInstanceId = msg.flowNodeInstanceId;
12
11
 
13
- if (!externalTaskId) {
14
- node.error('Error: The message did not contain the required external task id.', msg);
15
- }
12
+ if (!flowNodeInstanceId) {
13
+ node.error('Error: The message did not contain the required external task id.', msg);
14
+ }
16
15
 
17
- eventEmitter.emit(`finish-${externalTaskId}`, msg.payload);
18
- });
16
+ eventEmitter.emit(`handle-${flowNodeInstanceId}`, msg, false);
17
+ });
19
18
  }
20
- RED.nodes.registerType("externaltask-output", ExternalTaskOutput);
21
- }
19
+ RED.nodes.registerType('externaltask-output', ExternalTaskOutput);
20
+ };
@@ -1,41 +1,41 @@
1
1
  <script type="text/javascript">
2
- RED.nodes.registerType('message-event-trigger',{
2
+ RED.nodes.registerType('message-event-trigger', {
3
3
  category: 'ProcessCube',
4
4
  color: '#02AFD6',
5
5
  defaults: {
6
- name: {value: ""},
7
- engine: {value: "", type: "processcube-engine-config"},
8
- messagename: {value: "", required: true},
9
- processInstanceId: {value:""}
6
+ name: { value: '' },
7
+ engine: { value: '', type: 'processcube-engine-config' },
8
+ messagename: { value: '', required: true },
9
+ processInstanceId: { value: '' },
10
10
  },
11
11
  inputs: 1,
12
12
  outputs: 1,
13
- icon: "font-awesome/fa-envelope-open",
14
- label: function() {
15
- return this.name||"message-event-trigger";
16
- }
13
+ icon: 'font-awesome/fa-envelope-open',
14
+ label: function () {
15
+ return this.name || 'message-event-trigger';
16
+ },
17
17
  });
18
18
  </script>
19
19
 
20
20
  <script type="text/html" data-template-name="message-event-trigger">
21
21
  <div class="form-row">
22
22
  <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
23
- <input type="text" id="node-input-name" placeholder="Name">
23
+ <input type="text" id="node-input-name" placeholder="Name" />
24
24
  </div>
25
25
  <div class="form-row">
26
26
  <label for="node-input-engine"><i class="fa fa-tag"></i> Engine</label>
27
- <input type="text" id="node-input-engine" placeholder="Engine">
27
+ <input type="text" id="node-input-engine" placeholder="Engine" />
28
28
  </div>
29
29
  <div class="form-row">
30
30
  <label for="node-input-messagename"><i class="fa fa-tag"></i> Message Name</label>
31
- <input type="text" id="node-input-messagename" placeholder="Name of the Message-Event">
31
+ <input type="text" id="node-input-messagename" placeholder="Name of the Message-Event" />
32
32
  </div>
33
33
  <div class="form-row">
34
34
  <label for="node-input-processinstanceid"><i class="fa fa-tag"></i> Process Instance Id</label>
35
- <input type="text" id="node-input-processinstanceid" placeholder="Id of the recipient process instance">
35
+ <input type="text" id="node-input-processinstanceid" placeholder="Id of the recipient process instance" />
36
36
  </div>
37
37
  </script>
38
38
 
39
39
  <script type="text/html" data-help-name="externaltask-input">
40
40
  <p>A node which emmits an message event to the Engine.</p>
41
- </script>
41
+ </script>
@@ -2,44 +2,36 @@ const process = require('process');
2
2
 
3
3
  const engine_client = require('@5minds/processcube_engine_client');
4
4
 
5
- module.exports = function(RED) {
5
+ module.exports = function (RED) {
6
6
  function MessageEventTrigger(config) {
7
7
  RED.nodes.createNode(this, config);
8
8
  var node = this;
9
- var nodeContext = node.context();
10
9
 
11
10
  this.engine = this.server = RED.nodes.getNode(config.engine);
12
11
 
13
- const engineUrl = this.engine?.url || process.env.ENGINE_URL || 'http://engine:8000';
14
-
15
- var client = nodeContext.get('client');
16
-
17
- if (!client) {
18
- nodeContext.set('client', new engine_client.EngineClient(engineUrl));
19
- client = nodeContext.get('client');
20
- }
21
-
22
- node.on('input', function(msg) {
23
-
24
- client.events.triggerMessageEvent(
25
- config.messagename,
26
- {
27
- processInstanceId: config.processinstanceid,
28
- payload: msg.payload,
29
- identity: node.server.identity
30
- }
31
-
32
- ).then((result) => {
33
-
34
- msg.payload = result;
35
-
36
- node.send(msg);
37
- node.status({fill: "blue", shape: "dot", text: `message event triggered`});
38
-
39
- }).catch((error) => {
40
- node.error(error);
41
- });
12
+ const client = this.engine.getEngineClient();
13
+
14
+ node.on('input', function (msg) {
15
+ client.events
16
+ .triggerMessageEvent(config.messagename, {
17
+ processInstanceId: config.processinstanceid,
18
+ payload: msg.payload,
19
+ identity: node.server.identity,
20
+ })
21
+ .then((result) => {
22
+ msg.payload = result;
23
+
24
+ node.send(msg);
25
+ node.status({
26
+ fill: 'blue',
27
+ shape: 'dot',
28
+ text: `message event triggered`,
29
+ });
30
+ })
31
+ .catch((error) => {
32
+ node.error(error);
33
+ });
42
34
  });
43
35
  }
44
- RED.nodes.registerType("message-event-trigger", MessageEventTrigger);
45
- }
36
+ RED.nodes.registerType('message-event-trigger', MessageEventTrigger);
37
+ };