bpmn-elements 9.0.0 → 9.1.1

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +1 -1
  3. package/dist/EventBroker.js +4 -0
  4. package/dist/Tracker.js +89 -0
  5. package/dist/activity/Activity.js +47 -57
  6. package/dist/activity/ActivityExecution.js +6 -9
  7. package/dist/definition/Definition.js +23 -17
  8. package/dist/definition/DefinitionExecution.js +23 -0
  9. package/dist/eventDefinitions/CancelEventDefinition.js +20 -76
  10. package/dist/eventDefinitions/CompensateEventDefinition.js +54 -41
  11. package/dist/eventDefinitions/ConditionalEventDefinition.js +11 -2
  12. package/dist/eventDefinitions/ErrorEventDefinition.js +2 -0
  13. package/dist/events/BoundaryEvent.js +25 -10
  14. package/dist/flows/Association.js +0 -7
  15. package/dist/gateways/EventBasedGateway.js +1 -2
  16. package/dist/process/Process.js +5 -0
  17. package/dist/process/ProcessExecution.js +128 -36
  18. package/dist/tasks/SubProcess.js +2 -1
  19. package/package.json +5 -5
  20. package/src/EventBroker.js +1 -0
  21. package/src/Tracker.js +73 -0
  22. package/src/activity/Activity.js +45 -51
  23. package/src/activity/ActivityExecution.js +6 -8
  24. package/src/definition/Definition.js +24 -21
  25. package/src/definition/DefinitionExecution.js +26 -0
  26. package/src/eventDefinitions/CancelEventDefinition.js +22 -66
  27. package/src/eventDefinitions/CompensateEventDefinition.js +48 -40
  28. package/src/eventDefinitions/ConditionalEventDefinition.js +12 -2
  29. package/src/eventDefinitions/ErrorEventDefinition.js +2 -0
  30. package/src/events/BoundaryEvent.js +20 -8
  31. package/src/flows/Association.js +0 -10
  32. package/src/gateways/EventBasedGateway.js +1 -2
  33. package/src/process/Process.js +6 -0
  34. package/src/process/ProcessExecution.js +126 -36
  35. package/src/tasks/SubProcess.js +2 -1
  36. package/types/index.d.ts +132 -11
@@ -29,12 +29,16 @@ function CompensateEventDefinition(activity, eventDefinition, context) {
29
29
  this.logger = environment.Logger(type.toLowerCase());
30
30
  if (!isThrowing) {
31
31
  this[kCompleted] = false;
32
- this[kAssociations] = context.getOutboundAssociations(id) || [];
32
+ this[kAssociations] = context.getOutboundAssociations(id);
33
33
  const messageQueueName = `${reference.referenceType}-${(0, _shared.brokerSafeId)(id)}-q`;
34
34
  this[kMessageQ] = broker.assertQueue(messageQueueName, {
35
35
  autoDelete: false,
36
36
  durable: true
37
37
  });
38
+ this[kCompensateQ] = broker.assertQueue('compensate-q', {
39
+ autoDelete: false,
40
+ durable: true
41
+ });
38
42
  broker.bindQueue(messageQueueName, 'api', `*.${reference.referenceType}.#`, {
39
43
  durable: true,
40
44
  priority: 400
@@ -53,6 +57,11 @@ CompensateEventDefinition.prototype.execute = function execute(executeMessage) {
53
57
  CompensateEventDefinition.prototype.executeCatch = function executeCatch(executeMessage) {
54
58
  this[kExecuteMessage] = executeMessage;
55
59
  this[kCompleted] = false;
60
+ if (executeMessage.fields.routingKey === 'execute.compensating') {
61
+ this._debug('resumed at compensating');
62
+ this[kCompleted] = true;
63
+ return this._compensate();
64
+ }
56
65
  const executeContent = executeMessage.content;
57
66
  const {
58
67
  executionId,
@@ -60,44 +69,34 @@ CompensateEventDefinition.prototype.executeCatch = function executeCatch(execute
60
69
  } = executeContent;
61
70
  this._debug('expect compensate');
62
71
  const broker = this.broker;
72
+ broker.cancel('_convey-messages');
63
73
  broker.assertExchange('compensate', 'topic');
64
- this[kCompensateQ] = broker.assertQueue('compensate-q', {
65
- durable: true,
66
- autoDelete: false
67
- });
68
74
  broker.subscribeTmp('compensate', 'execute.#', this._onCollect.bind(this), {
69
75
  noAck: true,
70
76
  consumerTag: '_oncollect-messages'
71
77
  });
72
- broker.publish('execution', 'execute.detach', (0, _messageHelper.cloneContent)(executeContent, {
73
- sourceExchange: 'execution',
74
- bindExchange: 'compensate'
75
- }));
76
78
  this[kMessageQ].consume(this._onCompensateApiMessage.bind(this), {
77
79
  noAck: true,
78
80
  consumerTag: `_oncompensate-${executionId}`
79
81
  });
80
82
  if (this[kCompleted]) return;
81
- const onApiMessage = this._onApiMessage.bind(this);
82
- broker.subscribeTmp('api', `activity.#.${executionId}`, onApiMessage, {
83
+ broker.subscribeTmp('api', `activity.#.${parent.executionId}#`, this._onApiMessage.bind(this), {
83
84
  noAck: true,
84
85
  consumerTag: `_api-${executionId}`
85
86
  });
86
- const detachContent = (0, _messageHelper.cloneContent)(executeContent, {
87
- executionId: parent.executionId,
88
- bindExchange: 'compensate'
89
- });
90
- detachContent.parent = (0, _messageHelper.shiftParent)(parent);
91
- broker.publish('event', 'activity.detach', detachContent);
87
+ broker.publish('execution', 'execute.detach', (0, _messageHelper.cloneContent)(executeContent, {
88
+ sourceExchange: 'execution',
89
+ bindExchange: 'compensate',
90
+ expect: 'compensate'
91
+ }));
92
92
  };
93
93
  CompensateEventDefinition.prototype.executeThrow = function executeThrow(executeMessage) {
94
94
  const executeContent = executeMessage.content;
95
95
  const {
96
- executionId,
97
96
  parent
98
97
  } = executeContent;
99
98
  const parentExecutionId = parent && parent.executionId;
100
- this.logger.debug(`<${executionId} (${this.activity.id})> throw compensate`);
99
+ this.logger.debug(`<${parentExecutionId} (${this.id})> throw compensate`);
101
100
  const broker = this.broker;
102
101
  const throwContent = (0, _messageHelper.cloneContent)(executeContent, {
103
102
  executionId: parentExecutionId,
@@ -120,12 +119,12 @@ CompensateEventDefinition.prototype._onCollect = function onCollect(routingKey,
120
119
  }
121
120
  };
122
121
  CompensateEventDefinition.prototype._onCompensateApiMessage = function onCompensateApiMessage(routingKey, message) {
123
- const output = message.content.message;
124
122
  this[kCompleted] = true;
125
- this._stop();
126
- this._debug('caught compensate event');
123
+ const output = message.content.message;
127
124
  const broker = this.broker;
128
125
  const executeContent = this[kExecuteMessage].content;
126
+ this._stopCollect();
127
+ this._debug('caught compensate event');
129
128
  const catchContent = (0, _messageHelper.cloneContent)(executeContent, {
130
129
  message: {
131
130
  ...output
@@ -133,27 +132,42 @@ CompensateEventDefinition.prototype._onCompensateApiMessage = function onCompens
133
132
  executionId: executeContent.parent.executionId
134
133
  });
135
134
  catchContent.parent = (0, _messageHelper.shiftParent)(catchContent.parent);
135
+ this[kCompensateQ].queueMessage({
136
+ routingKey: 'execute.compensated'
137
+ }, (0, _messageHelper.cloneContent)(executeContent));
138
+ broker.publish('execution', 'execute.compensating', (0, _messageHelper.cloneContent)(executeContent, {
139
+ message: {
140
+ ...output
141
+ }
142
+ }));
136
143
  broker.publish('event', 'activity.catch', catchContent, {
137
144
  type: 'catch'
138
145
  });
139
- const compensateQ = this[kCompensateQ];
140
- compensateQ.on('depleted', onDepleted);
141
- compensateQ.consume(this._onCollected.bind(this), {
146
+ return this._compensate();
147
+ };
148
+ CompensateEventDefinition.prototype._compensate = function compensate() {
149
+ return this[kCompensateQ].consume(this._onCollected.bind(this), {
142
150
  noAck: true,
143
151
  consumerTag: '_convey-messages'
144
152
  });
145
- for (const association of this[kAssociations]) association.complete((0, _messageHelper.cloneMessage)(message));
146
- function onDepleted() {
147
- compensateQ.off('depleted', onDepleted);
148
- return broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(executeContent, {
149
- output,
150
- state: 'catch'
151
- }));
152
- }
153
153
  };
154
154
  CompensateEventDefinition.prototype._onCollected = function onCollected(routingKey, message) {
155
+ if (routingKey === 'execute.compensated') {
156
+ const broker = this.broker;
157
+ broker.cancel('_convey-messages');
158
+ return this.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(message.content, {
159
+ cancelActivity: false
160
+ }));
161
+ }
155
162
  for (const association of this[kAssociations]) association.take((0, _messageHelper.cloneMessage)(message));
156
163
  };
164
+ CompensateEventDefinition.prototype._onDiscardApiMessage = function onDiscardApiMessage(routingKey, message) {
165
+ this[kCompleted] = true;
166
+ this._stop();
167
+ this[kCompensateQ].purge();
168
+ for (const association of this[kAssociations]) association.discard((0, _messageHelper.cloneMessage)(message));
169
+ return this.broker.publish('execution', 'execute.discard', (0, _messageHelper.cloneContent)(this[kExecuteMessage].content));
170
+ };
157
171
  CompensateEventDefinition.prototype._onApiMessage = function onApiMessage(routingKey, message) {
158
172
  const messageType = message.properties.type;
159
173
  switch (messageType) {
@@ -163,27 +177,26 @@ CompensateEventDefinition.prototype._onApiMessage = function onApiMessage(routin
163
177
  }
164
178
  case 'discard':
165
179
  {
166
- this[kCompleted] = true;
167
- this._stop();
168
- for (const association of this[kAssociations]) association.discard((0, _messageHelper.cloneMessage)(message));
169
- return this.broker.publish('execution', 'execute.discard', (0, _messageHelper.cloneContent)(this[kExecuteMessage].content));
180
+ return this._onDiscardApiMessage(routingKey, message);
170
181
  }
171
182
  case 'stop':
172
183
  {
173
- this._stop();
174
- break;
184
+ return this._stop();
175
185
  }
176
186
  }
177
187
  };
178
- CompensateEventDefinition.prototype._stop = function stop() {
188
+ CompensateEventDefinition.prototype._stopCollect = function stopCollect() {
179
189
  const broker = this.broker,
180
190
  executionId = this.executionId;
181
191
  broker.cancel(`_api-${executionId}`);
182
192
  broker.cancel(`_oncompensate-${executionId}`);
183
193
  broker.cancel('_oncollect-messages');
184
- broker.cancel('_convey-messages');
185
194
  this[kMessageQ].purge();
186
195
  };
196
+ CompensateEventDefinition.prototype._stop = function stop() {
197
+ this._stopCollect();
198
+ this.broker.cancel('_convey-messages');
199
+ };
187
200
  CompensateEventDefinition.prototype._debug = function debug(msg) {
188
201
  this.logger.debug(`<${this.executionId} (${this.activity.id})> ${msg}`);
189
202
  };
@@ -66,9 +66,12 @@ ConditionalEventDefinition.prototype.executeCatch = function executeCatch(execut
66
66
  const executeContent = executeMessage.content;
67
67
  const {
68
68
  executionId,
69
- index
69
+ index,
70
+ parent
70
71
  } = executeContent;
71
- this.broker.subscribeTmp('api', `activity.#.${executionId}`, this._onCatchApiMessage.bind(this), {
72
+ const parentExecutionId = parent.executionId;
73
+ const broker = this.broker;
74
+ broker.subscribeTmp('api', `activity.#.${executionId}`, this._onCatchApiMessage.bind(this), {
72
75
  noAck: true,
73
76
  consumerTag: `_api-${executionId}_${index}`
74
77
  });
@@ -81,6 +84,12 @@ ConditionalEventDefinition.prototype.executeCatch = function executeCatch(execut
81
84
  priority: 300,
82
85
  consumerTag: `_onend-${executionId}_${index}`
83
86
  });
87
+ const waitContent = (0, _messageHelper.cloneContent)(executeContent, {
88
+ executionId: parentExecutionId,
89
+ condition: this.condition
90
+ });
91
+ waitContent.parent = (0, _messageHelper.shiftParent)(parent);
92
+ broker.publish('event', 'activity.wait', waitContent);
84
93
  };
85
94
  ConditionalEventDefinition.prototype._onWaitApiMessage = function onWaitApiMessage(routingKey, message) {
86
95
  const messageType = message.properties.type;
@@ -86,6 +86,8 @@ ErrorEventDefinition.prototype.executeCatch = function executeCatch(executeMessa
86
86
  consumerTag: `_onerror-${executionId}`
87
87
  });
88
88
  broker.publish('execution', 'execute.expect', (0, _messageHelper.cloneContent)(executeContent, {
89
+ pattern: 'activity.error',
90
+ exchange: 'execution',
89
91
  expectRoutingKey,
90
92
  expect: {
91
93
  ...info.message
@@ -99,7 +99,7 @@ BoundaryEventBehaviour.prototype._onExecutionMessage = function onExecutionMessa
99
99
  BoundaryEventBehaviour.prototype._onCompleted = function onCompleted(_, {
100
100
  content
101
101
  }) {
102
- if (!this.cancelActivity && !content.cancelActivity) {
102
+ if (content.cancelActivity === false || !this.cancelActivity && !content.cancelActivity) {
103
103
  this._stop();
104
104
  return this.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(content, {
105
105
  isDefinitionScope: false,
@@ -129,25 +129,34 @@ BoundaryEventBehaviour.prototype._onExpectMessage = function onExpectMessage(_,
129
129
  }) {
130
130
  const {
131
131
  executionId,
132
- expectRoutingKey
132
+ expectRoutingKey,
133
+ pattern,
134
+ exchange
133
135
  } = content;
134
136
  const attachedTo = this.attachedTo;
135
137
  const errorConsumerTag = `_bound-error-listener-${executionId}`;
136
138
  this[kAttachedTags].push(errorConsumerTag);
137
- attachedTo.broker.subscribeTmp('event', 'activity.error', (__, errorMessage) => {
138
- if (errorMessage.content.id !== attachedTo.id) return;
139
- this.broker.publish('execution', expectRoutingKey, (0, _messageHelper.cloneContent)(errorMessage.content));
139
+ attachedTo.broker.subscribeTmp('event', pattern, (__, message) => {
140
+ if (message.content.id !== attachedTo.id) return;
141
+ this.broker.publish(exchange, expectRoutingKey, (0, _messageHelper.cloneContent)(message.content, {
142
+ attachedTo: attachedTo.id
143
+ }), {
144
+ ...message.properties,
145
+ mandatory: false
146
+ });
140
147
  }, {
141
148
  noAck: true,
142
149
  consumerTag: errorConsumerTag,
143
- priority: 300
150
+ priority: 400
144
151
  });
145
152
  };
146
- BoundaryEventBehaviour.prototype._onDetachMessage = function onDetachMessage(_, {
147
- content
148
- }) {
153
+ BoundaryEventBehaviour.prototype._onDetachMessage = function onDetachMessage(_, message) {
154
+ const content = message.content;
155
+ const {
156
+ executionId,
157
+ parent
158
+ } = this[kExecuteMessage].content;
149
159
  const id = this.id,
150
- executionId = this.executionId,
151
160
  attachedTo = this.attachedTo;
152
161
  this.activity.logger.debug(`<${executionId} (${id})> detach from activity <${attachedTo.id}>`);
153
162
  this._stop(true);
@@ -169,6 +178,12 @@ BoundaryEventBehaviour.prototype._onDetachMessage = function onDetachMessage(_,
169
178
  }, {
170
179
  cloneMessage: _messageHelper.cloneMessage
171
180
  });
181
+ const detachContent = (0, _messageHelper.cloneContent)(content, {
182
+ executionId
183
+ });
184
+ detachContent.parent = parent;
185
+ this.activity.removeInboundListeners();
186
+ broker.publish('event', 'activity.detach', detachContent);
172
187
  broker.subscribeOnce('execution', 'execute.bound.completed', (__, {
173
188
  content: completeContent
174
189
  }) => {
@@ -32,7 +32,6 @@ function Association(associationDef, {
32
32
  this.environment = environment;
33
33
  const logger = this.logger = environment.Logger(type.toLowerCase());
34
34
  this[kCounters] = {
35
- complete: 0,
36
35
  take: 0,
37
36
  discard: 0
38
37
  };
@@ -72,12 +71,6 @@ Association.prototype.discard = function discard(content = {}) {
72
71
  this._publishEvent('discard', content);
73
72
  return true;
74
73
  };
75
- Association.prototype.complete = function complete(content = {}) {
76
- this.logger.debug(`<${this.id}> completed target <${this.targetId}>`);
77
- ++this[kCounters].complete;
78
- this._publishEvent('complete', content);
79
- return true;
80
- };
81
74
  Association.prototype.getState = function getState() {
82
75
  return this._createMessageContent({
83
76
  counters: this.counters,
@@ -47,7 +47,6 @@ EventBasedGatewayBehaviour.prototype.execute = function execute(executeMessage)
47
47
  }
48
48
  const broker = this.activity.broker;
49
49
  broker.subscribeOnce('api', `activity.stop.${executionId}`, () => this._stop(), {
50
- noAck: true,
51
50
  consumerTag: '_api-stop-execution'
52
51
  });
53
52
  this[kCompleted] = false;
@@ -58,7 +57,7 @@ EventBasedGatewayBehaviour.prototype.execute = function execute(executeMessage)
58
57
  EventBasedGatewayBehaviour.prototype._onTargetCompleted = function onTargetCompleted(executeMessage, _, message, owner) {
59
58
  const {
60
59
  id: targetId,
61
- exexutionId: targetExecutionId
60
+ executionId: targetExecutionId
62
61
  } = message.content;
63
62
  const executeContent = executeMessage.content;
64
63
  const executionId = executeContent.executionId;
@@ -117,6 +117,11 @@ Object.defineProperty(Process.prototype, 'status', {
117
117
  return this[kStatus];
118
118
  }
119
119
  });
120
+ Object.defineProperty(Process.prototype, 'activityStatus', {
121
+ get() {
122
+ return this[kExec].execution && this[kExec].execution.activityStatus || 'idle';
123
+ }
124
+ });
120
125
  Process.prototype.init = function init(useAsExecutionId) {
121
126
  const exec = this[kExec];
122
127
  const initExecutionId = exec.initExecutionId = useAsExecutionId || (0, _shared.getUniqueId)(this.id);
@@ -7,6 +7,7 @@ exports.default = void 0;
7
7
  var _Api = require("../Api.js");
8
8
  var _messageHelper = require("../messageHelper.js");
9
9
  var _shared = require("../shared.js");
10
+ var _Tracker = require("../Tracker.js");
10
11
  var _default = ProcessExecution;
11
12
  exports.default = _default;
12
13
  const kActivated = Symbol.for('activated');
@@ -18,17 +19,20 @@ const kMessageHandlers = Symbol.for('messageHandlers');
18
19
  const kParent = Symbol.for('parent');
19
20
  const kStatus = Symbol.for('status');
20
21
  const kStopped = Symbol.for('stopped');
22
+ const kTracker = Symbol.for('activity tracker');
21
23
  function ProcessExecution(parentActivity, context) {
22
24
  const {
23
25
  id,
24
26
  type,
25
27
  broker,
26
- isSubProcess
28
+ isSubProcess,
29
+ isTransaction
27
30
  } = parentActivity;
28
31
  this[kParent] = parentActivity;
29
32
  this.id = id;
30
33
  this.type = type;
31
34
  this.isSubProcess = isSubProcess;
35
+ this.isTransaction = isSubProcess && isTransaction;
32
36
  this.broker = broker;
33
37
  this.environment = context.environment;
34
38
  this.context = context;
@@ -52,6 +56,7 @@ function ProcessExecution(parentActivity, context) {
52
56
  this[kStopped] = false;
53
57
  this[kActivated] = false;
54
58
  this[kStatus] = 'init';
59
+ this[kTracker] = new _Tracker.ActivityTracker(id);
55
60
  this.executionId = undefined;
56
61
  this[kMessageHandlers] = {
57
62
  onActivityEvent: this._onActivityEvent.bind(this),
@@ -88,6 +93,11 @@ Object.defineProperty(ProcessExecution.prototype, 'isRunning', {
88
93
  return this[kActivated];
89
94
  }
90
95
  });
96
+ Object.defineProperty(ProcessExecution.prototype, 'activityStatus', {
97
+ get() {
98
+ return this[kTracker].activityStatus;
99
+ }
100
+ });
91
101
  ProcessExecution.prototype.execute = function execute(executeMessage) {
92
102
  if (!executeMessage) throw new Error('Process execution requires message');
93
103
  if (!executeMessage.content || !executeMessage.content.executionId) throw new Error('Process execution requires execution id');
@@ -131,6 +141,7 @@ ProcessExecution.prototype.resume = function resume() {
131
141
  if (this[kCompleted]) return;
132
142
  const status = this.status;
133
143
  if (status === 'init') return this._start();
144
+ const tracker = this[kTracker];
134
145
  for (const msg of postponed.slice()) {
135
146
  const activity = this.getActivityById(msg.content.id);
136
147
  if (!activity) continue;
@@ -140,6 +151,7 @@ ProcessExecution.prototype.resume = function resume() {
140
151
  msg.ack();
141
152
  continue;
142
153
  }
154
+ tracker.track(msg.fields.routingKey, msg);
143
155
  activity.resume();
144
156
  }
145
157
  if (this[kCompleted]) return;
@@ -248,6 +260,17 @@ ProcessExecution.prototype.discard = function discard() {
248
260
  type: 'discard'
249
261
  });
250
262
  };
263
+ ProcessExecution.prototype.cancel = function discard() {
264
+ return this[kActivityQ].queueMessage({
265
+ routingKey: 'execution.cancel'
266
+ }, {
267
+ id: this.id,
268
+ type: this.type,
269
+ executionId: this.executionId
270
+ }, {
271
+ type: 'cancel'
272
+ });
273
+ };
251
274
  ProcessExecution.prototype.getState = function getState() {
252
275
  const {
253
276
  children,
@@ -285,6 +308,9 @@ ProcessExecution.prototype.getActivityById = function getActivityById(activityId
285
308
  ProcessExecution.prototype.getSequenceFlows = function getSequenceFlows() {
286
309
  return this[kElements].flows.slice();
287
310
  };
311
+ ProcessExecution.prototype.getAssociations = function getAssociations() {
312
+ return this[kElements].associations.slice();
313
+ };
288
314
  ProcessExecution.prototype.getApi = function getApi(message) {
289
315
  if (!message) return (0, _Api.ProcessApi)(this.broker, this[kExecuteMessage]);
290
316
  const content = message.content;
@@ -322,6 +348,7 @@ ProcessExecution.prototype._start = function start() {
322
348
  for (const a of startActivities) a.shake();
323
349
  }
324
350
  for (const a of startActivities) a.init();
351
+ this[kStatus] = 'executing';
325
352
  for (const a of startActivities) a.run();
326
353
  postponed.splice(0);
327
354
  detachedActivities.splice(0);
@@ -465,6 +492,7 @@ ProcessExecution.prototype._onActivityEvent = function onActivityEvent(routingKe
465
492
  });
466
493
  }
467
494
  if (delegate) delegate = this._onDelegateEvent(message);
495
+ this[kTracker].track(routingKey, message);
468
496
  this.broker.publish('event', routingKey, content, {
469
497
  ...message.properties,
470
498
  delegate,
@@ -472,7 +500,6 @@ ProcessExecution.prototype._onActivityEvent = function onActivityEvent(routingKe
472
500
  });
473
501
  if (shaking) return this._onShookEnd(message);
474
502
  if (!isDirectChild) return;
475
- if (content.isAssociation) return;
476
503
  switch (routingKey) {
477
504
  case 'process.terminate':
478
505
  return this[kActivityQ].queueMessage({
@@ -502,6 +529,17 @@ ProcessExecution.prototype._onChildMessage = function onChildMessage(routingKey,
502
529
  case 'execution.discard':
503
530
  message.ack();
504
531
  return this._onDiscard(message);
532
+ case 'execution.discard.detached':
533
+ {
534
+ message.ack();
535
+ for (const detached of this[kElements].detachedActivities) {
536
+ this._getChildApi(detached).discard();
537
+ }
538
+ return;
539
+ }
540
+ case 'execution.cancel':
541
+ message.ack();
542
+ return this._onCancel(message);
505
543
  case 'activity.error.caught':
506
544
  {
507
545
  const prevMsg = this[kElements].postponed.find(msg => {
@@ -510,7 +548,6 @@ ProcessExecution.prototype._onChildMessage = function onChildMessage(routingKey,
510
548
  if (!prevMsg) return message.ack();
511
549
  break;
512
550
  }
513
- case 'activity.compensation.end':
514
551
  case 'flow.looped':
515
552
  case 'activity.leave':
516
553
  return this._onChildCompleted(message);
@@ -522,14 +559,17 @@ ProcessExecution.prototype._onChildMessage = function onChildMessage(routingKey,
522
559
  this[kElements].detachedActivities.push((0, _messageHelper.cloneMessage)(message));
523
560
  break;
524
561
  }
562
+ case 'activity.cancel':
563
+ {
564
+ if (this.isTransaction) this._onCancel(message);
565
+ break;
566
+ }
525
567
  case 'activity.discard':
526
- case 'activity.compensation.start':
527
568
  case 'activity.enter':
528
569
  {
529
- this[kStatus] = 'executing';
530
570
  if (!content.inbound) break;
531
571
  for (const inbound of content.inbound) {
532
- if (!inbound.isSequenceFlow) continue;
572
+ if (!inbound.isSequenceFlow && !inbound.isAssociation) continue;
533
573
  const inboundMessage = this._popPostponed(inbound);
534
574
  if (inboundMessage) inboundMessage.ack();
535
575
  }
@@ -567,7 +607,7 @@ ProcessExecution.prototype._popPostponed = function popPostponed(byContent) {
567
607
  detachedActivities
568
608
  } = this[kElements];
569
609
  const postponedIdx = postponed.findIndex(msg => {
570
- if (msg.content.isSequenceFlow) return msg.content.sequenceId === byContent.sequenceId;
610
+ if (msg.content.isSequenceFlow || msg.content.isAssociation) return msg.content.sequenceId === byContent.sequenceId;
571
611
  return msg.content.executionId === byContent.executionId;
572
612
  });
573
613
  let postponedMsg;
@@ -598,9 +638,16 @@ ProcessExecution.prototype._onChildCompleted = function onChildCompleted(message
598
638
  return this._complete('completed');
599
639
  }
600
640
  this._debug(`left <${id}> (${type}), pending runs ${postponedCount}, ${postponed.map(a => a.content.id).join(',')}`);
601
- if (postponedCount === detachedActivities.length) {
602
- for (const api of this.getPostponed()) api.discard();
603
- return;
641
+ if (postponedCount && postponedCount === detachedActivities.length) {
642
+ return this[kActivityQ].queueMessage({
643
+ routingKey: 'execution.discard.detached'
644
+ }, {
645
+ id: this.id,
646
+ type: this.type,
647
+ executionId: this.executionId
648
+ }, {
649
+ type: 'cancel'
650
+ });
604
651
  }
605
652
  if (isEnd && startActivities.length) {
606
653
  const startSequences = this[kElements].startSequences;
@@ -637,33 +684,42 @@ ProcessExecution.prototype._onDiscard = function onDiscard() {
637
684
  this._deactivate();
638
685
  const running = this[kElements].postponed.splice(0);
639
686
  this._debug(`discard process execution (discard child executions ${running.length})`);
640
- for (const flow of this.getSequenceFlows()) flow.stop();
641
- for (const msg of running) this._getChildApi(msg).discard();
687
+ if (this.isSubProcess) {
688
+ this.stop();
689
+ } else {
690
+ for (const flow of this.getSequenceFlows()) flow.stop();
691
+ for (const flow of this.getAssociations()) flow.stop();
692
+ for (const msg of running) this._getChildApi(msg).discard();
693
+ }
642
694
  this[kActivityQ].purge();
643
695
  return this._complete('discard');
644
696
  };
645
- ProcessExecution.prototype._onApiMessage = function onApiMessage(routingKey, message) {
646
- const executionId = this.executionId;
647
- const broker = this.broker;
648
- if (message.properties.delegate) {
649
- const correlationId = message.properties.correlationId || (0, _shared.getUniqueId)(executionId);
650
- this._debug(`delegate api ${routingKey} message to children, with correlationId <${correlationId}>`);
651
- let consumed = false;
652
- broker.subscribeTmp('event', 'activity.consumed', (_, msg) => {
653
- if (msg.properties.correlationId === correlationId) {
654
- consumed = true;
655
- this._debug(`delegated api message was consumed by ${msg.content ? msg.content.executionId : 'unknown'}`);
697
+ ProcessExecution.prototype._onCancel = function onCancel() {
698
+ const running = this[kElements].postponed.slice(0);
699
+ const isTransaction = this.isTransaction;
700
+ if (isTransaction) {
701
+ this._debug(`cancel transaction execution (cancel child executions ${running.length})`);
702
+ this[kStatus] = 'cancel';
703
+ this.broker.publish('event', 'transaction.cancel', (0, _messageHelper.cloneMessage)(this[kExecuteMessage], {
704
+ state: 'cancel'
705
+ }));
706
+ for (const msg of running) {
707
+ if (msg.content.expect === 'compensate') {
708
+ this._getChildApi(msg).sendApiMessage('compensate');
709
+ } else if (!msg.content.isForCompensation) {
710
+ this._getChildApi(msg).discard();
656
711
  }
657
- }, {
658
- consumerTag: `_ct-delegate-${correlationId}`,
659
- noAck: true
660
- });
661
- for (const child of this[kElements].children) {
662
- if (child.placeholder) continue;
663
- child.broker.publish('api', routingKey, (0, _messageHelper.cloneContent)(message.content), message.properties);
664
- if (consumed) break;
665
712
  }
666
- return broker.cancel(`_ct-delegate-${correlationId}`);
713
+ } else {
714
+ this._debug(`cancel process execution (cancel child executions ${running.length})`);
715
+ for (const msg of running) {
716
+ this._getChildApi(msg).discard();
717
+ }
718
+ }
719
+ };
720
+ ProcessExecution.prototype._onApiMessage = function onApiMessage(routingKey, message) {
721
+ if (message.properties.delegate) {
722
+ return this._delegateApiMessage(routingKey, message);
667
723
  }
668
724
  if (this.id !== message.content.id) {
669
725
  const child = this.getActivityById(message.content.id);
@@ -672,6 +728,8 @@ ProcessExecution.prototype._onApiMessage = function onApiMessage(routingKey, mes
672
728
  }
673
729
  if (this.executionId !== message.content.executionId) return;
674
730
  switch (message.properties.type) {
731
+ case 'cancel':
732
+ return this.cancel(message);
675
733
  case 'discard':
676
734
  return this.discard(message);
677
735
  case 'stop':
@@ -683,11 +741,43 @@ ProcessExecution.prototype._onApiMessage = function onApiMessage(routingKey, mes
683
741
  break;
684
742
  }
685
743
  };
744
+ ProcessExecution.prototype._delegateApiMessage = function delegateApiMessage(routingKey, message, continueOnConsumed) {
745
+ const correlationId = message.properties.correlationId || (0, _shared.getUniqueId)(this.executionId);
746
+ this._debug(`delegate api ${routingKey} message to children, with correlationId <${correlationId}>`);
747
+ const broker = this.broker;
748
+ let consumed = false;
749
+ broker.subscribeTmp('event', 'activity.consumed', (_, msg) => {
750
+ if (msg.properties.correlationId === correlationId) {
751
+ consumed = true;
752
+ this._debug(`delegated api message was consumed by ${msg.content ? msg.content.executionId : 'unknown'}`);
753
+ }
754
+ }, {
755
+ consumerTag: `_ct-delegate-${correlationId}`,
756
+ noAck: true
757
+ });
758
+ for (const child of this[kElements].children) {
759
+ if (child.placeholder) continue;
760
+ child.broker.publish('api', routingKey, (0, _messageHelper.cloneContent)(message.content), message.properties);
761
+ if (consumed && !continueOnConsumed) break;
762
+ }
763
+ return broker.cancel(`_ct-delegate-${correlationId}`);
764
+ };
686
765
  ProcessExecution.prototype._complete = function complete(completionType, content) {
687
766
  this._deactivate();
688
- this._debug(`process execution ${completionType}`);
689
767
  this[kCompleted] = true;
690
- if (this.status !== 'terminated') this[kStatus] = completionType;
768
+ const status = this.status;
769
+ switch (this.status) {
770
+ case 'cancel':
771
+ this._debug('process execution cancelled');
772
+ case 'discard':
773
+ completionType = status;
774
+ break;
775
+ case 'terminated':
776
+ break;
777
+ default:
778
+ this._debug(`process execution ${completionType}`);
779
+ this[kStatus] = completionType;
780
+ }
691
781
  const broker = this.broker;
692
782
  this[kActivityQ].delete();
693
783
  return broker.publish(this._exchangeName, `execution.${completionType}.${this.executionId}`, (0, _messageHelper.cloneContent)(this[kExecuteMessage].content, {
@@ -706,13 +796,15 @@ ProcessExecution.prototype._terminate = function terminate(message) {
706
796
  this._debug('terminating process execution');
707
797
  const running = this[kElements].postponed.splice(0);
708
798
  for (const flow of this.getSequenceFlows()) flow.stop();
799
+ for (const flow of this.getAssociations()) flow.stop();
709
800
  for (const msg of running) {
710
801
  const {
711
802
  id: postponedId,
712
- isSequenceFlow
803
+ isSequenceFlow,
804
+ isAssociation
713
805
  } = msg.content;
714
806
  if (postponedId === message.content.id) continue;
715
- if (isSequenceFlow) continue;
807
+ if (isSequenceFlow || isAssociation) continue;
716
808
  this._getChildApi(msg).stop();
717
809
  msg.ack();
718
810
  }