bpmn-elements 8.0.1 → 8.1.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,6 +1,10 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ # 8.1.0
5
+
6
+ - support non-interrupting BoundaryEvent with ISO8601 repeating interval timeCycle
7
+
4
8
  # 8.0.1
5
9
 
6
10
  ## Fix
@@ -543,6 +543,7 @@ proto._onInbound = function onInbound(routingKey, message) {
543
543
  switch (routingKey) {
544
544
  case 'association.take':
545
545
  case 'flow.take':
546
+ case 'activity.restart':
546
547
  case 'activity.enter':
547
548
  return this.run({
548
549
  message: content.message,
@@ -976,9 +977,9 @@ proto._doRunLeave = function doRunLeave(message, isDiscarded, onOutbound) {
976
977
  });
977
978
  }
978
979
 
979
- this.broker.publish('run', 'run.leave', (0, _messageHelper.cloneContent)(content, { ...(outbound.length ? {
980
+ this.broker.publish('run', 'run.leave', (0, _messageHelper.cloneContent)(content, { ...(outbound.length && {
980
981
  outbound
981
- } : undefined)
982
+ })
982
983
  }), {
983
984
  correlationId
984
985
  });
@@ -1030,9 +1031,9 @@ proto._doRunOutbound = function doRunOutbound(outboundList, content, discardSequ
1030
1031
  this.broker.publish('run', 'run.outbound.' + action, (0, _messageHelper.cloneContent)(content, {
1031
1032
  flow: { ...outboundFlow,
1032
1033
  sequenceId: (0, _shared.getUniqueId)(`${flowId}_${action}`),
1033
- ...(discardSequence ? {
1034
+ ...(discardSequence && {
1034
1035
  discardSequence: discardSequence.slice()
1035
- } : undefined)
1036
+ })
1036
1037
  }
1037
1038
  }));
1038
1039
  }
@@ -1130,15 +1131,15 @@ proto._createMessage = function createMessage(override) {
1130
1131
  const result = { ...override,
1131
1132
  id: this.id,
1132
1133
  type: this.type,
1133
- ...(name ? {
1134
+ ...(name && {
1134
1135
  name
1135
- } : undefined),
1136
- ...(status ? {
1136
+ }),
1137
+ ...(status && {
1137
1138
  status
1138
- } : undefined),
1139
- ...(parent ? {
1139
+ }),
1140
+ ...(parent && {
1140
1141
  parent: (0, _messageHelper.cloneParent)(parent)
1141
- } : undefined)
1142
+ })
1142
1143
  };
1143
1144
 
1144
1145
  for (const [flag, value] of Object.entries(this[kFlags])) {
@@ -1315,9 +1316,9 @@ OutboundEvaluator.prototype.completed = function completed(err) {
1315
1316
 
1316
1317
  for (const flow of Object.values(result)) {
1317
1318
  evaluationResult.push({ ...flow,
1318
- ...(message !== undefined ? {
1319
+ ...(message !== undefined && {
1319
1320
  message
1320
- } : undefined)
1321
+ })
1321
1322
  });
1322
1323
  }
1323
1324
 
@@ -1328,8 +1329,8 @@ function formatFlowAction(flow, options) {
1328
1329
  return { ...options,
1329
1330
  id: flow.id,
1330
1331
  action: options.action,
1331
- ...(flow.isDefault ? {
1332
+ ...(flow.isDefault && {
1332
1333
  isDefault: true
1333
- } : undefined)
1334
+ })
1334
1335
  };
1335
1336
  }
@@ -414,9 +414,9 @@ proto._debug = function debug(logMessage, executionId) {
414
414
  };
415
415
 
416
416
  function getExecuteMessage(message) {
417
- const result = (0, _messageHelper.cloneMessage)(message, { ...(message.fields.redelivered ? {
417
+ const result = (0, _messageHelper.cloneMessage)(message, { ...(message.fields.redelivered && {
418
418
  isRecovered: true
419
- } : undefined),
419
+ }),
420
420
  ignoreIfExecuting: undefined
421
421
  });
422
422
  return result;
@@ -30,9 +30,9 @@ function Message(messageDef, context) {
30
30
  id,
31
31
  type,
32
32
  messageType: 'message',
33
- ...(name ? {
33
+ ...(name && {
34
34
  name: environment.resolveExpression(name, executionMessage)
35
- } : undefined),
35
+ }),
36
36
  parent: { ...parent
37
37
  }
38
38
  };
@@ -30,9 +30,9 @@ function Signal(signalDef, context) {
30
30
  id,
31
31
  type,
32
32
  messageType: 'signal',
33
- ...(name ? {
33
+ ...(name && {
34
34
  name: environment.resolveExpression(name, executionMessage)
35
- } : undefined),
35
+ }),
36
36
  parent: { ...parent
37
37
  }
38
38
  };
@@ -12,6 +12,7 @@ var _iso8601Duration = require("iso8601-duration");
12
12
  const kStopped = Symbol.for('stopped');
13
13
  const kTimerContent = Symbol.for('timerContent');
14
14
  const kTimer = Symbol.for('timer');
15
+ const repeatPattern = /^\s*R(\d+)\//;
15
16
 
16
17
  function TimerEventDefinition(activity, eventDefinition) {
17
18
  const type = this.type = eventDefinition.type || 'TimerEventDefinition';
@@ -77,9 +78,9 @@ proto.execute = function execute(executeMessage) {
77
78
  const resolvedTimer = this._getTimers(executeMessage);
78
79
 
79
80
  const timerContent = this[kTimerContent] = (0, _messageHelper.cloneContent)(content, { ...resolvedTimer,
80
- ...(isResumed ? {
81
+ ...(isResumed && {
81
82
  isResumed
82
- } : undefined),
83
+ }),
83
84
  startedAt,
84
85
  state: 'timer'
85
86
  });
@@ -129,6 +130,14 @@ proto._completed = function completed(completeContent, options) {
129
130
  };
130
131
  const broker = this.broker;
131
132
  broker.publish('event', 'activity.timeout', (0, _messageHelper.cloneContent)(timerContent, content), options);
133
+
134
+ if (timerContent.repeat > 1) {
135
+ const repeat = timerContent.repeat - 1;
136
+ broker.publish('execution', 'execute.repeat', (0, _messageHelper.cloneContent)(timerContent, { ...content,
137
+ repeat
138
+ }), options);
139
+ }
140
+
132
141
  broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(timerContent, content), options);
133
142
  };
134
143
 
@@ -171,9 +180,9 @@ proto._onApiMessage = function onApiMessage(routingKey, message) {
171
180
 
172
181
  return this._completed({
173
182
  state: 'cancel',
174
- ...(message.content.message ? {
183
+ ...(message.content.message && {
175
184
  message: message.content.message
176
- } : undefined)
185
+ })
177
186
  }, {
178
187
  correlationId
179
188
  });
@@ -213,39 +222,48 @@ proto._stop = function stop() {
213
222
  proto._getTimers = function getTimers(executeMessage) {
214
223
  const content = executeMessage.content;
215
224
  const now = Date.now();
216
- const result = { ...('expireAt' in content ? {
225
+ const result = { ...('expireAt' in content && {
217
226
  expireAt: new Date(content.expireAt)
218
- } : undefined)
227
+ })
219
228
  };
220
229
 
221
230
  for (const t of ['timeDuration', 'timeDate', 'timeCycle']) {
222
231
  if (t in content) result[t] = content[t];else if (t in this) result[t] = this.environment.resolveExpression(this[t], executeMessage);else continue;
223
- let expireAtDate;
224
-
225
- if (t === 'timeDuration') {
226
- const durationStr = result[t];
227
-
228
- if (durationStr) {
229
- const delay = this._getDurationInMilliseconds(durationStr);
230
-
231
- if (delay !== undefined) expireAtDate = new Date(now + delay);
232
- } else {
233
- expireAtDate = new Date(now);
234
- }
235
- } else if (t === 'timeDate') {
236
- const dateStr = result[t];
237
-
238
- if (dateStr) {
239
- const ms = Date.parse(dateStr);
240
-
241
- if (!isNaN(ms)) {
242
- expireAtDate = new Date(ms);
243
- } else {
244
- this._warn(`invalid timeDate >${dateStr}<`);
245
- }
246
- } else {
247
- expireAtDate = new Date(now);
232
+ let expireAtDate, repeat;
233
+ const timerStr = result[t];
234
+
235
+ if (timerStr) {
236
+ switch (t) {
237
+ case 'timeCycle':
238
+ {
239
+ const mRepeat = timerStr.match(repeatPattern);
240
+ if (mRepeat && mRepeat.length) repeat = parseInt(mRepeat[1]);
241
+ }
242
+
243
+ case 'timeDuration':
244
+ {
245
+ const delay = this._getDurationInMilliseconds(timerStr);
246
+
247
+ if (delay !== undefined) expireAtDate = new Date(now + delay);
248
+ break;
249
+ }
250
+
251
+ case 'timeDate':
252
+ {
253
+ const dateStr = result[t];
254
+ const ms = Date.parse(dateStr);
255
+
256
+ if (!isNaN(ms)) {
257
+ expireAtDate = new Date(ms);
258
+ } else {
259
+ this._warn(`invalid timeDate >${dateStr}<`);
260
+ }
261
+
262
+ break;
263
+ }
248
264
  }
265
+ } else {
266
+ expireAtDate = new Date(now);
249
267
  }
250
268
 
251
269
  if (!expireAtDate) continue;
@@ -253,6 +271,7 @@ proto._getTimers = function getTimers(executeMessage) {
253
271
  if (!('expireAt' in result) || result.expireAt > expireAtDate) {
254
272
  result.timerType = t;
255
273
  result.expireAt = expireAtDate;
274
+ if (repeat) result.repeat = repeat;
256
275
  }
257
276
  }
258
277
 
@@ -264,6 +283,10 @@ proto._getTimers = function getTimers(executeMessage) {
264
283
  result.timeout = 0;
265
284
  }
266
285
 
286
+ if (content.inbound && 'repeat' in content.inbound[0]) {
287
+ result.repeat = content.inbound[0].repeat;
288
+ }
289
+
267
290
  return result;
268
291
  };
269
292
 
@@ -271,7 +294,7 @@ proto._getDurationInMilliseconds = function getDurationInMilliseconds(duration)
271
294
  try {
272
295
  return (0, _iso8601Duration.toSeconds)((0, _iso8601Duration.parse)(duration)) * 1000;
273
296
  } catch (err) {
274
- this._warn(`failed to parse timeDuration >${duration}<: ${err.message}`);
297
+ this._warn(`failed to parse ${this.timerType} >${duration}<: ${err.message}`);
275
298
  }
276
299
  };
277
300
 
@@ -66,14 +66,6 @@ proto.execute = function execute(executeMessage) {
66
66
  if (isRootScope) {
67
67
  this[kExecuteMessage] = executeMessage;
68
68
  const broker = this.broker;
69
-
70
- if (eventDefinitionExecution && !this.environment.settings.strict) {
71
- broker.subscribeTmp('execution', 'execute.expect', this._onExpectMessage.bind(this), {
72
- noAck: true,
73
- consumerTag: '_expect-tag'
74
- });
75
- }
76
-
77
69
  const consumerTag = `_bound-listener-${executionId}`;
78
70
  this.attachedTo.broker.subscribeTmp('event', 'activity.leave', this._onAttachedLeave.bind(this), {
79
71
  noAck: true,
@@ -81,14 +73,23 @@ proto.execute = function execute(executeMessage) {
81
73
  priority: 300
82
74
  });
83
75
  this[kAttachedTags].push(consumerTag);
84
- broker.subscribeOnce('execution', 'execute.detach', this._onDetachMessage.bind(this), {
85
- consumerTag: '_detach-tag'
86
- });
87
76
  broker.subscribeOnce('api', `activity.#.${executionId}`, this._onApiMessage.bind(this), {
88
77
  consumerTag: `_api-${executionId}`
89
78
  });
90
- broker.subscribeOnce('execution', 'execute.bound.completed', this._onCompleted.bind(this), {
91
- consumerTag: `_execution-completed-${executionId}`
79
+ const execQ = broker.assertQueue(`_bound-execution-${executionId}`, {
80
+ durable: false,
81
+ autoDelete: true
82
+ });
83
+ broker.bindQueue(execQ.name, 'execution', 'execute.detach');
84
+ broker.bindQueue(execQ.name, 'execution', 'execute.bound.completed');
85
+ broker.bindQueue(execQ.name, 'execution', 'execute.repeat');
86
+
87
+ if (eventDefinitionExecution && !this.environment.settings.strict) {
88
+ broker.bindQueue(execQ.name, 'execution', 'execute.expect');
89
+ }
90
+
91
+ execQ.consume(this._onExecutionMessage.bind(this), {
92
+ consumerTag: '_execution-tag'
92
93
  });
93
94
  }
94
95
 
@@ -97,19 +98,37 @@ proto.execute = function execute(executeMessage) {
97
98
  }
98
99
  };
99
100
 
101
+ proto._onExecutionMessage = function onExecutionMessage(routingKey, message) {
102
+ message.ack();
103
+
104
+ switch (routingKey) {
105
+ case 'execute.detach':
106
+ return this._onDetachMessage(routingKey, message);
107
+
108
+ case 'execute.bound.completed':
109
+ return this._onCompleted(routingKey, message);
110
+
111
+ case 'execute.repeat':
112
+ return this._onRepeatMessage(routingKey, message);
113
+
114
+ case 'execute.expect':
115
+ return this._onExpectMessage(routingKey, message);
116
+ }
117
+ };
118
+
100
119
  proto._onCompleted = function onCompleted(_, {
101
120
  content
102
121
  }) {
103
122
  if (!this.cancelActivity && !content.cancelActivity) {
104
123
  this._stop();
105
124
 
106
- return this.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(content));
125
+ return this.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(content, {
126
+ cancelActivity: false
127
+ }));
107
128
  }
108
129
 
109
130
  this[kCompleteContent] = content;
110
- const {
111
- inbound
112
- } = this[kExecuteMessage].content;
131
+ const inbound = this[kExecuteMessage].content.inbound;
113
132
  const attachedToContent = inbound && inbound[0];
114
133
  const attachedTo = this.attachedTo;
115
134
  this.activity.logger.debug(`<${this.executionId} (${this.id})> cancel ${attachedTo.status} activity <${attachedToContent.executionId} (${attachedToContent.id})>`);
@@ -199,6 +218,17 @@ proto._onApiMessage = function onApiMessage(_, message) {
199
218
  }
200
219
  };
201
220
 
221
+ proto._onRepeatMessage = function onRepeatMessage(_, message) {
222
+ if (this.cancelActivity) return;
223
+ const executeMessage = this[kExecuteMessage];
224
+ const repeat = message.content.repeat;
225
+ this.broker.getQueue('inbound-q').queueMessage({
226
+ routingKey: 'activity.restart'
227
+ }, (0, _messageHelper.cloneContent)(executeMessage.content.inbound[0], {
228
+ repeat
229
+ }));
230
+ };
231
+
202
232
  proto._stop = function stop(detach) {
203
233
  const attachedTo = this.attachedTo,
204
234
  broker = this.broker,
@@ -208,8 +238,7 @@ proto._stop = function stop(detach) {
208
238
 
209
239
  for (const shovelName of this[kShovels].splice(0)) attachedTo.broker.closeShovel(shovelName);
210
240
 
211
- broker.cancel('_expect-tag');
212
- broker.cancel('_detach-tag');
241
+ broker.cancel('_execution-tag');
213
242
  broker.cancel(`_execution-completed-${executionId}`);
214
243
  if (detach) return;
215
244
  broker.cancel(`_api-${executionId}`);
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "bpmn-elements",
3
- "version": "8.0.1",
3
+ "version": "8.1.0",
4
4
  "description": "Executable workflow elements based on BPMN 2.0",
5
5
  "main": "dist/index.js",
6
6
  "module": "index.js",
7
+ "sideEffects": false,
7
8
  "scripts": {
8
9
  "test": "mocha -R dot",
9
10
  "posttest": "npm run dist && eslint . --cache",
@@ -11,9 +12,7 @@
11
12
  "cov:html": "nyc mocha -R dot && nyc report --reporter=html",
12
13
  "test:lcov": "nyc mocha -R dot && nyc report --reporter lcov && npm run posttest",
13
14
  "dist": "babel index.js -d dist && babel src -d dist/src",
14
- "predist": "node -e \"fs.rmdirSync('./dist/src', {recursive: true});\"",
15
- "test-md": "node scripts/test-markdown ./API.md && node scripts/test-markdown ./docs/Examples.md",
16
- "toc": "node scripts/generate-api-toc ./API.md,./docs/Examples.md,./docs/Form.md"
15
+ "predist": "node -e \"fs.rmdirSync('./dist/src', {recursive: true});\""
17
16
  },
18
17
  "repository": {
19
18
  "type": "git",
@@ -47,10 +46,10 @@
47
46
  ],
48
47
  "devDependencies": {
49
48
  "@aircall/expression-parser": "^1.0.4",
50
- "@babel/cli": "^7.17.10",
51
- "@babel/core": "^7.18.5",
52
- "@babel/preset-env": "^7.18.2",
53
- "@babel/register": "^7.17.7",
49
+ "@babel/cli": "^7.18.6",
50
+ "@babel/core": "^7.18.6",
51
+ "@babel/preset-env": "^7.18.6",
52
+ "@babel/register": "^7.18.6",
54
53
  "bpmn-moddle": "^7.1.2",
55
54
  "camunda-bpmn-moddle": "^6.1.2",
56
55
  "chai": "^4.3.6",
@@ -61,7 +60,7 @@
61
60
  "mocha": "^9.2.2",
62
61
  "mocha-cakes-2": "^3.3.0",
63
62
  "moddle-context-serializer": "^2.1.0",
64
- "nock": "^13.2.6",
63
+ "nock": "^13.2.8",
65
64
  "nyc": "^15.1.0"
66
65
  },
67
66
  "dependencies": {
@@ -452,6 +452,7 @@ proto._onInbound = function onInbound(routingKey, message) {
452
452
  switch (routingKey) {
453
453
  case 'association.take':
454
454
  case 'flow.take':
455
+ case 'activity.restart':
455
456
  case 'activity.enter':
456
457
  return this.run({
457
458
  message: content.message,
@@ -788,7 +789,7 @@ proto._doRunLeave = function doRunLeave(message, isDiscarded, onOutbound) {
788
789
  }
789
790
 
790
791
  this.broker.publish('run', 'run.leave', cloneContent(content, {
791
- ...(outbound.length ? {outbound} : undefined),
792
+ ...(outbound.length && {outbound}),
792
793
  }), {correlationId});
793
794
 
794
795
  onOutbound();
@@ -832,7 +833,7 @@ proto._doRunOutbound = function doRunOutbound(outboundList, content, discardSequ
832
833
  flow: {
833
834
  ...outboundFlow,
834
835
  sequenceId: getUniqueId(`${flowId}_${action}`),
835
- ...(discardSequence ? {discardSequence: discardSequence.slice()} : undefined),
836
+ ...(discardSequence && {discardSequence: discardSequence.slice()}),
836
837
  },
837
838
  }));
838
839
  }
@@ -919,9 +920,9 @@ proto._createMessage = function createMessage(override) {
919
920
  ...override,
920
921
  id: this.id,
921
922
  type: this.type,
922
- ...(name ? {name} : undefined),
923
- ...(status ? {status} : undefined),
924
- ...(parent ? {parent: cloneParent(parent)} : undefined),
923
+ ...(name && {name}),
924
+ ...(status && {status}),
925
+ ...(parent && {parent: cloneParent(parent)}),
925
926
  };
926
927
 
927
928
  for (const [flag, value] of Object.entries(this[kFlags])) {
@@ -1070,7 +1071,7 @@ OutboundEvaluator.prototype.completed = function completed(err) {
1070
1071
  for (const flow of Object.values(result)) {
1071
1072
  evaluationResult.push({
1072
1073
  ...flow,
1073
- ...(message !== undefined ? {message} : undefined),
1074
+ ...(message !== undefined && {message}),
1074
1075
  });
1075
1076
  }
1076
1077
 
@@ -1082,6 +1083,6 @@ function formatFlowAction(flow, options) {
1082
1083
  ...options,
1083
1084
  id: flow.id,
1084
1085
  action: options.action,
1085
- ...(flow.isDefault ? {isDefault: true} : undefined),
1086
+ ...(flow.isDefault && {isDefault: true}),
1086
1087
  };
1087
1088
  }
@@ -345,7 +345,7 @@ proto._debug = function debug(logMessage, executionId) {
345
345
 
346
346
  function getExecuteMessage(message) {
347
347
  const result = cloneMessage(message, {
348
- ...(message.fields.redelivered ? {isRecovered: true} : undefined),
348
+ ...(message.fields.redelivered && {isRecovered: true}),
349
349
  ignoreIfExecuting: undefined,
350
350
  });
351
351
  return result;
@@ -16,7 +16,7 @@ export default function Message(messageDef, context) {
16
16
  id,
17
17
  type,
18
18
  messageType: 'message',
19
- ...(name ? {name: environment.resolveExpression(name, executionMessage)} : undefined),
19
+ ...(name && {name: environment.resolveExpression(name, executionMessage)}),
20
20
  parent: {...parent},
21
21
  };
22
22
  }
@@ -16,7 +16,7 @@ export default function Signal(signalDef, context) {
16
16
  id,
17
17
  type,
18
18
  messageType: 'signal',
19
- ...(name ? {name: environment.resolveExpression(name, executionMessage)} : undefined),
19
+ ...(name && {name: environment.resolveExpression(name, executionMessage)}),
20
20
  parent: {...parent},
21
21
  };
22
22
  }
@@ -4,6 +4,7 @@ import {toSeconds, parse} from 'iso8601-duration';
4
4
  const kStopped = Symbol.for('stopped');
5
5
  const kTimerContent = Symbol.for('timerContent');
6
6
  const kTimer = Symbol.for('timer');
7
+ const repeatPattern = /^\s*R(\d+)\//;
7
8
 
8
9
  export default function TimerEventDefinition(activity, eventDefinition) {
9
10
  const type = this.type = eventDefinition.type || 'TimerEventDefinition';
@@ -63,7 +64,7 @@ proto.execute = function execute(executeMessage) {
63
64
  const resolvedTimer = this._getTimers(executeMessage);
64
65
  const timerContent = this[kTimerContent] = cloneContent(content, {
65
66
  ...resolvedTimer,
66
- ...(isResumed ? {isResumed} : undefined),
67
+ ...(isResumed && {isResumed}),
67
68
  startedAt,
68
69
  state: 'timer',
69
70
  });
@@ -112,6 +113,12 @@ proto._completed = function completed(completeContent, options) {
112
113
 
113
114
  const broker = this.broker;
114
115
  broker.publish('event', 'activity.timeout', cloneContent(timerContent, content), options);
116
+
117
+ if (timerContent.repeat > 1) {
118
+ const repeat = timerContent.repeat - 1;
119
+ broker.publish('execution', 'execute.repeat', cloneContent(timerContent, {...content, repeat}), options);
120
+ }
121
+
115
122
  broker.publish('execution', 'execute.completed', cloneContent(timerContent, content), options);
116
123
  };
117
124
 
@@ -146,7 +153,7 @@ proto._onApiMessage = function onApiMessage(routingKey, message) {
146
153
  this._stop();
147
154
  return this._completed({
148
155
  state: 'cancel',
149
- ...(message.content.message ? {message: message.content.message} : undefined),
156
+ ...(message.content.message && {message: message.content.message}),
150
157
  }, {correlationId});
151
158
  }
152
159
  case 'stop': {
@@ -175,7 +182,7 @@ proto._getTimers = function getTimers(executeMessage) {
175
182
 
176
183
  const now = Date.now();
177
184
  const result = {
178
- ...('expireAt' in content ? {expireAt: new Date(content.expireAt)} : undefined),
185
+ ...('expireAt' in content && {expireAt: new Date(content.expireAt)}),
179
186
  };
180
187
 
181
188
  for (const t of ['timeDuration', 'timeDate', 'timeCycle']) {
@@ -183,33 +190,39 @@ proto._getTimers = function getTimers(executeMessage) {
183
190
  else if (t in this) result[t] = this.environment.resolveExpression(this[t], executeMessage);
184
191
  else continue;
185
192
 
186
- let expireAtDate;
187
- if (t === 'timeDuration') {
188
- const durationStr = result[t];
189
- if (durationStr) {
190
- const delay = this._getDurationInMilliseconds(durationStr);
191
- if (delay !== undefined) expireAtDate = new Date(now + delay);
192
- } else {
193
- expireAtDate = new Date(now);
194
- }
195
- } else if (t === 'timeDate') {
196
- const dateStr = result[t];
197
- if (dateStr) {
198
- const ms = Date.parse(dateStr);
199
- if (!isNaN(ms)) {
200
- expireAtDate = new Date(ms);
201
- } else {
202
- this._warn(`invalid timeDate >${dateStr}<`);
193
+ let expireAtDate, repeat;
194
+ const timerStr = result[t];
195
+ if (timerStr) {
196
+ switch (t) {
197
+ case 'timeCycle': {
198
+ const mRepeat = timerStr.match(repeatPattern);
199
+ if (mRepeat && mRepeat.length) repeat = parseInt(mRepeat[1]);
200
+ }
201
+ case 'timeDuration': {
202
+ const delay = this._getDurationInMilliseconds(timerStr);
203
+ if (delay !== undefined) expireAtDate = new Date(now + delay);
204
+ break;
205
+ }
206
+ case 'timeDate': {
207
+ const dateStr = result[t];
208
+ const ms = Date.parse(dateStr);
209
+ if (!isNaN(ms)) {
210
+ expireAtDate = new Date(ms);
211
+ } else {
212
+ this._warn(`invalid timeDate >${dateStr}<`);
213
+ }
214
+ break;
203
215
  }
204
- } else {
205
- expireAtDate = new Date(now);
206
216
  }
217
+ } else {
218
+ expireAtDate = new Date(now);
207
219
  }
208
220
 
209
221
  if (!expireAtDate) continue;
210
222
  if (!('expireAt' in result) || result.expireAt > expireAtDate) {
211
223
  result.timerType = t;
212
224
  result.expireAt = expireAtDate;
225
+ if (repeat) result.repeat = repeat;
213
226
  }
214
227
  }
215
228
 
@@ -221,6 +234,10 @@ proto._getTimers = function getTimers(executeMessage) {
221
234
  result.timeout = 0;
222
235
  }
223
236
 
237
+ if (content.inbound && 'repeat' in content.inbound[0]) {
238
+ result.repeat = content.inbound[0].repeat;
239
+ }
240
+
224
241
  return result;
225
242
  };
226
243
 
@@ -228,7 +245,7 @@ proto._getDurationInMilliseconds = function getDurationInMilliseconds(duration)
228
245
  try {
229
246
  return toSeconds(parse(duration)) * 1000;
230
247
  } catch (err) {
231
- this._warn(`failed to parse timeDuration >${duration}<: ${err.message}`);
248
+ this._warn(`failed to parse ${this.timerType} >${duration}<: ${err.message}`);
232
249
  }
233
250
  };
234
251
 
@@ -50,13 +50,6 @@ proto.execute = function execute(executeMessage) {
50
50
  this[kExecuteMessage] = executeMessage;
51
51
 
52
52
  const broker = this.broker;
53
- if (eventDefinitionExecution && !this.environment.settings.strict) {
54
- broker.subscribeTmp('execution', 'execute.expect', this._onExpectMessage.bind(this), {
55
- noAck: true,
56
- consumerTag: '_expect-tag',
57
- });
58
- }
59
-
60
53
  const consumerTag = `_bound-listener-${executionId}`;
61
54
  this.attachedTo.broker.subscribeTmp('event', 'activity.leave', this._onAttachedLeave.bind(this), {
62
55
  noAck: true,
@@ -65,15 +58,19 @@ proto.execute = function execute(executeMessage) {
65
58
  });
66
59
  this[kAttachedTags].push(consumerTag);
67
60
 
68
- broker.subscribeOnce('execution', 'execute.detach', this._onDetachMessage.bind(this), {
69
- consumerTag: '_detach-tag',
70
- });
71
61
  broker.subscribeOnce('api', `activity.#.${executionId}`, this._onApiMessage.bind(this), {
72
62
  consumerTag: `_api-${executionId}`,
73
63
  });
74
- broker.subscribeOnce('execution', 'execute.bound.completed', this._onCompleted.bind(this), {
75
- consumerTag: `_execution-completed-${executionId}`,
76
- });
64
+
65
+ const execQ = broker.assertQueue(`_bound-execution-${executionId}`, {durable: false, autoDelete: true});
66
+ broker.bindQueue(execQ.name, 'execution', 'execute.detach');
67
+ broker.bindQueue(execQ.name, 'execution', 'execute.bound.completed');
68
+ broker.bindQueue(execQ.name, 'execution', 'execute.repeat');
69
+ if (eventDefinitionExecution && !this.environment.settings.strict) {
70
+ broker.bindQueue(execQ.name, 'execution', 'execute.expect');
71
+ }
72
+
73
+ execQ.consume(this._onExecutionMessage.bind(this), {consumerTag: '_execution-tag'});
77
74
  }
78
75
 
79
76
  if (eventDefinitionExecution) {
@@ -81,15 +78,29 @@ proto.execute = function execute(executeMessage) {
81
78
  }
82
79
  };
83
80
 
81
+ proto._onExecutionMessage = function onExecutionMessage(routingKey, message) {
82
+ message.ack();
83
+ switch (routingKey) {
84
+ case 'execute.detach':
85
+ return this._onDetachMessage(routingKey, message);
86
+ case 'execute.bound.completed':
87
+ return this._onCompleted(routingKey, message);
88
+ case 'execute.repeat':
89
+ return this._onRepeatMessage(routingKey, message);
90
+ case 'execute.expect':
91
+ return this._onExpectMessage(routingKey, message);
92
+ }
93
+ };
94
+
84
95
  proto._onCompleted = function onCompleted(_, {content}) {
85
96
  if (!this.cancelActivity && !content.cancelActivity) {
86
97
  this._stop();
87
- return this.broker.publish('execution', 'execute.completed', cloneContent(content));
98
+ return this.broker.publish('execution', 'execute.completed', cloneContent(content, {cancelActivity: false}));
88
99
  }
89
100
 
90
101
  this[kCompleteContent] = content;
91
102
 
92
- const {inbound} = this[kExecuteMessage].content;
103
+ const inbound = this[kExecuteMessage].content.inbound;
93
104
  const attachedToContent = inbound && inbound[0];
94
105
  const attachedTo = this.attachedTo;
95
106
  this.activity.logger.debug(`<${this.executionId} (${this.id})> cancel ${attachedTo.status} activity <${attachedToContent.executionId} (${attachedToContent.id})>`);
@@ -160,13 +171,19 @@ proto._onApiMessage = function onApiMessage(_, message) {
160
171
  }
161
172
  };
162
173
 
174
+ proto._onRepeatMessage = function onRepeatMessage(_, message) {
175
+ if (this.cancelActivity) return;
176
+ const executeMessage = this[kExecuteMessage];
177
+ const repeat = message.content.repeat;
178
+ this.broker.getQueue('inbound-q').queueMessage({routingKey: 'activity.restart'}, cloneContent(executeMessage.content.inbound[0], {repeat}));
179
+ };
180
+
163
181
  proto._stop = function stop(detach) {
164
182
  const attachedTo = this.attachedTo, broker = this.broker, executionId = this.executionId;
165
183
  for (const tag of this[kAttachedTags].splice(0)) attachedTo.broker.cancel(tag);
166
184
  for (const shovelName of this[kShovels].splice(0)) attachedTo.broker.closeShovel(shovelName);
167
185
 
168
- broker.cancel('_expect-tag');
169
- broker.cancel('_detach-tag');
186
+ broker.cancel('_execution-tag');
170
187
  broker.cancel(`_execution-completed-${executionId}`);
171
188
 
172
189
  if (detach) return;