@onify/flow-extensions 1.1.0 → 3.0.0

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/CHANGELOG.md CHANGED
@@ -1,7 +1,17 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- # 1.0.1
4
+ # 3.0.0
5
+
6
+ - Support activity script and expression execution listeners
7
+ - Remove default behaviour of saving activity output even if output is not declared. Output **was** saved to `environment.output._<Activity ID>` if no result variable or output was declared. The reason was that there was no easy way of getting output from SubProcess or a multi-instance task. Now SubProcess output can be defined and multi-instance output can be solved with execution listener
8
+
9
+ # 2.0.0
10
+
11
+ - fix resume extension when recovered mid formatting
12
+ - drop node 12 support
13
+
14
+ # 1.1.0
5
15
 
6
16
  - allow ISO8601 repeating interval and duration in timeCycle
7
17
 
package/dist/index.js CHANGED
@@ -5,23 +5,15 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.extendFn = extendFn;
7
7
  exports.extensions = extensions;
8
-
9
8
  var _cronParser = _interopRequireDefault(require("cron-parser"));
10
-
11
- var _IOProperties = _interopRequireDefault(require("./src/IOProperties"));
12
-
13
- var _IOForm = _interopRequireDefault(require("./src/IOForm"));
14
-
15
- var _Connector = _interopRequireDefault(require("./src/Connector"));
16
-
17
- var _ServiceExpression = _interopRequireDefault(require("./src/ServiceExpression"));
18
-
19
- var _IO = require("./src/IO");
20
-
9
+ var _IOProperties = _interopRequireDefault(require("./src/IOProperties.js"));
10
+ var _IOForm = _interopRequireDefault(require("./src/IOForm.js"));
11
+ var _Connector = _interopRequireDefault(require("./src/Connector.js"));
12
+ var _ServiceExpression = _interopRequireDefault(require("./src/ServiceExpression.js"));
13
+ var _IO = require("./src/IO.js");
14
+ var _ExecutionListeners = _interopRequireDefault(require("./src/ExecutionListeners.js"));
21
15
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22
-
23
16
  const iso8601cycle = /^\s*(R\d+\/)?P\w+/i;
24
-
25
17
  class FormatError extends Error {
26
18
  constructor(elementId, err) {
27
19
  super(`<${elementId}> ${err.message}`);
@@ -30,33 +22,28 @@ class FormatError extends Error {
30
22
  statusCode: 500
31
23
  };
32
24
  }
33
-
34
25
  }
35
-
36
26
  function extensions(element, context) {
37
27
  if (element.type === 'bpmn:Process') return new OnifyProcessExtensions(element, context);
38
28
  return new OnifyElementExtensions(element, context);
39
29
  }
40
-
41
30
  class OnifyProcessExtensions {
42
31
  constructor(bp, context) {
43
32
  this.process = bp;
44
33
  this.context = context;
45
34
  this.extensions = getExtensions(bp, context);
46
-
47
35
  this._activate();
48
36
  }
49
-
50
37
  _activate() {
51
38
  const bp = this.process;
52
39
  bp.on('process.enter', elementApi => {
53
40
  try {
54
41
  const result = this._onEnter(elementApi);
55
-
56
42
  const environment = elementApi.environment;
57
43
  environment.assignVariables(result);
58
44
  } catch (err) {
59
- elementApi.broker.publish('event', 'process.error', { ...elementApi.content,
45
+ elementApi.broker.publish('event', 'process.error', {
46
+ ...elementApi.content,
60
47
  error: new FormatError(bp.id, err)
61
48
  }, {
62
49
  mandatory: true,
@@ -67,7 +54,6 @@ class OnifyProcessExtensions {
67
54
  consumerTag: '_onify-extension-on-enter'
68
55
  });
69
56
  }
70
-
71
57
  _onEnter(elementApi) {
72
58
  const {
73
59
  format,
@@ -76,17 +62,13 @@ class OnifyProcessExtensions {
76
62
  const result = {};
77
63
  Object.assign(result, format.resolve(elementApi));
78
64
  Object.assign(elementApi.content, result);
79
-
80
65
  if (properties) {
81
66
  Object.assign(result, properties.resolve(elementApi));
82
67
  Object.assign(elementApi.content, result);
83
68
  }
84
-
85
69
  return result;
86
70
  }
87
-
88
71
  }
89
-
90
72
  class OnifyElementExtensions {
91
73
  constructor(activity, context) {
92
74
  this.activity = activity;
@@ -94,43 +76,28 @@ class OnifyElementExtensions {
94
76
  const {
95
77
  Service
96
78
  } = this.extensions = getExtensions(activity, context);
97
-
98
79
  if (Service) {
99
80
  activity.behaviour.Service = Service;
100
81
  }
101
82
  }
102
-
103
- activate() {
83
+ activate(message) {
104
84
  const activity = this.activity;
105
85
  const broker = activity.broker;
106
86
  const formatQ = activity.broker.getQueue('format-run-q');
107
- activity.on('enter', async elementApi => {
108
- formatQ.queueMessage({
109
- routingKey: 'run.enter.format'
110
- }, {
111
- endRoutingKey: 'run.enter.complete'
87
+ if (message.fields.redelivered && message.fields.routingKey === 'run.start') {
88
+ activity.on('start', elementApi => {
89
+ this._formatOnEnter(broker, formatQ, elementApi);
112
90
  }, {
113
- persistent: false
91
+ consumerTag: '_onify-extension-on-enter'
114
92
  });
115
-
116
- try {
117
- var format = await this._onEnter(elementApi);
118
- } catch (err) {
119
- return broker.publish('format', 'run.enter.error', {
120
- error: err
121
- }, {
122
- persistent: false
123
- });
124
- }
125
-
126
- broker.publish('format', 'run.enter.complete', format, {
127
- persistent: false
93
+ } else {
94
+ activity.on('enter', elementApi => {
95
+ this._formatOnEnter(broker, formatQ, elementApi);
96
+ }, {
97
+ consumerTag: '_onify-extension-on-enter'
128
98
  });
129
- }, {
130
- consumerTag: '_onify-extension-on-enter'
131
- });
99
+ }
132
100
  activity.on('activity.execution.completed', async elementApi => {
133
- if (elementApi.fields.redelivered) return;
134
101
  formatQ.queueMessage({
135
102
  routingKey: 'run.end.format'
136
103
  }, {
@@ -138,7 +105,6 @@ class OnifyElementExtensions {
138
105
  }, {
139
106
  persistent: false
140
107
  });
141
-
142
108
  try {
143
109
  var format = await this._onExecuted(elementApi);
144
110
  } catch (err) {
@@ -148,22 +114,98 @@ class OnifyElementExtensions {
148
114
  persistent: false
149
115
  });
150
116
  }
151
-
152
- broker.publish('format', 'run.end.complete', { ...format
117
+ broker.publish('format', 'run.end.complete', {
118
+ ...format
153
119
  }, {
154
120
  persistent: false
155
121
  });
156
122
  }, {
157
123
  consumerTag: '_onify-extension-on-executed'
158
124
  });
125
+ const executionListeners = this.extensions.listeners;
126
+ if (executionListeners !== null && executionListeners !== void 0 && executionListeners.onStart) {
127
+ activity.on('start', async elementApi => {
128
+ formatQ.queueMessage({
129
+ routingKey: 'run.listener.start'
130
+ }, {
131
+ endRoutingKey: 'run.listener.start.complete'
132
+ }, {
133
+ persistent: false
134
+ });
135
+ try {
136
+ var format = await executionListeners.execute('start', elementApi);
137
+ } catch (err) {
138
+ return broker.publish('format', 'run.listener.start.error', {
139
+ error: err
140
+ }, {
141
+ persistent: false
142
+ });
143
+ }
144
+ broker.publish('format', 'run.listener.start.complete', {
145
+ ...format
146
+ }, {
147
+ persistent: false
148
+ });
149
+ }, {
150
+ consumerTag: '_onify-extension-on-listenerstart'
151
+ });
152
+ }
153
+ if (executionListeners !== null && executionListeners !== void 0 && executionListeners.onEnd) {
154
+ activity.on('end', async elementApi => {
155
+ formatQ.queueMessage({
156
+ routingKey: 'run.listener.end'
157
+ }, {
158
+ endRoutingKey: 'run.listener.end.complete'
159
+ }, {
160
+ persistent: false
161
+ });
162
+ try {
163
+ var format = await executionListeners.execute('end', elementApi);
164
+ } catch (err) {
165
+ return broker.publish('format', 'run.listener.end.error', {
166
+ error: err
167
+ }, {
168
+ persistent: false
169
+ });
170
+ }
171
+ broker.publish('format', 'run.listener.end.complete', {
172
+ ...format
173
+ }, {
174
+ persistent: false
175
+ });
176
+ }, {
177
+ consumerTag: '_onify-extension-on-listenerend'
178
+ });
179
+ }
159
180
  }
160
-
161
181
  deactivate() {
162
182
  const broker = this.activity.broker;
163
183
  broker.cancel('_onify-extension-on-enter');
164
184
  broker.cancel('_onify-extension-on-executed');
185
+ broker.cancel('_onify-extension-on-listenerstart');
186
+ broker.cancel('_onify-extension-on-listenerend');
187
+ }
188
+ async _formatOnEnter(broker, formatQ, elementApi) {
189
+ formatQ.queueMessage({
190
+ routingKey: 'run.enter.format'
191
+ }, {
192
+ endRoutingKey: 'run.enter.complete'
193
+ }, {
194
+ persistent: false
195
+ });
196
+ try {
197
+ var format = await this._onEnter(elementApi);
198
+ } catch (err) {
199
+ return broker.publish('format', 'run.enter.error', {
200
+ error: err
201
+ }, {
202
+ persistent: false
203
+ });
204
+ }
205
+ broker.publish('format', 'run.enter.complete', format, {
206
+ persistent: false
207
+ });
165
208
  }
166
-
167
209
  async _onEnter(elementApi) {
168
210
  const {
169
211
  format,
@@ -174,17 +216,14 @@ class OnifyElementExtensions {
174
216
  const result = {};
175
217
  Object.assign(result, format.resolve(elementApi));
176
218
  Object.assign(elementApi.content, result);
177
-
178
219
  if (properties) {
179
220
  Object.assign(result, {
180
221
  properties: properties.resolve(elementApi)
181
222
  });
182
223
  Object.assign(elementApi.content, result);
183
224
  }
184
-
185
225
  if (io) {
186
226
  var _io$input, _io$output;
187
-
188
227
  if ((_io$input = io.input) !== null && _io$input !== void 0 && _io$input.length) {
189
228
  const input = await io.getInput(this.activity, elementApi);
190
229
  Object.assign(result, {
@@ -192,28 +231,23 @@ class OnifyElementExtensions {
192
231
  });
193
232
  Object.assign(elementApi.content, result);
194
233
  }
195
-
196
234
  if ((_io$output = io.output) !== null && _io$output !== void 0 && _io$output.length) {
197
235
  result.assignToOutput = true;
198
236
  }
199
237
  }
200
-
201
238
  if (form) {
202
239
  Object.assign(result, {
203
240
  form: form.resolve(elementApi)
204
241
  });
205
242
  }
206
-
207
243
  return result;
208
244
  }
209
-
210
245
  async _onExecuted(elementApi) {
211
246
  const {
212
247
  properties,
213
248
  io
214
249
  } = this.extensions;
215
250
  const result = {};
216
-
217
251
  if (io !== null && io !== void 0 && io.output.length) {
218
252
  const output = await io.getOutput(this.activity, elementApi);
219
253
  Object.assign(result, {
@@ -223,7 +257,6 @@ class OnifyElementExtensions {
223
257
  output
224
258
  });
225
259
  }
226
-
227
260
  if (properties) {
228
261
  const props = properties.resolve(elementApi);
229
262
  Object.assign(result, {
@@ -233,28 +266,22 @@ class OnifyElementExtensions {
233
266
  properties: props
234
267
  });
235
268
  }
236
-
237
269
  const {
238
270
  output,
239
271
  assignToOutput,
240
272
  resultVariable
241
273
  } = elementApi.content;
242
274
  if (output === undefined || output === null) return result;
243
-
244
275
  if (assignToOutput && typeof output === 'object') {
245
276
  Object.assign(this.activity.environment.output, output);
246
277
  } else if (resultVariable) {
247
278
  elementApi.environment.output[resultVariable] = output;
248
279
  }
249
-
250
280
  return result;
251
281
  }
252
-
253
282
  }
254
-
255
283
  function getExtensions(element, context) {
256
284
  var _element$behaviour$ex, _ext$values, _ext$fields;
257
-
258
285
  const result = {
259
286
  format: element.type === 'bpmn:Process' ? new FormatProcess(element) : new FormatActivity(element)
260
287
  };
@@ -262,21 +289,19 @@ function getExtensions(element, context) {
262
289
  if (expression) result.Service = _ServiceExpression.default;
263
290
  const extensions = (_element$behaviour$ex = element.behaviour.extensionElements) === null || _element$behaviour$ex === void 0 ? void 0 : _element$behaviour$ex.values;
264
291
  if (!extensions) return result;
265
-
292
+ const listeners = new _ExecutionListeners.default(element, context);
293
+ let listener = 0;
266
294
  for (const ext of extensions) {
267
295
  switch (ext.$type) {
268
296
  case 'camunda:Properties':
269
297
  if ((_ext$values = ext.values) !== null && _ext$values !== void 0 && _ext$values.length) result.properties = new _IOProperties.default(element, ext);
270
298
  break;
271
-
272
299
  case 'camunda:InputOutput':
273
300
  result.io = new _IO.InputOutput(element.id, ext, context);
274
301
  break;
275
-
276
302
  case 'camunda:FormData':
277
303
  if ((_ext$fields = ext.fields) !== null && _ext$fields !== void 0 && _ext$fields.length) result.form = new _IOForm.default(element, ext);
278
304
  break;
279
-
280
305
  case 'camunda:Connector':
281
306
  {
282
307
  const {
@@ -287,18 +312,19 @@ function getExtensions(element, context) {
287
312
  result.Service = _Connector.default.bind(_Connector.default, connectorId, io);
288
313
  break;
289
314
  }
315
+ case 'camunda:ExecutionListener':
316
+ listeners.add(ext, listener++);
317
+ break;
290
318
  }
291
319
  }
292
-
320
+ if (listeners.length) result.listeners = listeners;
293
321
  return result;
294
322
  }
295
-
296
323
  class FormatActivity {
297
324
  constructor(activity) {
298
325
  this.activity = activity;
299
- this.resultVariable = activity.behaviour.resultVariable || '_' + activity.id;
326
+ this.resultVariable = activity.behaviour.resultVariable;
300
327
  let timeCycles;
301
-
302
328
  if (activity.eventDefinitions) {
303
329
  for (const ed of activity.eventDefinitions.filter(e => e.type === 'bpmn:TimerEventDefinition')) {
304
330
  if (!('timeCycle' in ed)) continue;
@@ -306,13 +332,10 @@ class FormatActivity {
306
332
  timeCycles.push(ed.timeCycle);
307
333
  }
308
334
  }
309
-
310
335
  this.timeCycles = timeCycles;
311
336
  }
312
-
313
337
  resolve(elementApi) {
314
338
  var _documentation$, _user, _groups;
315
-
316
339
  let user, groups, assigneeValue, description;
317
340
  const activity = this.activity;
318
341
  const {
@@ -328,20 +351,18 @@ class FormatActivity {
328
351
  if (documentation) description = (_documentation$ = documentation[0]) === null || _documentation$ === void 0 ? void 0 : _documentation$.text;
329
352
  let expireAt;
330
353
  let timeCycles = this.timeCycles;
331
-
332
354
  if (timeCycles) {
333
355
  for (const cycle of timeCycles) {
334
356
  const cron = elementApi.resolveExpression(cycle);
335
357
  if (!cron || iso8601cycle.test(cron)) continue;
336
-
337
358
  const expireAtDt = _cronParser.default.parseExpression(cron).next().toDate();
338
-
339
359
  if (!expireAt || expireAtDt < expireAt) expireAt = expireAtDt;
340
360
  }
341
361
  }
342
-
343
362
  return {
344
- resultVariable: this.resultVariable,
363
+ ...(this.resultVariable && {
364
+ resultVariable: this.resultVariable
365
+ }),
345
366
  ...(scheduledStart && activity.parent.type === 'bpmn:Process' && {
346
367
  scheduledStart
347
368
  }),
@@ -362,17 +383,13 @@ class FormatActivity {
362
383
  })
363
384
  };
364
385
  }
365
-
366
386
  }
367
-
368
387
  class FormatProcess {
369
388
  constructor(bp) {
370
389
  this.process = bp;
371
390
  }
372
-
373
391
  resolve(elementApi) {
374
392
  var _documentation$2, _user2, _groups2;
375
-
376
393
  let user, groups, description;
377
394
  const bp = this.process;
378
395
  const {
@@ -384,7 +401,6 @@ class FormatProcess {
384
401
  if (candidateStarterGroups) groups = resolveAndSplit(elementApi, candidateStarterGroups);
385
402
  if (documentation) description = (_documentation$2 = documentation[0]) === null || _documentation$2 === void 0 ? void 0 : _documentation$2.text;
386
403
  return {
387
- resultVariable: this.resultVariable,
388
404
  ...(((_user2 = user) === null || _user2 === void 0 ? void 0 : _user2.length) && {
389
405
  candidateStarterUsers: user
390
406
  }),
@@ -396,9 +412,7 @@ class FormatProcess {
396
412
  })
397
413
  };
398
414
  }
399
-
400
415
  }
401
-
402
416
  function resolveAndSplit(elementApi, str) {
403
417
  if (Array.isArray(str)) return str.filter(Boolean);
404
418
  if (typeof str !== 'string') return;
@@ -407,10 +421,8 @@ function resolveAndSplit(elementApi, str) {
407
421
  if (typeof resolved !== 'string') return;
408
422
  return resolved.split(',').map(g => g.trim && g.trim().toLowerCase()).filter(Boolean);
409
423
  }
410
-
411
424
  function extendFn(behaviour, context) {
412
425
  var _behaviour$extensionE;
413
-
414
426
  if (behaviour.$type === 'bpmn:StartEvent' && behaviour.eventDefinitions) {
415
427
  const timer = behaviour.eventDefinitions.find(({
416
428
  type,
@@ -420,21 +432,28 @@ function extendFn(behaviour, context) {
420
432
  scheduledStart: timer.behaviour.timeCycle
421
433
  });
422
434
  }
423
-
424
435
  if (!Array.isArray((_behaviour$extensionE = behaviour.extensionElements) === null || _behaviour$extensionE === void 0 ? void 0 : _behaviour$extensionE.values)) return;
425
- const inputOutput = behaviour.extensionElements.values.find(el => el.$type === 'camunda:InputOutput');
426
- const connector = behaviour.extensionElements.values.find(el => el.$type === 'camunda:Connector');
427
- if (inputOutput) registerIOScripts(behaviour.id, context, inputOutput.$type, inputOutput);
428
- if (connector) registerIOScripts(behaviour.id, context, connector.$type, connector.inputOutput);
436
+ let listener = 0;
437
+ for (const extension of behaviour.extensionElements.values) {
438
+ switch (extension.$type) {
439
+ case 'camunda:InputOutput':
440
+ registerIOScripts(behaviour.id, context, extension.$type, extension);
441
+ break;
442
+ case 'camunda:Connector':
443
+ registerIOScripts(behaviour.id, context, extension.$type, extension.inputOutput);
444
+ break;
445
+ case 'camunda:ExecutionListener':
446
+ registerListenerScript(behaviour.id, context, extension.$type, extension, listener++);
447
+ break;
448
+ }
449
+ }
429
450
  }
430
-
431
451
  function registerIOScripts(parentId, context, type, ioBehaviour) {
432
452
  if (!ioBehaviour) return;
433
453
  const {
434
454
  inputParameters = [],
435
455
  outputParameters = []
436
456
  } = ioBehaviour;
437
-
438
457
  for (const {
439
458
  $type,
440
459
  name,
@@ -454,4 +473,22 @@ function registerIOScripts(parentId, context, type, ioBehaviour) {
454
473
  })
455
474
  });
456
475
  }
476
+ }
477
+ function registerListenerScript(parentId, context, type, listener, pos) {
478
+ const {
479
+ event,
480
+ script
481
+ } = listener;
482
+ if (!script) return;
483
+ const id = `${parentId}/${type}/${event}/${pos}`;
484
+ context.addScript(id, {
485
+ id,
486
+ scriptFormat: script.scriptFormat,
487
+ ...(script.value && {
488
+ body: script.value
489
+ }),
490
+ ...(script.resource && {
491
+ resource: script.resource
492
+ })
493
+ });
457
494
  }
@@ -4,9 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = Connector;
7
-
8
7
  var _Errors = require("./Errors");
9
-
10
8
  function Connector(connectorId, io, activity, executionMessage) {
11
9
  if (!(this instanceof Connector)) return new Connector(connectorId, io, activity, executionMessage);
12
10
  this.connectorId = connectorId;
@@ -14,7 +12,6 @@ function Connector(connectorId, io, activity, executionMessage) {
14
12
  this.activity = activity;
15
13
  this.executionMessage = executionMessage;
16
14
  }
17
-
18
15
  Connector.prototype.execute = async function execute(...args) {
19
16
  const callback = args.pop();
20
17
  args.push(formatCallback);
@@ -25,20 +22,18 @@ Connector.prototype.execute = async function execute(...args) {
25
22
  const executionMessage = this.executionMessage;
26
23
  const serviceFunction = environment.services[connectorId];
27
24
  if (!serviceFunction) return callback(new _Errors.NotImplemented(connectorId));
28
-
29
25
  try {
30
26
  const input = io && (await io.getInput(activity, executionMessage));
31
27
  await serviceFunction.call(activity, input, ...args);
32
28
  } catch (err) {
33
29
  return callback(err);
34
30
  }
35
-
36
31
  async function formatCallback(serviceErr, result) {
37
32
  if (serviceErr) return callback(serviceErr);
38
33
  if (!(io !== null && io !== void 0 && io.output.length)) return callback(null, result);
39
-
40
34
  try {
41
- const formattedResult = await io.getOutput(activity, { ...executionMessage,
35
+ const formattedResult = await io.getOutput(activity, {
36
+ ...executionMessage,
42
37
  ...result
43
38
  });
44
39
  return callback(null, formattedResult);
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.NotImplemented = void 0;
7
-
8
7
  class NotImplemented extends Error {
9
8
  constructor(serviceId) {
10
9
  super(`${serviceId} service function not found`);
@@ -13,7 +12,5 @@ class NotImplemented extends Error {
13
12
  statusCode: 501
14
13
  };
15
14
  }
16
-
17
15
  }
18
-
19
16
  exports.NotImplemented = NotImplemented;