@5minds/node-red-contrib-processcube 0.14.0-fix-error-in-process-instance-query-9aeac9-lyzi0gix → 0.14.0

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