@5minds/node-red-contrib-processcube 1.2.3 → 1.3.0-develop-cc3fd3-m24lvddh

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,187 @@
1
+ [
2
+ {
3
+ "id": "d6c450fe98faa45f",
4
+ "type": "tab",
5
+ "label": "Wait for usertask",
6
+ "disabled": false,
7
+ "info": "",
8
+ "env": []
9
+ },
10
+ {
11
+ "id": "30b8f9c606a2bb83",
12
+ "type": "wait-for-usertask",
13
+ "z": "d6c450fe98faa45f",
14
+ "name": "",
15
+ "engine": "42e6796dddd9d4db",
16
+ "query": "payload",
17
+ "query_type": "msg",
18
+ "only_for_new": true,
19
+ "x": 590,
20
+ "y": 120,
21
+ "wires": [
22
+ [
23
+ "d9cb8e69e985d6df",
24
+ "a5c0ea907e64e759"
25
+ ]
26
+ ]
27
+ },
28
+ {
29
+ "id": "20fd37adf0fb6f39",
30
+ "type": "process-start",
31
+ "z": "d6c450fe98faa45f",
32
+ "name": "",
33
+ "engine": "42e6796dddd9d4db",
34
+ "processmodel": "Wait-For-Usertask_Process",
35
+ "startevent": "StartEvent_1",
36
+ "x": 250,
37
+ "y": 120,
38
+ "wires": [
39
+ [
40
+ "1e1e9c428172eae2",
41
+ "c65a4ba5461e7ee0"
42
+ ]
43
+ ]
44
+ },
45
+ {
46
+ "id": "e18451d69a169f5f",
47
+ "type": "inject",
48
+ "z": "d6c450fe98faa45f",
49
+ "name": "",
50
+ "props": [
51
+ {
52
+ "p": "payload"
53
+ }
54
+ ],
55
+ "repeat": "",
56
+ "crontab": "",
57
+ "once": false,
58
+ "onceDelay": 0.1,
59
+ "topic": "",
60
+ "payload": "{}",
61
+ "payloadType": "json",
62
+ "x": 90,
63
+ "y": 120,
64
+ "wires": [
65
+ [
66
+ "20fd37adf0fb6f39"
67
+ ]
68
+ ]
69
+ },
70
+ {
71
+ "id": "d9cb8e69e985d6df",
72
+ "type": "delay",
73
+ "z": "d6c450fe98faa45f",
74
+ "name": "",
75
+ "pauseType": "delay",
76
+ "timeout": "5",
77
+ "timeoutUnits": "seconds",
78
+ "rate": "1",
79
+ "nbRateUnits": "1",
80
+ "rateUnits": "second",
81
+ "randomFirst": "1",
82
+ "randomLast": "5",
83
+ "randomUnits": "seconds",
84
+ "drop": false,
85
+ "allowrate": false,
86
+ "outputs": 1,
87
+ "x": 540,
88
+ "y": 200,
89
+ "wires": [
90
+ [
91
+ "acecfeef70b5c4ad"
92
+ ]
93
+ ]
94
+ },
95
+ {
96
+ "id": "44f4c6c64263d366",
97
+ "type": "usertask-output",
98
+ "z": "d6c450fe98faa45f",
99
+ "name": "",
100
+ "engine": "42e6796dddd9d4db",
101
+ "result": "payload",
102
+ "result_type": "msg",
103
+ "x": 880,
104
+ "y": 200,
105
+ "wires": [
106
+ []
107
+ ]
108
+ },
109
+ {
110
+ "id": "acecfeef70b5c4ad",
111
+ "type": "function",
112
+ "z": "d6c450fe98faa45f",
113
+ "name": "pre",
114
+ "func": "msg.payload = {};\nreturn msg;",
115
+ "outputs": 1,
116
+ "timeout": 0,
117
+ "noerr": 0,
118
+ "initialize": "",
119
+ "finalize": "",
120
+ "libs": [],
121
+ "x": 710,
122
+ "y": 200,
123
+ "wires": [
124
+ [
125
+ "44f4c6c64263d366"
126
+ ]
127
+ ]
128
+ },
129
+ {
130
+ "id": "1e1e9c428172eae2",
131
+ "type": "function",
132
+ "z": "d6c450fe98faa45f",
133
+ "name": "query",
134
+ "func": "msg.payload = {\n \"processInstanceId\": msg.payload.processInstanceId,\n \"flowNodeId\": \"make_it\"\n};\n\nreturn msg;",
135
+ "outputs": 1,
136
+ "timeout": 0,
137
+ "noerr": 0,
138
+ "initialize": "",
139
+ "finalize": "",
140
+ "libs": [],
141
+ "x": 410,
142
+ "y": 120,
143
+ "wires": [
144
+ [
145
+ "30b8f9c606a2bb83"
146
+ ]
147
+ ]
148
+ },
149
+ {
150
+ "id": "c65a4ba5461e7ee0",
151
+ "type": "debug",
152
+ "z": "d6c450fe98faa45f",
153
+ "name": "debug 32",
154
+ "active": true,
155
+ "tosidebar": true,
156
+ "console": false,
157
+ "tostatus": false,
158
+ "complete": "false",
159
+ "statusVal": "",
160
+ "statusType": "auto",
161
+ "x": 370,
162
+ "y": 60,
163
+ "wires": []
164
+ },
165
+ {
166
+ "id": "a5c0ea907e64e759",
167
+ "type": "debug",
168
+ "z": "d6c450fe98faa45f",
169
+ "name": "debug 33",
170
+ "active": true,
171
+ "tosidebar": true,
172
+ "console": false,
173
+ "tostatus": false,
174
+ "complete": "false",
175
+ "statusVal": "",
176
+ "statusType": "auto",
177
+ "x": 760,
178
+ "y": 60,
179
+ "wires": []
180
+ },
181
+ {
182
+ "id": "42e6796dddd9d4db",
183
+ "type": "processcube-engine-config",
184
+ "name": "Engine 8000",
185
+ "url": "http://engine:8000"
186
+ }
187
+ ]
@@ -6,6 +6,7 @@
6
6
  name: { value: '' },
7
7
  engine: { value: '', type: 'processcube-engine-config' },
8
8
  topic: { value: '' },
9
+ workerConfig: { value: '', type: 'json' },
9
10
  },
10
11
  inputs: 0,
11
12
  outputs: 1,
@@ -13,6 +14,15 @@
13
14
  label: function () {
14
15
  return this.name || 'externaltask-input';
15
16
  },
17
+ oneditprepare: function () {
18
+ $('#node-input-workerConfig').typedInput({
19
+ default: 'json',
20
+ types: ['json'],
21
+ });
22
+ },
23
+ oneditsave: function () {
24
+ this.workerConfig = $('#node-input-workerConfig').typedInput('value');
25
+ },
16
26
  });
17
27
  </script>
18
28
 
@@ -29,6 +39,10 @@
29
39
  <label for="node-input-topic"><i class="fa fa-tag"></i> Topic</label>
30
40
  <input type="text" id="node-input-topic" placeholder="Topic of ExternalTask" />
31
41
  </div>
42
+ <div class="form-row"></div>
43
+ <label for="node-input-workerConfig"><i class="fa fa-tag"></i> Worker Config</label>
44
+ <input type="text" id="node-input-workerConfig" />
45
+ </div>
32
46
  </script>
33
47
 
34
48
  <script type="text/markdown" data-help-name="externaltask-input">
@@ -16,7 +16,7 @@ module.exports = function (RED) {
16
16
  var node = this;
17
17
  var flowContext = node.context().flow;
18
18
 
19
- const engine = RED.nodes.getNode(config.engine);
19
+ const engine = RED.nodes.getNode(config.engine);
20
20
 
21
21
  const client = engine.engineClient;
22
22
 
@@ -32,83 +32,85 @@ module.exports = function (RED) {
32
32
  eventEmitter = flowContext.get('emitter');
33
33
  }
34
34
 
35
- client.externalTasks
36
- .subscribeToExternalTaskTopic(config.topic, async (payload, externalTask) => {
37
- const saveHandleCallback = (data, callback) => {
38
- try {
39
- callback(data);
40
- } catch (error) {
41
- node.error(`Error in callback 'saveHandleCallback': ${error.message}`);
42
- }
43
- };
35
+ const etwCallback = async (payload, externalTask) => {
36
+ const saveHandleCallback = (data, callback) => {
37
+ try {
38
+ callback(data);
39
+ } catch (error) {
40
+ node.error(`Error in callback 'saveHandleCallback': ${error.message}`);
41
+ }
42
+ };
44
43
 
45
- return await new Promise((resolve, reject) => {
46
- const handleFinishTask = (msg) => {
47
- let result = RED.util.encodeObject(msg.payload);
44
+ return await new Promise((resolve, reject) => {
45
+ const handleFinishTask = (msg) => {
46
+ let result = RED.util.encodeObject(msg.payload);
48
47
 
49
- node.log(
50
- `handle finish task *flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* ${externalTask.processInstanceId} with result ${result} on msg._msgid ${msg._msgid}.`,
51
- );
48
+ node.log(
49
+ `handle event for *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* ${externalTask.processInstanceId} with result ${result} on msg._msgid ${msg._msgid}.`
50
+ );
52
51
 
53
- if (externalTask.flowNodeInstanceId) {
54
- delete started_external_tasks[externalTask.flowNodeInstanceId];
55
- }
52
+ if (externalTask.flowNodeInstanceId) {
53
+ delete started_external_tasks[externalTask.flowNodeInstanceId];
54
+ }
56
55
 
57
- showStatus(node, Object.keys(started_external_tasks).length);
56
+ showStatus(node, Object.keys(started_external_tasks).length);
58
57
 
59
- //resolve(result);
60
- saveHandleCallback(result, resolve);
61
- };
58
+ //resolve(result);
59
+ saveHandleCallback(result, resolve);
60
+ };
61
+
62
+ const handleErrorTask = (msg) => {
63
+ node.log(
64
+ `handle error event for *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}' on *msg._msgid* '${msg._msgid}'.`
65
+ );
62
66
 
63
- const handleErrorTask = (msg) => {
64
- node.log(
65
- `handle error task *flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}' on *msg._msgid* '${msg._msgid}'.`,
66
- );
67
+ if (externalTask.flowNodeInstanceId) {
68
+ delete started_external_tasks[externalTask.flowNodeInstanceId];
69
+ }
67
70
 
68
- if (externalTask.flowNodeInstanceId) {
69
- delete started_external_tasks[externalTask.flowNodeInstanceId];
70
- }
71
+ showStatus(node, Object.keys(started_external_tasks).length);
71
72
 
72
- showStatus(node, Object.keys(started_external_tasks).length);
73
+ // TODO: with reject, the default error handling is proceed
74
+ // SEE: https://github.com/5minds/ProcessCube.Engine.Client.ts/blob/develop/src/ExternalTaskWorker.ts#L180
75
+ // reject(result);
76
+ //resolve(msg);
77
+ saveHandleCallback(msg, resolve);
78
+ };
73
79
 
74
- // TODO: with reject, the default error handling is proceed
75
- // SEE: https://github.com/5minds/ProcessCube.Engine.Client.ts/blob/develop/src/ExternalTaskWorker.ts#L180
76
- // reject(result);
77
- //resolve(msg);
78
- saveHandleCallback(msg, resolve);
79
- };
80
+ eventEmitter.once(`handle-${externalTask.flowNodeInstanceId}`, (msg, isError = false) => {
81
+ node.log(
82
+ `handle event for *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}' with *msg._msgid* '${msg._msgid}' and *isError* '${isError}'`
83
+ );
80
84
 
81
- eventEmitter.once(`handle-${externalTask.flowNodeInstanceId}`, (msg, isError = false) => {
82
- node.log(
83
- `handle-${externalTask.flowNodeInstanceId}: *flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}' with *msg._msgid* '${msg._msgid}' and *isError* '${isError}'`,
84
- );
85
+ if (isError) {
86
+ handleErrorTask(msg);
87
+ } else {
88
+ handleFinishTask(msg);
89
+ }
90
+ });
85
91
 
86
- if (isError) {
87
- handleErrorTask(msg);
88
- } else {
89
- handleFinishTask(msg);
90
- }
91
- });
92
+ started_external_tasks[externalTask.flowNodeInstanceId] = externalTask;
92
93
 
93
- started_external_tasks[externalTask.flowNodeInstanceId] = externalTask;
94
+ showStatus(node, Object.keys(started_external_tasks).length);
94
95
 
95
- showStatus(node, Object.keys(started_external_tasks).length);
96
+ let msg = {
97
+ _msgid: RED.util.generateId(),
98
+ task: RED.util.encodeObject(externalTask),
99
+ payload: payload,
100
+ flowNodeInstanceId: externalTask.flowNodeInstanceId,
101
+ processInstanceId: externalTask.processInstanceId
102
+ };
96
103
 
97
- let msg = {
98
- _msgid: RED.util.generateId(),
99
- task: RED.util.encodeObject(externalTask),
100
- payload: payload,
101
- flowNodeInstanceId: externalTask.flowNodeInstanceId,
102
- processInstanceId: externalTask.processInstanceId
103
- };
104
+ node.log(
105
+ `Received *external task flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}' with *msg._msgid* '${msg._msgid}'`
106
+ );
104
107
 
105
- node.log(
106
- `new task *flowNodeInstanceId* '${externalTask.flowNodeInstanceId}' and *processInstanceId* '${externalTask.processInstanceId}' with *msg._msgid* '${msg._msgid}'`,
107
- );
108
+ node.send(msg);
109
+ });
110
+ };
108
111
 
109
- node.send(msg);
110
- });
111
- })
112
+ client.externalTasks
113
+ .subscribeToExternalTaskTopic(config.topic, etwCallback, config.workerConfig)
112
114
  .then(async (externalTaskWorker) => {
113
115
  node.status({ fill: 'blue', shape: 'ring', text: 'subcribed' });
114
116
 
@@ -63,6 +63,14 @@
63
63
  "info": "",
64
64
  "env": []
65
65
  },
66
+ {
67
+ "id": "d6c450fe98faa45f",
68
+ "type": "tab",
69
+ "label": "Wait for usertask",
70
+ "disabled": false,
71
+ "info": "",
72
+ "env": []
73
+ },
66
74
  {
67
75
  "id": "4c59118134081e05",
68
76
  "type": "group",
@@ -1111,8 +1119,6 @@
1111
1119
  "engine": "42e6796dddd9d4db",
1112
1120
  "query": "payload",
1113
1121
  "query_type": "msg",
1114
- "force_send_array": false,
1115
- "multisend": false,
1116
1122
  "x": 180,
1117
1123
  "y": 420,
1118
1124
  "wires": [
@@ -1245,8 +1251,6 @@
1245
1251
  "engine": "b78932d162d8d468",
1246
1252
  "query": "payload",
1247
1253
  "query_type": "msg",
1248
- "force_send_array": false,
1249
- "multisend": false,
1250
1254
  "x": 200,
1251
1255
  "y": 440,
1252
1256
  "wires": [
@@ -1821,5 +1825,176 @@
1821
1825
  "x": 110,
1822
1826
  "y": 260,
1823
1827
  "wires": []
1828
+ },
1829
+ {
1830
+ "id": "30b8f9c606a2bb83",
1831
+ "type": "wait-for-usertask",
1832
+ "z": "d6c450fe98faa45f",
1833
+ "name": "",
1834
+ "engine": "42e6796dddd9d4db",
1835
+ "query": "payload",
1836
+ "query_type": "msg",
1837
+ "only_for_new": true,
1838
+ "x": 590,
1839
+ "y": 120,
1840
+ "wires": [
1841
+ [
1842
+ "d9cb8e69e985d6df",
1843
+ "a5c0ea907e64e759"
1844
+ ]
1845
+ ]
1846
+ },
1847
+ {
1848
+ "id": "20fd37adf0fb6f39",
1849
+ "type": "process-start",
1850
+ "z": "d6c450fe98faa45f",
1851
+ "name": "",
1852
+ "engine": "42e6796dddd9d4db",
1853
+ "processmodel": "Wait-For-Usertask_Process",
1854
+ "startevent": "StartEvent_1",
1855
+ "x": 250,
1856
+ "y": 120,
1857
+ "wires": [
1858
+ [
1859
+ "1e1e9c428172eae2",
1860
+ "c65a4ba5461e7ee0"
1861
+ ]
1862
+ ]
1863
+ },
1864
+ {
1865
+ "id": "e18451d69a169f5f",
1866
+ "type": "inject",
1867
+ "z": "d6c450fe98faa45f",
1868
+ "name": "",
1869
+ "props": [
1870
+ {
1871
+ "p": "payload"
1872
+ }
1873
+ ],
1874
+ "repeat": "",
1875
+ "crontab": "",
1876
+ "once": false,
1877
+ "onceDelay": 0.1,
1878
+ "topic": "",
1879
+ "payload": "{}",
1880
+ "payloadType": "json",
1881
+ "x": 90,
1882
+ "y": 120,
1883
+ "wires": [
1884
+ [
1885
+ "20fd37adf0fb6f39"
1886
+ ]
1887
+ ]
1888
+ },
1889
+ {
1890
+ "id": "d9cb8e69e985d6df",
1891
+ "type": "delay",
1892
+ "z": "d6c450fe98faa45f",
1893
+ "name": "",
1894
+ "pauseType": "delay",
1895
+ "timeout": "5",
1896
+ "timeoutUnits": "seconds",
1897
+ "rate": "1",
1898
+ "nbRateUnits": "1",
1899
+ "rateUnits": "second",
1900
+ "randomFirst": "1",
1901
+ "randomLast": "5",
1902
+ "randomUnits": "seconds",
1903
+ "drop": false,
1904
+ "allowrate": false,
1905
+ "outputs": 1,
1906
+ "x": 540,
1907
+ "y": 200,
1908
+ "wires": [
1909
+ [
1910
+ "acecfeef70b5c4ad"
1911
+ ]
1912
+ ]
1913
+ },
1914
+ {
1915
+ "id": "44f4c6c64263d366",
1916
+ "type": "usertask-output",
1917
+ "z": "d6c450fe98faa45f",
1918
+ "name": "",
1919
+ "engine": "42e6796dddd9d4db",
1920
+ "result": "payload",
1921
+ "result_type": "msg",
1922
+ "x": 880,
1923
+ "y": 200,
1924
+ "wires": [
1925
+ []
1926
+ ]
1927
+ },
1928
+ {
1929
+ "id": "acecfeef70b5c4ad",
1930
+ "type": "function",
1931
+ "z": "d6c450fe98faa45f",
1932
+ "name": "pre",
1933
+ "func": "msg.payload = {};\nreturn msg;",
1934
+ "outputs": 1,
1935
+ "timeout": 0,
1936
+ "noerr": 0,
1937
+ "initialize": "",
1938
+ "finalize": "",
1939
+ "libs": [],
1940
+ "x": 710,
1941
+ "y": 200,
1942
+ "wires": [
1943
+ [
1944
+ "44f4c6c64263d366"
1945
+ ]
1946
+ ]
1947
+ },
1948
+ {
1949
+ "id": "1e1e9c428172eae2",
1950
+ "type": "function",
1951
+ "z": "d6c450fe98faa45f",
1952
+ "name": "query",
1953
+ "func": "msg.payload = {\n \"processInstanceId\": msg.payload.processInstanceId,\n \"flowNodeId\": \"make_it\"\n};\n\nreturn msg;",
1954
+ "outputs": 1,
1955
+ "timeout": 0,
1956
+ "noerr": 0,
1957
+ "initialize": "",
1958
+ "finalize": "",
1959
+ "libs": [],
1960
+ "x": 410,
1961
+ "y": 120,
1962
+ "wires": [
1963
+ [
1964
+ "30b8f9c606a2bb83"
1965
+ ]
1966
+ ]
1967
+ },
1968
+ {
1969
+ "id": "c65a4ba5461e7ee0",
1970
+ "type": "debug",
1971
+ "z": "d6c450fe98faa45f",
1972
+ "name": "debug 32",
1973
+ "active": true,
1974
+ "tosidebar": true,
1975
+ "console": false,
1976
+ "tostatus": false,
1977
+ "complete": "false",
1978
+ "statusVal": "",
1979
+ "statusType": "auto",
1980
+ "x": 370,
1981
+ "y": 60,
1982
+ "wires": []
1983
+ },
1984
+ {
1985
+ "id": "a5c0ea907e64e759",
1986
+ "type": "debug",
1987
+ "z": "d6c450fe98faa45f",
1988
+ "name": "debug 33",
1989
+ "active": true,
1990
+ "tosidebar": true,
1991
+ "console": false,
1992
+ "tostatus": false,
1993
+ "complete": "false",
1994
+ "statusVal": "",
1995
+ "statusType": "auto",
1996
+ "x": 760,
1997
+ "y": 60,
1998
+ "wires": []
1824
1999
  }
1825
2000
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@5minds/node-red-contrib-processcube",
3
- "version": "1.2.3",
3
+ "version": "1.3.0-develop-cc3fd3-m24lvddh",
4
4
  "license": "MIT",
5
5
  "description": "Node-RED nodes for ProcessCube",
6
6
  "scripts": {
@@ -52,7 +52,8 @@
52
52
  "UserTaskNewListener": "usertask-new-listener.js",
53
53
  "UserTaskFinishedListener": "usertask-finished-listener.js",
54
54
  "UserTaskInput": "usertask-input.js",
55
- "UserTaskOutput": "usertask-output.js"
55
+ "UserTaskOutput": "usertask-output.js",
56
+ "WaitForUsertask": "wait-for-usertask.js"
56
57
  },
57
58
  "examples": "examples"
58
59
  },
@@ -2,16 +2,40 @@
2
2
  RED.nodes.registerType('processcube-engine-config', {
3
3
  category: 'config',
4
4
  defaults: {
5
- name : { value: '' },
5
+ name: { value: '' },
6
6
  url: { value: 'http://engine:8000', required: true },
7
+ clientId: { value: '' },
8
+ clientIdType: { type: 'str' },
9
+ clientSecret: { value: '' },
10
+ clientSecretType: { type: 'str' },
7
11
  },
8
12
  label: function () {
9
13
  return this.name || this.url;
10
14
  },
11
- credentials: {
12
- clientId: { type: 'text' },
13
- clientSecret: { type: 'password' },
14
- }
15
+ oneditprepare: function () {
16
+ $('#node-config-input-clientId').typedInput({
17
+ default: 'str',
18
+ types: ['str', 'global', 'flow', 'env', 'msg', 'cred'],
19
+ });
20
+
21
+ $('#node-config-input-clientSecret').typedInput({
22
+ default: 'str',
23
+ types: ['str', 'global', 'flow', 'env', 'msg', 'cred'],
24
+ });
25
+
26
+ $('#node-config-input-clientId').typedInput('value', this.clientId);
27
+ $('#node-config-input-clientId').typedInput('type', this.clientIdType);
28
+
29
+ $('#node-config-input-clientSecret').typedInput('value', this.clientSecret);
30
+ $('#node-config-input-clientSecret').typedInput('type', this.clientSecretType);
31
+ },
32
+ oneditsave: function () {
33
+ this.clientId = $('#node-config-input-clientId').typedInput('value');
34
+ this.clientIdType = $('#node-config-input-clientId').typedInput('type');
35
+
36
+ this.clientSecret = $('#node-config-input-clientSecret').typedInput('value');
37
+ this.clientSecretType = $('#node-config-input-clientSecret').typedInput('type');
38
+ },
15
39
  });
16
40
  </script>
17
41
 
@@ -11,18 +11,21 @@ module.exports = function (RED) {
11
11
  const identityChangedCallbacks = [];
12
12
  this.url = n.url;
13
13
  this.identity = null;
14
-
14
+
15
+ this.credentials.clientId = RED.util.evaluateNodeProperty(n.clientId, n.clientIdType, node);
16
+ this.credentials.clientSecret = RED.util.evaluateNodeProperty(n.clientSecret, n.clientSecretType, node);
17
+
15
18
  this.registerOnIdentityChanged = function (callback) {
16
19
  identityChangedCallbacks.push(callback);
17
20
  };
18
21
 
19
- this.isIdentityReady = function() {
22
+ this.isIdentityReady = function () {
20
23
  if (this.credentials.clientId && this.credentials.clientSecret) {
21
24
  return this.identity != null;
22
25
  } else {
23
26
  return true;
24
27
  }
25
- }
28
+ };
26
29
 
27
30
  this.setIdentity = (identity) => {
28
31
  node.log(`setIdentity: ${JSON.stringify(identity)}`);
@@ -50,7 +53,7 @@ module.exports = function (RED) {
50
53
  this.credentials.clientId,
51
54
  this.credentials.clientSecret,
52
55
  authorityUrl,
53
- node,
56
+ node
54
57
  ).catch((reason) => {
55
58
  console.error(reason);
56
59
  node.error(reason);
@@ -136,7 +139,7 @@ async function startRefreshingIdentityCycle(clientId, clientSecret, authorityUrl
136
139
  if (retries === 0) {
137
140
  console.error(
138
141
  'Could not refresh identity for external task worker processes. Stopping all external task workers.',
139
- { error },
142
+ { error }
140
143
  );
141
144
  return;
142
145
  }
@@ -16,23 +16,26 @@
16
16
  <bpmn:userTask id="user_task" name="User Task">
17
17
  <bpmn:extensionElements>
18
18
  <camunda:formData>
19
- <camunda:formField id="checkbox" label="Checkox" type="checkbox" defaultValue="true" customForm="{&#34;hint&#34;:&#34;hintttt&#34;,&#34;entries&#34;:[{&#34;key&#34;:&#34;value&#34;,&#34;value&#34;:&#34;Label&#34;}]}" />
20
- <camunda:formField id="color" label="Color" type="color" defaultValue="#cd2323" />
21
- <camunda:formField id="date-time-local" label="Datetime Local" type="datetime-local" customForm="{&#34;hint&#34;:&#34;jljkkljlk&#34;}" />
22
- <camunda:formField id="email" label="Email" type="email" customForm="{&#34;hint&#34;:&#34;hklojh&#34;,&#34;placeholder&#34;:&#34;luis@luis.de&#34;}" />
23
- <camunda:formField id="header" type="header" defaultValue="This is a Heading" customForm="{&#34;style&#34;:&#34;heading_2&#34;}" />
24
- <camunda:formField id="hidden" type="hidden" defaultValue="jkljlkj" />
25
- <camunda:formField id="month" label="Month" type="month" customForm="{&#34;hint&#34;:&#34;this is a hint for month&#34;}" />
26
- <camunda:formField id="paragraph" type="paragraph" defaultValue="This is a paragraph." />
27
- <camunda:formField id="password" label="Password" type="password" customForm="{&#34;hint&#34;:&#34;Dont use smt i can guess&#34;}" />
28
- <camunda:formField id="radio" label="Radio" type="radio" customForm="{&#34;entries&#34;:[{&#34;key&#34;:&#34;value&#34;,&#34;value&#34;:&#34;Label&#34;},{&#34;key&#34;:&#34;value2&#34;,&#34;value&#34;:&#34;Label2&#34;}]}" />
29
- <camunda:formField id="range" label="Range" type="range" customForm="{&#34;step&#34;:1,&#34;min&#34;:100,&#34;max&#34;:1000}" />
30
- <camunda:formField id="select" label="Select" type="select" customForm="{&#34;entries&#34;:[{&#34;key&#34;:&#34;value&#34;,&#34;value&#34;:&#34;Label&#34;},{&#34;key&#34;:&#34;value&#34;,&#34;value&#34;:&#34;Label&#34;}],&#34;placeholder&#34;:&#34;Select Something&#34;}" />
31
- <camunda:formField id="tele" label="Tel." type="tel" customForm="{&#34;hint&#34;:&#34;jkl&#34;,&#34;placeholder&#34;:&#34;110&#34;}" />
32
- <camunda:formField id="textarea" label="Textarea" type="textarea" defaultValue="jkl" customForm="{&#34;placeholder&#34;:&#34;placeholder&#34;}" />
33
- <camunda:formField id="time" label="Time" type="time" customForm="{&#34;hint&#34;:&#34;jkl&#34;,&#34;placeholder&#34;:&#34;12:0000&#34;}" />
34
- <camunda:formField id="url" label="URL" type="url" customForm="{&#34;placeholder&#34;:&#34;http://&#34;,&#34;hint&#34;:&#34;no youtube&#34;}" />
35
- <camunda:formField id="week" label="Week" type="week" customForm="{&#34;hint&#34;:&#34;What week?&#34;}" />
19
+ <camunda:formField id="checkbox" label="Checkbox" type="checkbox" customForm="{&#34;entries&#34;:[{&#34;key&#34;:&#34;key&#34;,&#34;value&#34;:&#34;value&#34;}],&#34;hint&#34;:&#34;hint&#34;}" />
20
+ <camunda:formField id="colorpicker" label="Colorpicker" type="color" defaultValue="#e32626" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
21
+ <camunda:formField id="date" label="Date" type="date" defaultValue="2024-10-18" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
22
+ <camunda:formField id="datetime" label="Datetime" type="datetime-local" defaultValue="2024-10-19T12:41" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
23
+ <camunda:formField id="email" label="Email" type="email" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
24
+ <camunda:formField id="header" type="header" defaultValue="Header" />
25
+ <camunda:formField id="hidden" type="hidden" defaultValue="hidden" />
26
+ <camunda:formField id="number" label="Number" type="number" customForm="{&#34;step&#34;:1.5,&#34;hint&#34;:&#34;hint&#34;}" />
27
+ <camunda:formField id="month" label="Month" type="month" defaultValue="2024-07" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
28
+ <camunda:formField id="paragraph" type="paragraph" defaultValue="A lot of words..." />
29
+ <camunda:formField id="password" label="Pasword" type="password" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
30
+ <camunda:formField id="radio" label="Radio" type="radio" customForm="{&#34;entries&#34;:[{&#34;key&#34;:&#34;key&#34;,&#34;value&#34;:&#34;value&#34;}],&#34;hint&#34;:&#34;hint&#34;}" />
31
+ <camunda:formField id="range" label="Range" type="range" defaultValue="30" customForm="{&#34;step&#34;:0.4,&#34;min&#34;:3,&#34;hint&#34;:&#34;hint&#34;}" />
32
+ <camunda:formField id="select" label="Select" type="select" customForm="{&#34;entries&#34;:[{&#34;key&#34;:&#34;key&#34;,&#34;value&#34;:&#34;value&#34;}],&#34;hint&#34;:&#34;hint&#34;}" />
33
+ <camunda:formField id="tel" label="Tel." type="tel" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
34
+ <camunda:formField id="text" label="Text" type="string" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
35
+ <camunda:formField id="textarea" label="Textarea" type="textarea" defaultValue="more words..." customForm="{&#34;rows&#34;:14,&#34;hint&#34;:&#34;hint&#34;}" />
36
+ <camunda:formField id="time" label="Time" type="time" defaultValue="11:54" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
37
+ <camunda:formField id="url" label="URL" type="url" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
38
+ <camunda:formField id="week" label="Week" type="week" defaultValue="2024-W37" customForm="{&#34;hint&#34;:&#34;hint&#34;}" />
36
39
  </camunda:formData>
37
40
  </bpmn:extensionElements>
38
41
  <bpmn:incoming>Flow_142awo6</bpmn:incoming>
@@ -0,0 +1,74 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Wait-For-Usertask_Definition" targetNamespace="http://bpmn.io/schema/bpmn" exporter="5Minds Studio" exporterVersion="1">
3
+ <bpmn:collaboration id="Collaboration_1cidyxu" name="">
4
+ <bpmn:participant id="Participant_0px403d" name="Wait-For-Usertask" processRef="Wait-For-Usertask_Process" />
5
+ </bpmn:collaboration>
6
+ <bpmn:process id="Wait-For-Usertask_Process" name="Wait-For-Usertask" isExecutable="true">
7
+ <bpmn:laneSet>
8
+ <bpmn:lane id="Lane_1xzf0d3" name="Lane">
9
+ <bpmn:flowNodeRef>StartEvent_1</bpmn:flowNodeRef>
10
+ <bpmn:flowNodeRef>Event_0zjvsuj</bpmn:flowNodeRef>
11
+ <bpmn:flowNodeRef>make_it</bpmn:flowNodeRef>
12
+ <bpmn:flowNodeRef>Event_02k45ju</bpmn:flowNodeRef>
13
+ </bpmn:lane>
14
+ </bpmn:laneSet>
15
+ <bpmn:startEvent id="StartEvent_1" name="Start">
16
+ <bpmn:outgoing>Flow_085u77p</bpmn:outgoing>
17
+ </bpmn:startEvent>
18
+ <bpmn:sequenceFlow id="Flow_085u77p" sourceRef="StartEvent_1" targetRef="Event_02k45ju" />
19
+ <bpmn:sequenceFlow id="Flow_0rvexxn" sourceRef="Event_02k45ju" targetRef="make_it" />
20
+ <bpmn:endEvent id="Event_0zjvsuj">
21
+ <bpmn:incoming>Flow_10jnerm</bpmn:incoming>
22
+ </bpmn:endEvent>
23
+ <bpmn:sequenceFlow id="Flow_10jnerm" sourceRef="make_it" targetRef="Event_0zjvsuj" />
24
+ <bpmn:userTask id="make_it" name="Make it">
25
+ <bpmn:incoming>Flow_0rvexxn</bpmn:incoming>
26
+ <bpmn:outgoing>Flow_10jnerm</bpmn:outgoing>
27
+ </bpmn:userTask>
28
+ <bpmn:intermediateCatchEvent id="Event_02k45ju">
29
+ <bpmn:incoming>Flow_085u77p</bpmn:incoming>
30
+ <bpmn:outgoing>Flow_0rvexxn</bpmn:outgoing>
31
+ <bpmn:timerEventDefinition id="TimerEventDefinition_157z719">
32
+ <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT5S</bpmn:timeDuration>
33
+ </bpmn:timerEventDefinition>
34
+ </bpmn:intermediateCatchEvent>
35
+ </bpmn:process>
36
+ <bpmndi:BPMNDiagram id="BPMNDiagram_1">
37
+ <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_1cidyxu">
38
+ <bpmndi:BPMNShape id="Participant_0px403d_di" bpmnElement="Participant_0px403d" isHorizontal="true">
39
+ <dc:Bounds x="5" y="4" width="535" height="346" />
40
+ </bpmndi:BPMNShape>
41
+ <bpmndi:BPMNShape id="Lane_1xzf0d3_di" bpmnElement="Lane_1xzf0d3" isHorizontal="true">
42
+ <dc:Bounds x="35" y="4" width="505" height="346" />
43
+ </bpmndi:BPMNShape>
44
+ <bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
45
+ <dc:Bounds x="92" y="152" width="36" height="36" />
46
+ <bpmndi:BPMNLabel>
47
+ <dc:Bounds x="98" y="195" width="24" height="14" />
48
+ </bpmndi:BPMNLabel>
49
+ </bpmndi:BPMNShape>
50
+ <bpmndi:BPMNShape id="Event_0zjvsuj_di" bpmnElement="Event_0zjvsuj">
51
+ <dc:Bounds x="442" y="152" width="36" height="36" />
52
+ </bpmndi:BPMNShape>
53
+ <bpmndi:BPMNShape id="Activity_0yse9qf_di" bpmnElement="make_it">
54
+ <dc:Bounds x="280" y="130" width="100" height="80" />
55
+ <bpmndi:BPMNLabel />
56
+ </bpmndi:BPMNShape>
57
+ <bpmndi:BPMNShape id="Event_09boky1_di" bpmnElement="Event_02k45ju">
58
+ <dc:Bounds x="182" y="152" width="36" height="36" />
59
+ </bpmndi:BPMNShape>
60
+ <bpmndi:BPMNEdge id="Flow_085u77p_di" bpmnElement="Flow_085u77p">
61
+ <di:waypoint x="128" y="170" />
62
+ <di:waypoint x="182" y="170" />
63
+ </bpmndi:BPMNEdge>
64
+ <bpmndi:BPMNEdge id="Flow_0rvexxn_di" bpmnElement="Flow_0rvexxn">
65
+ <di:waypoint x="218" y="170" />
66
+ <di:waypoint x="280" y="170" />
67
+ </bpmndi:BPMNEdge>
68
+ <bpmndi:BPMNEdge id="Flow_10jnerm_di" bpmnElement="Flow_10jnerm">
69
+ <di:waypoint x="380" y="170" />
70
+ <di:waypoint x="442" y="170" />
71
+ </bpmndi:BPMNEdge>
72
+ </bpmndi:BPMNPlane>
73
+ </bpmndi:BPMNDiagram>
74
+ </bpmn:definitions>
@@ -0,0 +1,77 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('wait-for-usertask',{
3
+ category: 'ProcessCube',
4
+ color: '#02AFD6',
5
+ defaults: {
6
+ name: {value: ""},
7
+ engine: {value: "", type: "processcube-engine-config"},
8
+ query: {value: "payload"},
9
+ query_type: {value: "msg"},
10
+ only_for_new: {value: false}
11
+ },
12
+ inputs: 1,
13
+ outputs: 1,
14
+ icon: "font-awesome/fa-envelope-open",
15
+ label: function() {
16
+ return this.name || "wait-for-usertask";
17
+ },
18
+ oneditprepare: function() {
19
+ $("#node-input-query").typedInput({
20
+ default: 'msg',
21
+ types: ['msg', 'json']
22
+ });
23
+
24
+ $("#node-input-query").typedInput('value', this.query);
25
+ $("#node-input-query").typedInput('type', this.query_type);
26
+ },
27
+ oneditsave: function() {
28
+ this.query = $("#node-input-query").typedInput('value'),
29
+ this.query_type = $("#node-input-query").typedInput('type')
30
+
31
+ }
32
+ });
33
+ </script>
34
+
35
+ <script type="text/html" data-template-name="wait-for-usertask">
36
+ <div class="form-row">
37
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
38
+ <input type="text" id="node-input-name" placeholder="Name">
39
+ </div>
40
+ <div class="form-row">
41
+ <label for="node-input-engine"><i class="fa fa-tag"></i> Engine-URL</label>
42
+ <input type="text" id="node-input-engine" placeholder="http://engine:8000">
43
+ </div>
44
+ <div class="form-row">
45
+ <label for="node-input-query"><i class="fa fa-tag"></i> Query</label>
46
+ <input type="text" id="node-input-query">
47
+ </div>
48
+ <div class="form-row" style="display:flex; margin-bottom: 3px;">
49
+ <label for="node-input-only_for_new" style="vertical-align:top"><i class="fa fa-list-alt"></i> Only for new</label>
50
+ <div>
51
+ <input type="checkbox" checked id="node-input-only_for_new" style="display: inline-block; width: auto; margin: 0px 0px 0px 4px;">
52
+ <label style="width:auto" for="node-input-multisend">Trigger only for new user task or *checked* also for old ones?</label>
53
+ </div>
54
+ </div>
55
+ </script>
56
+
57
+ <script type="text/markdown" data-help-name="wait-for-usertask">
58
+ Waiting for Usertasks of the connected ProcessCube Engine.
59
+
60
+ ### Inputs
61
+
62
+ : payload (json) : the query to filter the usertasks.
63
+
64
+ ## Outputs
65
+
66
+ : payload.userTask (string) : The filtered UserTask
67
+
68
+ ### Details
69
+
70
+ - `msg.payload` or a constant json to filter the UserTasks for Waiting for
71
+ - `Only for new` will trigger only for new UserTasks if checked and also for old ones if not checked.
72
+
73
+ ### References
74
+
75
+ - [The ProcessCube&copy; Developer Network](https://processcube.io) - All documentation for the ProcessCube&copy; platform
76
+ - [ProcessCube&copy; LowCode Integration](https://processcube.io/docs/node-red) - LowCode integration in ProcessCube&copy;
77
+ </script>
@@ -0,0 +1,92 @@
1
+ module.exports = function (RED) {
2
+ function WaitForUsertask(config) {
3
+ RED.nodes.createNode(this, config);
4
+ var node = this;
5
+
6
+ node.engine = RED.nodes.getNode(config.engine);
7
+
8
+ const client = node.engine.engineClient;
9
+
10
+ let subscription = null;
11
+ let currentIdentity = node.engine.identity;
12
+ let subscribe = null;
13
+
14
+ node.on('input', async function (msg) {
15
+ subscribe = async () => {
16
+ if (!client) {
17
+ node.error('No engine configured.');
18
+ return;
19
+ }
20
+
21
+ const query = RED.util.evaluateNodeProperty(config.query, config.query_type, node, msg);
22
+
23
+ subscription = await client.userTasks.onUserTaskWaiting(async (userTaskWaitingNotification) => {
24
+
25
+ const newQuery = {
26
+ 'flowNodeInstanceId': userTaskWaitingNotification.flowNodeInstanceId,
27
+ ...query
28
+ };
29
+
30
+ const matchingFlowNodes = await client.userTasks.query(newQuery, { identity: currentIdentity });
31
+
32
+ if (matchingFlowNodes.userTasks && matchingFlowNodes.userTasks.length == 1) {
33
+ // remove subscription
34
+ client.userTasks.removeSubscription(subscription, currentIdentity);
35
+
36
+ const userTask = matchingFlowNodes.userTasks[0];
37
+
38
+ msg.payload = { userTask: userTask };
39
+ node.send(msg);
40
+ } else {
41
+ // nothing todo - wait for next notification
42
+ }
43
+
44
+ }, { identity: currentIdentity });
45
+
46
+ node.log({"Handling old userTasks config.only_for_new": config.only_for_new});
47
+
48
+ if (config.only_for_new === false) {
49
+ // only check suspended user tasks
50
+ const suspendedQuery = {
51
+ 'state': 'suspended',
52
+ ...query
53
+ };
54
+
55
+ const matchingFlowNodes = await client.userTasks.query(suspendedQuery, { identity: currentIdentity });
56
+
57
+ if (matchingFlowNodes.userTasks && matchingFlowNodes.userTasks.length >= 1) {
58
+ const userTask = matchingFlowNodes.userTasks[0];
59
+
60
+ msg.payload = { userTask: userTask };
61
+ node.send(msg);
62
+
63
+ // remove subscription
64
+ client.userTasks.removeSubscription(subscription, currentIdentity);
65
+ } else {
66
+ // let the *currentIdentity* be active
67
+ }
68
+ }
69
+ };
70
+
71
+ subscribe();
72
+ });
73
+
74
+ node.engine.registerOnIdentityChanged(async (identity) => {
75
+
76
+ if (subscription) {
77
+ client.userTasks.removeSubscription(subscription, currentIdentity);
78
+ currentIdentity = identity;
79
+ subscribe();
80
+ } else {
81
+ currentIdentity = identity;
82
+ }
83
+ });
84
+
85
+ node.on("close", async () => {
86
+ if (client != null && subscription != null) {
87
+ client.userTasks.removeSubscription(subscription, currentIdentity);
88
+ }
89
+ });
90
+ }
91
+ RED.nodes.registerType("wait-for-usertask", WaitForUsertask);
92
+ }