bpmn-elements 5.1.3 → 7.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +322 -0
  2. package/README.md +9 -3
  3. package/dist/index.js +71 -39
  4. package/dist/src/Api.js +77 -76
  5. package/dist/src/Context.js +169 -164
  6. package/dist/src/Environment.js +90 -102
  7. package/dist/src/EventBroker.js +89 -88
  8. package/dist/src/ExtensionsMapper.js +2 -2
  9. package/dist/src/MessageFormatter.js +164 -95
  10. package/dist/src/Scripts.js +6 -2
  11. package/dist/src/Timers.js +4 -6
  12. package/dist/src/activity/Activity.js +1108 -901
  13. package/dist/src/activity/ActivityExecution.js +342 -297
  14. package/dist/src/activity/Dummy.js +3 -3
  15. package/dist/src/definition/Definition.js +498 -444
  16. package/dist/src/definition/DefinitionExecution.js +722 -409
  17. package/dist/src/error/Errors.js +17 -7
  18. package/dist/src/eventDefinitions/CancelEventDefinition.js +190 -150
  19. package/dist/src/eventDefinitions/CompensateEventDefinition.js +194 -161
  20. package/dist/src/eventDefinitions/ConditionalEventDefinition.js +197 -135
  21. package/dist/src/eventDefinitions/ErrorEventDefinition.js +207 -165
  22. package/dist/src/eventDefinitions/EscalationEventDefinition.js +175 -141
  23. package/dist/src/eventDefinitions/EventDefinitionExecution.js +157 -129
  24. package/dist/src/eventDefinitions/LinkEventDefinition.js +174 -149
  25. package/dist/src/eventDefinitions/MessageEventDefinition.js +213 -176
  26. package/dist/src/eventDefinitions/SignalEventDefinition.js +203 -161
  27. package/dist/src/eventDefinitions/TerminateEventDefinition.js +21 -23
  28. package/dist/src/eventDefinitions/TimerEventDefinition.js +243 -228
  29. package/dist/src/events/BoundaryEvent.js +180 -144
  30. package/dist/src/events/EndEvent.js +18 -23
  31. package/dist/src/events/IntermediateCatchEvent.js +44 -58
  32. package/dist/src/events/IntermediateThrowEvent.js +18 -23
  33. package/dist/src/events/StartEvent.js +109 -94
  34. package/dist/src/flows/Association.js +94 -101
  35. package/dist/src/flows/MessageFlow.js +86 -103
  36. package/dist/src/flows/SequenceFlow.js +172 -184
  37. package/dist/src/gateways/EventBasedGateway.js +88 -84
  38. package/dist/src/gateways/ExclusiveGateway.js +13 -16
  39. package/dist/src/gateways/InclusiveGateway.js +11 -14
  40. package/dist/src/gateways/ParallelGateway.js +11 -14
  41. package/dist/src/getPropertyValue.js +34 -34
  42. package/dist/src/io/BpmnIO.js +31 -0
  43. package/dist/src/io/EnvironmentDataObject.js +33 -29
  44. package/dist/src/io/EnvironmentDataStore.js +52 -0
  45. package/dist/src/io/EnvironmentDataStoreReference.js +52 -0
  46. package/dist/src/io/InputOutputSpecification.js +177 -168
  47. package/dist/src/io/Properties.js +252 -0
  48. package/dist/src/messageHelper.js +1 -1
  49. package/dist/src/process/Process.js +433 -359
  50. package/dist/src/process/ProcessExecution.js +744 -645
  51. package/dist/src/shared.js +3 -6
  52. package/dist/src/tasks/CallActivity.js +160 -0
  53. package/dist/src/tasks/LoopCharacteristics.js +309 -330
  54. package/dist/src/tasks/ReceiveTask.js +233 -182
  55. package/dist/src/tasks/ScriptTask.js +35 -41
  56. package/dist/src/tasks/ServiceImplementation.js +13 -20
  57. package/dist/src/tasks/ServiceTask.js +82 -75
  58. package/dist/src/tasks/SignalTask.js +97 -93
  59. package/dist/src/tasks/StandardLoopCharacteristics.js +1 -1
  60. package/dist/src/tasks/SubProcess.js +195 -175
  61. package/dist/src/tasks/Task.js +17 -19
  62. package/index.js +8 -0
  63. package/package.json +16 -15
  64. package/src/Api.js +65 -59
  65. package/src/Context.js +142 -132
  66. package/src/Environment.js +88 -100
  67. package/src/EventBroker.js +67 -68
  68. package/src/ExtensionsMapper.js +2 -2
  69. package/src/MessageFormatter.js +132 -74
  70. package/src/Timers.js +4 -4
  71. package/src/activity/Activity.js +916 -757
  72. package/src/activity/ActivityExecution.js +293 -247
  73. package/src/activity/Dummy.js +2 -2
  74. package/src/definition/Definition.js +436 -401
  75. package/src/definition/DefinitionExecution.js +603 -343
  76. package/src/error/Errors.js +11 -6
  77. package/src/eventDefinitions/CancelEventDefinition.js +164 -121
  78. package/src/eventDefinitions/CompensateEventDefinition.js +158 -124
  79. package/src/eventDefinitions/ConditionalEventDefinition.js +147 -104
  80. package/src/eventDefinitions/ErrorEventDefinition.js +190 -131
  81. package/src/eventDefinitions/EscalationEventDefinition.js +139 -101
  82. package/src/eventDefinitions/EventDefinitionExecution.js +127 -95
  83. package/src/eventDefinitions/LinkEventDefinition.js +160 -129
  84. package/src/eventDefinitions/MessageEventDefinition.js +178 -121
  85. package/src/eventDefinitions/SignalEventDefinition.js +162 -106
  86. package/src/eventDefinitions/TerminateEventDefinition.js +19 -19
  87. package/src/eventDefinitions/TimerEventDefinition.js +202 -167
  88. package/src/events/BoundaryEvent.js +156 -115
  89. package/src/events/EndEvent.js +15 -18
  90. package/src/events/IntermediateCatchEvent.js +40 -44
  91. package/src/events/IntermediateThrowEvent.js +15 -18
  92. package/src/events/StartEvent.js +84 -50
  93. package/src/flows/Association.js +98 -113
  94. package/src/flows/MessageFlow.js +81 -97
  95. package/src/flows/SequenceFlow.js +145 -163
  96. package/src/gateways/EventBasedGateway.js +75 -68
  97. package/src/gateways/ExclusiveGateway.js +8 -13
  98. package/src/gateways/InclusiveGateway.js +8 -13
  99. package/src/gateways/ParallelGateway.js +8 -13
  100. package/src/getPropertyValue.js +34 -33
  101. package/src/io/BpmnIO.js +20 -0
  102. package/src/io/EnvironmentDataObject.js +29 -18
  103. package/src/io/EnvironmentDataStore.js +33 -0
  104. package/src/io/EnvironmentDataStoreReference.js +33 -0
  105. package/src/io/InputOutputSpecification.js +154 -157
  106. package/src/io/Properties.js +199 -0
  107. package/src/process/Process.js +374 -333
  108. package/src/process/ProcessExecution.js +606 -554
  109. package/src/shared.js +1 -5
  110. package/src/tasks/CallActivity.js +130 -0
  111. package/src/tasks/LoopCharacteristics.js +290 -289
  112. package/src/tasks/ReceiveTask.js +174 -107
  113. package/src/tasks/ScriptTask.js +27 -30
  114. package/src/tasks/ServiceImplementation.js +13 -18
  115. package/src/tasks/ServiceTask.js +67 -60
  116. package/src/tasks/SignalTask.js +77 -52
  117. package/src/tasks/StandardLoopCharacteristics.js +1 -1
  118. package/src/tasks/SubProcess.js +184 -157
  119. package/src/tasks/Task.js +15 -19
@@ -1,15 +1,19 @@
1
- import {ActivityError} from '../error/Errors';
2
- import {cloneContent, cloneMessage, unshiftParent} from '../messageHelper';
1
+ import {RunError} from '../error/Errors';
2
+ import {cloneContent, cloneMessage, unshiftParent, cloneParent} from '../messageHelper';
3
3
 
4
4
  export default function LoopCharacteristics(activity, loopCharacteristics) {
5
- const {id, broker, environment} = activity;
6
- const {batchSize = 50} = environment.settings;
5
+ this.activity = activity;
6
+ this.loopCharacteristics = loopCharacteristics;
7
7
  const {type = 'LoopCharacteristics', behaviour = {}} = loopCharacteristics;
8
- const {isSequential = false, collection: collectionExpression, elementVariable = 'item'} = behaviour;
8
+ this.type = type;
9
+ const {isSequential = false, collection} = behaviour;
10
+ this.isSequential = isSequential;
11
+ this.collection = collection;
9
12
 
10
13
  let completionCondition, startCondition, loopCardinality;
11
14
  if ('loopCardinality' in behaviour) loopCardinality = behaviour.loopCardinality;
12
15
  else if ('loopMaximum' in behaviour) loopCardinality = behaviour.loopMaximum;
16
+ this.loopCardinality = loopCardinality;
13
17
 
14
18
  if (behaviour.loopCondition) {
15
19
  if (behaviour.testBefore) startCondition = behaviour.loopCondition;
@@ -19,323 +23,320 @@ export default function LoopCharacteristics(activity, loopCharacteristics) {
19
23
  completionCondition = behaviour.completionCondition;
20
24
  }
21
25
 
22
- const loopType = getLoopType();
23
- if (!loopType) return;
26
+ if (collection) {
27
+ this.loopType = 'collection';
28
+ this.elementVariable = behaviour.elementVariable || 'item';
29
+ } else if (completionCondition) this.loopType = 'complete condition';
30
+ else if (startCondition) this.loopType = 'start condition';
31
+ else if (loopCardinality) this.loopType = 'cardinality';
24
32
 
25
- const {debug} = environment.Logger(type.toLowerCase());
26
- const executeConsumerTag = '_execute-q-multi-instance-tag';
27
- broker.cancel(executeConsumerTag);
33
+ this.characteristics = null;
34
+ this.execution = null;
35
+ }
28
36
 
29
- const apiConsumerTag = '_api-multi-instance-tag';
30
- broker.cancel(apiConsumerTag);
37
+ LoopCharacteristics.prototype.execute = function execute(executeMessage) {
38
+ if (!executeMessage) throw new TypeError('LoopCharacteristics execution requires message');
39
+ const chr = this.characteristics = this.characteristics || new Characteristics(this.activity, this.loopCharacteristics, executeMessage);
40
+ if (chr.cardinality === 0) return chr.complete();
31
41
 
32
- let loopSettings;
42
+ const execution = this.isSequential ? new SequentialLoopCharacteristics(this.activity, chr) : new ParallelLoopCharacteristics(this.activity, chr);
43
+ return execution.execute(executeMessage);
44
+ };
33
45
 
34
- const characteristicsApi = {
35
- type,
36
- loopType,
37
- collection: collectionExpression,
38
- elementVariable,
39
- isSequential,
40
- loopCardinality,
41
- execute,
42
- };
46
+ function SequentialLoopCharacteristics(activity, characteristics) {
47
+ this.activity = activity;
48
+ this.id = activity.id;
49
+ this.characteristics = characteristics;
50
+ }
43
51
 
44
- return characteristicsApi;
52
+ SequentialLoopCharacteristics.prototype.execute = function execute(executeMessage) {
53
+ const {routingKey: executeRoutingKey, redelivered: isRedelivered} = executeMessage.fields || {};
54
+ const chr = this.characteristics;
55
+ if (!chr.cardinality && !chr.startCondition && !chr.completionCondition) {
56
+ throw new RunError(`<${this.id}> cardinality, collection, or condition is required in sequential loops`, executeMessage);
57
+ }
45
58
 
46
- function getLoopType() {
47
- if (collectionExpression) return 'collection';
48
- if (completionCondition) return 'complete condition';
49
- if (startCondition) return 'start condition';
50
- if (loopCardinality) return 'cardinality';
59
+ let startIndex = 0;
60
+ if (isRedelivered && executeRoutingKey === 'execute.iteration.next') {
61
+ startIndex = executeMessage.content.index;
51
62
  }
63
+ chr.subscribe(this._onCompleteMessage.bind(this));
52
64
 
53
- function execute(executeMessage) {
54
- if (!executeMessage) throw new Error('LoopCharacteristics execution requires message');
55
- const {routingKey: executeRoutingKey, redelivered: isRedelivered} = executeMessage.fields || {};
56
- const {executionId: parentExecutionId} = executeMessage.content;
57
- if (!getCharacteristics()) return;
65
+ return this._startNext(startIndex, isRedelivered);
66
+ };
58
67
 
59
- try {
60
- return isSequential ? executeSequential() : executeParallel();
61
- } catch (err) {
62
- return activity.emitFatal(new ActivityError(err.message, executeMessage, err), executeMessage.content);
63
- }
68
+ SequentialLoopCharacteristics.prototype._startNext = function startNext(index, ignoreIfExecuting) {
69
+ const chr = this.characteristics;
70
+ const content = chr.next(index);
71
+ if (!content) return;
64
72
 
65
- function executeSequential() {
66
- const {cardinality, getContent: getStartContent} = getCharacteristics();
67
- if (cardinality === 0) return complete();
68
- if (!cardinality && !startCondition && !completionCondition) return activity.emitFatal(new ActivityError(`<${id}> cardinality, collection, or condition is required in sequential loops`, executeMessage), getStartContent());
69
-
70
- let startIndex = 0;
71
-
72
- if (isRedelivered && executeRoutingKey === 'execute.iteration.next') {
73
- startIndex = executeMessage.content.index;
74
- }
75
- subscribe(onCompleteMessage);
76
-
77
- return startNext(startIndex, isRedelivered);
78
-
79
- function startNext(index, ignoreIfExecuting) {
80
- const content = next(index);
81
- if (!content) return;
82
-
83
- const characteristics = getCharacteristics();
84
- if (startCondition && isConditionMet(startCondition, {content})) {
85
- debug(`<${parentExecutionId} (${id})> start condition met`);
86
- return;
87
- }
88
-
89
- debug(`<${content.executionId} (${id})>`, ignoreIfExecuting ? 'resume' : 'start', `sequential iteration index ${content.index}`);
90
- broker.publish('execution', 'execute.iteration.next', {
91
- ...content,
92
- ...characteristics.getContent(),
93
- index,
94
- preventComplete: true,
95
- output: characteristics.output.slice(),
96
- state: 'iteration.next',
97
- });
98
-
99
- broker.publish('execution', 'execute.start', {...content, ignoreIfExecuting});
100
- return content;
101
- }
102
-
103
- function onCompleteMessage(_, message) {
104
- const {content} = message;
105
- const loopOutput = getCharacteristics().output;
106
- if (content.output !== undefined) loopOutput[content.index] = content.output;
107
-
108
- broker.publish('execution', 'execute.iteration.completed', {
109
- ...message.content,
110
- ...getCharacteristics().getContent(),
111
- preventComplete: true,
112
- output: loopOutput.slice(),
113
- state: 'iteration.completed',
114
- });
115
-
116
- if (isConditionMet(completionCondition, message, loopOutput)) {
117
- debug(`<${parentExecutionId} (${id})> complete condition met`);
118
- } else if (startNext(content.index + 1)) return;
119
-
120
- debug(`<${parentExecutionId} (${id})> sequential loop completed`);
121
-
122
- return complete(content);
123
- }
124
-
125
- function complete(content) {
126
- stop();
127
-
128
- const {getContent, output} = getCharacteristics();
129
-
130
- return broker.publish('execution', 'execute.completed', {
131
- ...content,
132
- ...getContent(),
133
- output,
134
- });
135
- }
136
- }
73
+ if (chr.isStartConditionMet({content})) {
74
+ chr.debug('start condition met');
75
+ return;
76
+ }
137
77
 
138
- function executeParallel() {
139
- const {cardinality, getContent: getStartContent} = getCharacteristics();
140
-
141
- if (cardinality === 0) return complete();
142
- if (!cardinality) return activity.emitFatal(new ActivityError(`<${id}> cardinality or collection is required in parallel loops`, executeMessage), getStartContent());
143
-
144
- let index = 0, running = 0;
145
- if (isRedelivered) {
146
- if (!isNaN(executeMessage.content.index)) index = executeMessage.content.index;
147
- if (!isNaN(executeMessage.content.running)) running = executeMessage.content.running;
148
- }
149
- subscribe(onCompleteMessage);
150
-
151
- if (isRedelivered) return;
152
-
153
- return startBatch();
154
-
155
- function startBatch() {
156
- const {output: loopOutput, getContent} = getCharacteristics();
157
- const batch = [];
158
-
159
- let startContent = next(index);
160
- do {
161
- debug(`<${parentExecutionId} (${id})> start parallel iteration index ${index}`);
162
- batch.push(startContent);
163
- running++;
164
- index++;
165
-
166
- if (index >= cardinality || running >= batchSize) {
167
- break;
168
- }
169
- } while ((startContent = next(index)));
170
-
171
- broker.publish('execution', 'execute.iteration.batch', {
172
- ...getContent(),
173
- index,
174
- running,
175
- output: loopOutput,
176
- preventComplete: true,
177
- });
178
-
179
- for (const content of batch) {
180
- broker.publish('execution', 'execute.start', content);
181
- }
182
- }
183
-
184
- function onCompleteMessage(_, message) {
185
- const {content} = message;
186
- const {output: loopOutput} = getCharacteristics();
187
- if (content.output !== undefined) loopOutput[content.index] = content.output;
188
-
189
- running--;
190
-
191
- broker.publish('execution', 'execute.iteration.completed', {
192
- ...content,
193
- ...getCharacteristics().getContent(),
194
- index,
195
- running,
196
- output: loopOutput,
197
- state: 'iteration.completed',
198
- preventComplete: true,
199
- });
200
-
201
- if (running <= 0 && !next(index)) {
202
- return complete(content);
203
- }
204
-
205
- if (isConditionMet(completionCondition, message)) {
206
- return complete(content);
207
- }
208
-
209
- if (running <= 0) {
210
- running = 0;
211
- startBatch();
212
- }
213
- }
214
-
215
- function complete(content) {
216
- stop();
217
-
218
- const {getContent, output} = getCharacteristics();
219
-
220
- return broker.publish('execution', 'execute.completed', {
221
- ...content,
222
- ...getContent(),
223
- output,
224
- });
225
- }
226
- }
78
+ chr.debug(`${ignoreIfExecuting ? 'resume' : 'start'} sequential iteration index ${content.index}`);
79
+ const broker = this.activity.broker;
80
+ broker.publish('execution', 'execute.iteration.next', {
81
+ ...content,
82
+ ...chr.getContent(),
83
+ index,
84
+ preventComplete: true,
85
+ output: chr.output.slice(),
86
+ state: 'iteration.next',
87
+ });
88
+
89
+ broker.publish('execution', 'execute.start', {...content, ignoreIfExecuting});
90
+ return content;
91
+ };
92
+
93
+ SequentialLoopCharacteristics.prototype._onCompleteMessage = function onCompleteMessage(_, message) {
94
+ const {content} = message;
95
+ const chr = this.characteristics;
96
+ const loopOutput = chr.output;
97
+
98
+ if (content.output !== undefined) loopOutput[content.index] = content.output;
99
+
100
+ this.activity.broker.publish('execution', 'execute.iteration.completed', {
101
+ ...message.content,
102
+ ...chr.getContent(),
103
+ preventComplete: true,
104
+ output: loopOutput.slice(),
105
+ state: 'iteration.completed',
106
+ });
107
+
108
+ if (chr.isCompletionConditionMet(message, loopOutput)) {
109
+ chr.debug('complete condition met');
110
+ } else if (this._startNext(content.index + 1)) return;
111
+
112
+ chr.debug('sequential loop completed');
113
+
114
+ return chr.complete(content);
115
+ };
116
+
117
+ function ParallelLoopCharacteristics(activity, characteristics) {
118
+ this.activity = activity;
119
+ this.id = activity.id;
120
+ this.characteristics = characteristics;
121
+ this.running = 0;
122
+ this.index = 0;
123
+ }
227
124
 
228
- function next(index) {
229
- const executionId = `${parentExecutionId}_${index}`;
125
+ ParallelLoopCharacteristics.prototype.execute = function execute(executeMessage) {
126
+ const chr = this.characteristics;
127
+ if (!chr.cardinality) throw new RunError(`<${this.id}> cardinality or collection is required in parallel loops`, executeMessage);
230
128
 
231
- const {cardinality, collection, parent, getContent} = getCharacteristics();
232
- const content = {
233
- ...getContent(),
234
- isRootScope: undefined,
235
- executionId,
236
- isMultiInstance: true,
237
- index,
238
- parent,
239
- };
129
+ const isRedelivered = executeMessage.fields.redelivered;
130
+ if (isRedelivered) {
131
+ if (!isNaN(executeMessage.content.index)) this.index = executeMessage.content.index;
132
+ if (!isNaN(executeMessage.content.running)) this.running = executeMessage.content.running;
133
+ }
134
+ chr.subscribe(this._onCompleteMessage.bind(this));
135
+
136
+ if (isRedelivered) return;
240
137
 
241
- if (isComplete(content)) return;
138
+ return this._startBatch();
139
+ };
242
140
 
243
- if (collection) {
244
- content[elementVariable] = collection[index];
245
- }
141
+ ParallelLoopCharacteristics.prototype._startBatch = function startBatch() {
142
+ const chr = this.characteristics;
143
+ const cardinality = chr.cardinality;
144
+ const batch = [];
246
145
 
247
- return content;
146
+ let startContent = chr.next(this.index);
147
+ do {
148
+ chr.debug(`start parallel iteration index ${this.index}`);
149
+ batch.push(startContent);
150
+ this.running++;
151
+ this.index++;
248
152
 
249
- function isComplete() {
250
- if (cardinality > 0 && index >= cardinality) return true;
251
- if (collection && index >= collection.length) return true;
252
- }
153
+ if (this.index >= cardinality || this.running >= chr.batchSize) {
154
+ break;
253
155
  }
156
+ } while ((startContent = chr.next(this.index)));
157
+
158
+ const broker = this.activity.broker;
159
+ broker.publish('execution', 'execute.iteration.batch', {
160
+ ...chr.getContent(),
161
+ index: this.index,
162
+ running: this.running,
163
+ output: chr.output,
164
+ preventComplete: true,
165
+ });
166
+
167
+ for (const content of batch) {
168
+ broker.publish('execution', 'execute.start', content);
169
+ }
170
+ };
171
+
172
+ ParallelLoopCharacteristics.prototype._onCompleteMessage = function onCompleteMessage(_, message) {
173
+ const chr = this.characteristics;
174
+ const {content} = message;
175
+ if (content.output !== undefined) chr.output[content.index] = content.output;
176
+
177
+ this.running--;
178
+
179
+ this.activity.broker.publish('execution', 'execute.iteration.completed', {
180
+ ...content,
181
+ ...chr.getContent(),
182
+ index: this.index,
183
+ running: this.running,
184
+ output: chr.output,
185
+ state: 'iteration.completed',
186
+ preventComplete: true,
187
+ });
188
+
189
+ if (this.running <= 0 && !chr.next(this.index)) {
190
+ return chr.complete(content);
191
+ }
254
192
 
255
- function getCharacteristics() {
256
- if (loopSettings) return loopSettings;
193
+ if (chr.isCompletionConditionMet(message)) {
194
+ return chr.complete(content);
195
+ }
196
+
197
+ if (this.running <= 0) {
198
+ this.running = 0;
199
+ this._startBatch();
200
+ }
201
+ };
257
202
 
258
- const collection = getCollection();
259
- const cardinality = getCardinality(collection);
203
+ function Characteristics(activity, loopCharacteristics, executeMessage) {
204
+ this.activity = activity;
205
+ const behaviour = this.behaviour = loopCharacteristics.behaviour || {};
206
+ this.message = executeMessage;
260
207
 
261
- const messageContent = {
262
- ...cloneContent(executeMessage.content),
263
- loopCardinality: cardinality,
264
- isSequential,
265
- output: undefined,
266
- };
208
+ const type = this.type = loopCharacteristics.type || 'LoopCharacteristics';
209
+ this.id = activity.id;
210
+ this.broker = activity.broker;
211
+ this.parentExecutionId = executeMessage.content.executionId;
267
212
 
268
- if (cardinality !== undefined && isNaN(cardinality) || cardinality < 0) {
269
- return activity.emitFatal(new ActivityError(`<${id}> invalid loop cardinality >${cardinality}<`, executeMessage), messageContent);
270
- }
213
+ this.isSequential = behaviour.isSequential || false;
214
+ this.output = executeMessage.content.output || [];
215
+ this.parent = unshiftParent(executeMessage.content.parent, executeMessage.content);
271
216
 
272
- const output = executeMessage.content.output || [];
217
+ if ('loopCardinality' in behaviour) this.loopCardinality = behaviour.loopCardinality;
218
+ else if ('loopMaximum' in behaviour) this.loopCardinality = behaviour.loopMaximum;
273
219
 
274
- const parent = unshiftParent(executeMessage.content.parent, executeMessage.content);
220
+ if (behaviour.loopCondition) {
221
+ if (behaviour.testBefore) this.startCondition = behaviour.loopCondition;
222
+ else this.completionCondition = behaviour.loopCondition;
223
+ }
224
+ if (behaviour.completionCondition) {
225
+ this.completionCondition = behaviour.completionCondition;
226
+ }
275
227
 
276
- loopSettings = {
277
- cardinality,
278
- collection,
279
- messageContent,
280
- output,
281
- parent,
282
- getContent() {
283
- return cloneContent(messageContent);
284
- },
285
- };
228
+ const collection = this.collection = this.getCollection();
229
+ if (collection) {
230
+ this.elementVariable = behaviour.elementVariable || 'item';
231
+ }
232
+ this.cardinality = this.getCardinality(collection);
286
233
 
287
- return loopSettings;
288
- }
234
+ this.onApiMessage = this.onApiMessage.bind(this);
289
235
 
290
- function getCardinality(collection) {
291
- const collectionLen = Array.isArray(collection) ? collection.length : undefined;
292
- if (!loopCardinality) {
293
- return collectionLen;
294
- }
295
- const value = environment.resolveExpression(loopCardinality, executeMessage);
296
- if (value === undefined) return collectionLen;
297
- return Number(value);
298
- }
236
+ const environment = activity.environment;
237
+ this.logger = environment.Logger(type.toLowerCase());
238
+ this.batchSize = environment.settings.batchSize || 50;
239
+ }
299
240
 
300
- function getCollection() {
301
- if (!collectionExpression) return;
302
- debug(`<${id}> has collection`);
303
- return environment.resolveExpression(collectionExpression, executeMessage);
304
- }
241
+ Characteristics.prototype.getContent = function getContent() {
242
+ return {
243
+ ...cloneContent(this.message.content),
244
+ loopCardinality: this.cardinality,
245
+ isSequential: this.isSequential,
246
+ output: undefined,
247
+ };
248
+ };
249
+
250
+ Characteristics.prototype.next = function next(index) {
251
+ const cardinality = this.cardinality;
252
+ if (cardinality > 0 && index >= cardinality) return;
253
+
254
+ const collection = this.collection;
255
+ if (collection && index >= collection.length) return;
256
+
257
+ const content = {
258
+ ...this.getContent(),
259
+ isRootScope: undefined,
260
+ executionId: `${this.parentExecutionId}_${index}`,
261
+ isMultiInstance: true,
262
+ parent: cloneParent(this.parent),
263
+ index,
264
+ };
305
265
 
306
- function subscribe(onIterationCompleteMessage) {
307
- broker.subscribeTmp('api', `activity.*.${parentExecutionId}`, onApiMessage, {noAck: true, consumerTag: apiConsumerTag}, {priority: 400});
308
- broker.subscribeTmp('execution', 'execute.*', onComplete, {noAck: true, consumerTag: executeConsumerTag, priority: 300});
309
-
310
- function onComplete(routingKey, message, ...args) {
311
- if (!message.content.isMultiInstance) return;
312
- switch (routingKey) {
313
- case 'execute.cancel':
314
- case 'execute.completed':
315
- return onIterationCompleteMessage(routingKey, message, ...args);
316
- }
317
- }
318
- }
266
+ if (collection) {
267
+ content[this.elementVariable] = collection[index];
319
268
  }
320
269
 
321
- function onApiMessage(_, message) {
322
- switch (message.properties.type) {
323
- case 'stop':
324
- case 'discard':
325
- stop();
326
- break;
270
+ return content;
271
+ };
272
+
273
+ Characteristics.prototype.getCardinality = function getCardinality(collection) {
274
+ const collectionLen = this.collection && Array.isArray(collection) ? collection.length : undefined;
275
+ if (!this.loopCardinality) {
276
+ return collectionLen;
277
+ }
278
+ const value = this.activity.environment.resolveExpression(this.loopCardinality, this.message);
279
+ if (value !== undefined && isNaN(value) || value < 0) {
280
+ throw new RunError(`<${this.id}> invalid loop cardinality >${value}<`, this.message);
281
+ }
282
+ if (value === undefined) return collectionLen;
283
+ return Number(value);
284
+ };
285
+
286
+ Characteristics.prototype.getCollection = function getCollection() {
287
+ const collectionExpression = this.behaviour.collection;
288
+ if (!collectionExpression) return;
289
+ return this.activity.environment.resolveExpression(collectionExpression, this.message);
290
+ };
291
+
292
+ Characteristics.prototype.isStartConditionMet = function isStartConditionMet(message) {
293
+ if (!this.startCondition) return false;
294
+ return this.activity.environment.resolveExpression(this.startCondition, cloneMessage(message));
295
+ };
296
+
297
+ Characteristics.prototype.isCompletionConditionMet = function isCompletionConditionMet(message) {
298
+ if (!this.completionCondition) return false;
299
+ return this.activity.environment.resolveExpression(this.completionCondition, cloneMessage(message, {loopOutput: this.output}));
300
+ };
301
+
302
+ Characteristics.prototype.complete = function complete(content) {
303
+ this.stop();
304
+
305
+ return this.broker.publish('execution', 'execute.completed', {
306
+ ...content,
307
+ ...this.getContent(),
308
+ output: this.output,
309
+ });
310
+ };
311
+
312
+ Characteristics.prototype.subscribe = function subscribe(onIterationCompleteMessage) {
313
+ this.broker.subscribeTmp('api', `activity.*.${this.parentExecutionId}`, this.onApiMessage, {noAck: true, consumerTag: '_api-multi-instance-tag'}, {priority: 400});
314
+ this.broker.subscribeTmp('execution', 'execute.*', onComplete, {noAck: true, consumerTag: '_execute-q-multi-instance-tag', priority: 300});
315
+
316
+ function onComplete(routingKey, message, ...args) {
317
+ if (!message.content.isMultiInstance) return;
318
+ switch (routingKey) {
319
+ case 'execute.cancel':
320
+ case 'execute.completed':
321
+ return onIterationCompleteMessage(routingKey, message, ...args);
327
322
  }
328
323
  }
329
-
330
- function stop() {
331
- broker.cancel(executeConsumerTag);
332
- broker.cancel(apiConsumerTag);
324
+ };
325
+
326
+ Characteristics.prototype.onApiMessage = function onApiMessage(_, message) {
327
+ switch (message.properties.type) {
328
+ case 'stop':
329
+ case 'discard':
330
+ this.stop();
331
+ break;
333
332
  }
333
+ };
334
334
 
335
- function isConditionMet(condition, message, loopOutput) {
336
- if (!condition) return false;
337
- const testContext = cloneMessage(message, {loopOutput});
338
- return environment.resolveExpression(condition, testContext);
339
- }
340
- }
335
+ Characteristics.prototype.stop = function stop() {
336
+ this.broker.cancel('_execute-q-multi-instance-tag');
337
+ this.broker.cancel('_api-multi-instance-tag');
338
+ };
341
339
 
340
+ Characteristics.prototype.debug = function debug(msg) {
341
+ this.logger.debug(`<${this.parentExecutionId} (${this.id})> ${msg}`);
342
+ };