bpmnlint-plugin-camunda-compat 2.20.2 → 2.21.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 (52) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +39 -39
  3. package/index.js +224 -223
  4. package/package.json +53 -53
  5. package/rules/camunda-cloud/called-element.js +42 -42
  6. package/rules/camunda-cloud/collapsed-subprocess.js +40 -40
  7. package/rules/camunda-cloud/connector-properties/config.js +90 -90
  8. package/rules/camunda-cloud/connector-properties/index.js +47 -47
  9. package/rules/camunda-cloud/duplicate-task-headers.js +58 -58
  10. package/rules/camunda-cloud/element-type/config.js +66 -66
  11. package/rules/camunda-cloud/element-type/index.js +133 -133
  12. package/rules/camunda-cloud/error-reference.js +71 -71
  13. package/rules/camunda-cloud/escalation-boundary-event-attached-to-ref.js +48 -48
  14. package/rules/camunda-cloud/escalation-reference.js +66 -66
  15. package/rules/camunda-cloud/event-based-gateway-target.js +38 -38
  16. package/rules/camunda-cloud/executable-process.js +61 -61
  17. package/rules/camunda-cloud/feel.js +82 -82
  18. package/rules/camunda-cloud/implementation/config.js +16 -16
  19. package/rules/camunda-cloud/implementation/index.js +218 -218
  20. package/rules/camunda-cloud/inclusive-gateway.js +35 -35
  21. package/rules/camunda-cloud/link-event.js +142 -142
  22. package/rules/camunda-cloud/loop-characteristics.js +66 -66
  23. package/rules/camunda-cloud/message-reference.js +60 -60
  24. package/rules/camunda-cloud/no-candidate-users.js +38 -38
  25. package/rules/camunda-cloud/no-expression.js +173 -173
  26. package/rules/camunda-cloud/no-loop.js +316 -145
  27. package/rules/camunda-cloud/no-multiple-none-start-events.js +41 -41
  28. package/rules/camunda-cloud/no-propagate-all-parent-variables.js +44 -44
  29. package/rules/camunda-cloud/no-signal-event-sub-process.js +45 -45
  30. package/rules/camunda-cloud/no-task-schedule.js +18 -18
  31. package/rules/camunda-cloud/no-template.js +23 -23
  32. package/rules/camunda-cloud/no-zeebe-properties.js +18 -18
  33. package/rules/camunda-cloud/no-zeebe-user-task.js +27 -27
  34. package/rules/camunda-cloud/secrets.js +119 -119
  35. package/rules/camunda-cloud/sequence-flow-condition.js +56 -56
  36. package/rules/camunda-cloud/signal-reference.js +64 -64
  37. package/rules/camunda-cloud/start-event-form.js +97 -97
  38. package/rules/camunda-cloud/subscription.js +65 -65
  39. package/rules/camunda-cloud/task-schedule.js +67 -67
  40. package/rules/camunda-cloud/timer/config.js +46 -46
  41. package/rules/camunda-cloud/timer/index.js +183 -183
  42. package/rules/camunda-cloud/user-task-definition.js +24 -24
  43. package/rules/camunda-cloud/user-task-form.js +142 -142
  44. package/rules/camunda-cloud/wait-for-completion.js +46 -46
  45. package/rules/camunda-platform/history-time-to-live.js +19 -19
  46. package/rules/utils/cron.js +95 -95
  47. package/rules/utils/element.js +484 -484
  48. package/rules/utils/error-types.js +24 -24
  49. package/rules/utils/iso8601.js +52 -52
  50. package/rules/utils/reporter.js +37 -37
  51. package/rules/utils/rule.js +46 -46
  52. package/rules/utils/version.js +4 -4
@@ -1,146 +1,317 @@
1
- const { isString } = require('min-dash');
2
-
3
- const { is } = require('bpmnlint-utils');
4
-
5
- const {
6
- findExtensionElement,
7
- findParent,
8
- isAnyExactly
9
- } = require('../utils/element');
10
-
11
- const { reportErrors } = require('../utils/reporter');
12
-
13
- const { ERROR_TYPES } = require('../utils/error-types');
14
-
15
- const { skipInNonExecutableProcess } = require('../utils/rule');
16
-
17
- const LOOP_REQUIRED_ELEMENT_TYPES = [
18
- 'bpmn:CallActivity',
19
- 'bpmn:ManualTask',
20
- 'bpmn:Task'
21
- ];
22
-
23
- const LOOP_ELEMENT_TYPES = [
24
- ...LOOP_REQUIRED_ELEMENT_TYPES,
25
- 'bpmn:StartEvent',
26
- 'bpmn:EndEvent',
27
- 'bpmn:ManualTask',
28
- 'bpmn:ExclusiveGateway',
29
- 'bpmn:InclusiveGateway',
30
- 'bpmn:ParallelGateway',
31
- 'bpmn:SubProcess',
32
- 'bpmn:Task'
33
- ];
34
-
35
- module.exports = skipInNonExecutableProcess(function() {
36
- function check(node, reporter) {
37
- if (!is(node, 'bpmn:Process')) {
38
- return;
39
- }
40
-
41
- const error = getFlowElements(node)
42
- .filter(flowElement => {
43
- return isAnyExactly(flowElement, LOOP_ELEMENT_TYPES);
44
- })
45
- .reduce((error, flowElement) => {
46
- return error || findLoop(flowElement, node);
47
- }, null);
48
-
49
- if (error) {
50
- reportErrors(node, reporter, error);
51
- }
52
- }
53
-
54
- return {
55
- check
56
- };
57
- });
58
-
59
- function findLoop(flowElement, parentElement, visitedFlowElements = []) {
60
-
61
- // (1.1) is not a loop
62
- if (!isAnyExactly(flowElement, LOOP_ELEMENT_TYPES)) {
63
- return null;
64
- }
65
-
66
- const nextFlowElements = getNextFlowElements(flowElement);
67
-
68
- // (1.2) is not a loop
69
- if (!nextFlowElements.length) {
70
- return null;
71
- }
72
-
73
- // (2) may be a loop
74
- if (!visitedFlowElements.includes(flowElement)) {
75
- return nextFlowElements.reduce((error, nextFlowElement) => {
76
- return error || findLoop(nextFlowElement, parentElement, [ ...visitedFlowElements, flowElement ]);
77
- }, null);
78
- }
79
-
80
- // (3) is a loop but ignored
81
- if (isIgnoredLoop(visitedFlowElements)) {
82
- return null;
83
- }
84
-
85
- const elements = visitedFlowElements.slice(visitedFlowElements.indexOf(flowElement));
86
-
87
- // (4) is a loop
88
- return {
89
- message: `Loop detected: ${ elements.map(({ id }) => id).join(' -> ') } -> ${ flowElement.id }`,
90
- path: null,
91
- data: {
92
- type: ERROR_TYPES.LOOP_NOT_ALLOWED,
93
- node: parentElement,
94
- parentNode: null,
95
- elements: elements.map(({ id }) => id)
96
- }
97
- };
98
- }
99
-
100
- function getFlowElements(node) {
101
- return node.get('flowElements').reduce((flowElements, flowElement) => {
102
- if (is(flowElement, 'bpmn:FlowElementsContainer')) {
103
- return [ ...flowElements, ...getFlowElements(flowElement) ];
104
- }
105
-
106
- return [ ...flowElements, flowElement ];
107
- }, []);
108
- }
109
-
110
- function getNextFlowElements(flowElement) {
111
- if (is(flowElement, 'bpmn:CallActivity')) {
112
- const calledElement = findExtensionElement(flowElement, 'zeebe:CalledElement');
113
-
114
- if (calledElement) {
115
- const processId = calledElement.get('processId');
116
-
117
- if (isString(processId) && !isFeel(processId)) {
118
- const process = findParent(flowElement, 'bpmn:Process');
119
-
120
- if (process && process.get('id') === processId) {
121
- return process.get('flowElements').filter(flowElement => is(flowElement, 'bpmn:StartEvent'));
122
- }
123
- }
124
- }
125
- } else if (is(flowElement, 'bpmn:SubProcess')) {
126
- return flowElement
127
- .get('flowElements').filter(flowElement => is(flowElement, 'bpmn:StartEvent'));
128
- } else if (is(flowElement, 'bpmn:EndEvent')) {
129
- const parent = flowElement.$parent;
130
-
131
- if (is(parent, 'bpmn:SubProcess')) {
132
- flowElement = parent;
133
- }
134
- }
135
-
136
- return flowElement
137
- .get('outgoing').filter(outgoing => is(outgoing, 'bpmn:SequenceFlow')).map(sequenceFlow => sequenceFlow.get('targetRef'));
138
- }
139
-
140
- function isIgnoredLoop(elements) {
141
- return !elements.some(element => isAnyExactly(element, LOOP_REQUIRED_ELEMENT_TYPES));
142
- }
143
-
144
- function isFeel(value) {
145
- return isString(value) && value.startsWith('=');
1
+ const { isString } = require('min-dash');
2
+
3
+ const { is } = require('bpmnlint-utils');
4
+
5
+ const {
6
+ findExtensionElement,
7
+ findParent,
8
+ isAnyExactly
9
+ } = require('../utils/element');
10
+
11
+ const { reportErrors } = require('../utils/reporter');
12
+
13
+ const { ERROR_TYPES } = require('../utils/error-types');
14
+
15
+ const { skipInNonExecutableProcess } = require('../utils/rule');
16
+
17
+ /**
18
+ * @typedef {import('bpmn-moddle').BaseElement} ModdleElement
19
+ **/
20
+
21
+ const LOOP_REQUIRED_ELEMENT_TYPES = [
22
+ 'bpmn:CallActivity',
23
+ 'bpmn:ManualTask',
24
+ 'bpmn:Task'
25
+ ];
26
+
27
+ const LOOP_ELEMENT_TYPES = [
28
+ ...LOOP_REQUIRED_ELEMENT_TYPES,
29
+ 'bpmn:StartEvent',
30
+ 'bpmn:EndEvent',
31
+ 'bpmn:ManualTask',
32
+ 'bpmn:ExclusiveGateway',
33
+ 'bpmn:InclusiveGateway',
34
+ 'bpmn:ParallelGateway',
35
+ 'bpmn:SubProcess',
36
+ 'bpmn:Task'
37
+ ];
38
+
39
+ module.exports = skipInNonExecutableProcess(function() {
40
+ function check(node, reporter) {
41
+ if (!is(node, 'bpmn:Process')) {
42
+ return;
43
+ }
44
+
45
+ // 1. Remove all elements that can be part of an infinite loop
46
+ const relevantNodes = getFlowElements(node)
47
+ .filter(flowElement => {
48
+ return isAnyExactly(flowElement, LOOP_ELEMENT_TYPES);
49
+ });
50
+
51
+ // 2. Remove all non-required elements. This produces a graph that only contains the required elements,
52
+ // with annotated edges that preserve the original path.
53
+ // Any loop found within the simplified graph is a valid loop, as all Vertices in the graph are `LOOP_REQUIRED_ELEMENT_TYPES`.
54
+ const minimalGraph = simplifyGraph(relevantNodes);
55
+
56
+ // 3. Use breadth-first search to find loops in the simplified Graph.
57
+ const errors = findLoops(minimalGraph, node);
58
+
59
+ if (errors) {
60
+ reportErrors(node, reporter, errors);
61
+ }
62
+ }
63
+
64
+ return {
65
+ check
66
+ };
67
+ });
68
+
69
+ /**
70
+ * @typedef {Object} GraphNode
71
+ * @property {ModdleElement} element the bpmn element this node represents
72
+ * @property {Map<ModdleElement, Array<ModdleElement>>} incoming Maps the target node with the shortest path to it
73
+ * @property {Map<ModdleElement, Array<ModdleElement>>} outgoing Maps the source node with the shortest path to it
74
+ */
75
+
76
+ /**
77
+ * Simplifies the graph by removing all non-`LOOP_REQUIRED_ELEMENT_TYPES` elements and connecting incoming and outgoing nodes directly.
78
+ * Annotates the edges with the original path. Uses breadth-first search to find paths.
79
+ *
80
+ * @param {Array<ModdleElement>} flowElements
81
+ * @returns {Map<ModdleElement, GraphNode>}
82
+ */
83
+ function simplifyGraph(flowElements) {
84
+
85
+ // Transform Array<ModdleElement> into Map<ModdleElement, GraphNode>
86
+ const graph = elementsToGraph(flowElements);
87
+
88
+ breadthFirstSearch(graph, (node) => {
89
+ const { element, outgoing } = node;
90
+
91
+ // Remove non-required element and connect incoming and outgoing nodes directly
92
+ if (!isAnyExactly(element, LOOP_REQUIRED_ELEMENT_TYPES)) {
93
+ connectNodes(graph, node);
94
+ }
95
+
96
+ return [ ...outgoing.keys() ].map(key => graph.get(key));
97
+ });
98
+
99
+ // Clean up all references to removed elements
100
+ graph.forEach(({ incoming, outgoing }) => {
101
+ incoming.forEach((_, key) => {
102
+ if (!graph.has(key)) {
103
+ incoming.delete(key);
104
+ }
105
+ });
106
+
107
+ outgoing.forEach((_, key) => {
108
+ if (!graph.has(key)) {
109
+ outgoing.delete(key);
110
+ }
111
+ });
112
+ });
113
+
114
+ return graph;
115
+ }
116
+
117
+
118
+ /**
119
+ * Uses breadth-first search to find loops in the graph and generate errors.
120
+ *
121
+ * @param {Map<ModdleElement, GraphNode>} graph The simplified graph containing only required elements
122
+ * @param {ModdleElement} root used for reporting the errors
123
+ * @returns {Array<Object>} errors
124
+ */
125
+ function findLoops(graph, root) {
126
+ const errors = [];
127
+
128
+ // Traverse graph using breadth-first search, remembering the path. If we find a loop, report it.
129
+ breadthFirstSearch(graph, (node) => {
130
+ const { element, outgoing, path = [] } = node;
131
+
132
+ const nextElements = [ ];
133
+ outgoing.forEach((connectionPath, nextElement) => {
134
+ const newPath = [ ...path, element, ...connectionPath ];
135
+
136
+ // We already visited this node, we found a loop
137
+ if (newPath.includes(nextElement)) {
138
+ errors.push(handleLoop(newPath, nextElement, root));
139
+ } else {
140
+ const nextNode = graph.get(nextElement);
141
+ nextNode.path = nextNode.path || newPath;
142
+ nextElements.push(nextNode);
143
+ }
144
+ });
145
+
146
+ return nextElements;
147
+ });
148
+
149
+ return errors.filter(Boolean);
150
+ }
151
+
152
+ const handleLoop = (path, currentNode, root) => {
153
+ const loop = path.slice(path.indexOf(currentNode));
154
+
155
+ if (isIgnoredLoop(loop)) {
156
+ return null;
157
+ }
158
+
159
+ return {
160
+ message: `Loop detected: ${ loop.map(({ id }) => id).join(' -> ') } -> ${ currentNode.id }`,
161
+ path: null,
162
+ data: {
163
+ type: ERROR_TYPES.LOOP_NOT_ALLOWED,
164
+ node: root,
165
+ parentNode: null,
166
+ elements: loop.map(({ id }) => id)
167
+ }
168
+ };
169
+ };
170
+
171
+ function getFlowElements(node) {
172
+ return node.get('flowElements').reduce((flowElements, flowElement) => {
173
+ if (is(flowElement, 'bpmn:FlowElementsContainer')) {
174
+ return [ ...flowElements, flowElement, ...getFlowElements(flowElement) ];
175
+ }
176
+
177
+ return [ ...flowElements, flowElement ];
178
+ }, []);
179
+ }
180
+
181
+ function getNextFlowElements(flowElement) {
182
+ if (is(flowElement, 'bpmn:CallActivity')) {
183
+ const calledElement = findExtensionElement(flowElement, 'zeebe:CalledElement');
184
+
185
+ if (calledElement) {
186
+ const processId = calledElement.get('processId');
187
+
188
+ if (isString(processId) && !isFeel(processId)) {
189
+ const process = findParent(flowElement, 'bpmn:Process');
190
+
191
+ if (process && process.get('id') === processId) {
192
+ return process.get('flowElements').filter(flowElement => is(flowElement, 'bpmn:StartEvent'));
193
+ }
194
+ }
195
+ }
196
+ } else if (is(flowElement, 'bpmn:SubProcess')) {
197
+ return flowElement
198
+ .get('flowElements').filter(flowElement => is(flowElement, 'bpmn:StartEvent'));
199
+ } else if (is(flowElement, 'bpmn:EndEvent')) {
200
+ const parent = flowElement.$parent;
201
+
202
+ if (is(parent, 'bpmn:SubProcess')) {
203
+ flowElement = parent;
204
+ }
205
+ }
206
+
207
+ return flowElement
208
+ .get('outgoing').filter(outgoing => is(outgoing, 'bpmn:SequenceFlow')).map(sequenceFlow => sequenceFlow.get('targetRef'));
209
+ }
210
+
211
+ function isIgnoredLoop(elements) {
212
+ return !elements.some(element => isAnyExactly(element, LOOP_REQUIRED_ELEMENT_TYPES));
213
+ }
214
+
215
+ function isFeel(value) {
216
+ return isString(value) && value.startsWith('=');
217
+ }
218
+
219
+ const getOrSet = (map, key, defaultValue) => {
220
+ if (!map.has(key)) {
221
+ map.set(key, defaultValue);
222
+ }
223
+
224
+ return map.get(key);
225
+ };
226
+
227
+ const setIfAbsent = (map, key, value) => {
228
+ map.has(key) || map.set(key, value);
229
+ };
230
+
231
+ /**
232
+ * Transform Array of flow elements into a Graph structure, adding implicit connections (e.g. SubProcess -> StartEvent)
233
+ * via `getNextFlowElements`.
234
+ *
235
+ * @param {Array<ModdleElement>} flowElements
236
+ * @returns Map<ModdleElement, GraphNode>
237
+ */
238
+ function elementsToGraph(flowElements) {
239
+ return flowElements.reduce((currentMap, element) => {
240
+ const currentNode = getOrSet(currentMap, element, {
241
+ element,
242
+ incoming: new Map(),
243
+ outgoing: new Map(),
244
+ });
245
+
246
+ const nextFlowElements = getNextFlowElements(element);
247
+
248
+ nextFlowElements.forEach(nextElement => {
249
+ const nextNode = getOrSet(currentMap, nextElement, {
250
+ element: nextElement,
251
+ incoming: new Map(),
252
+ outgoing: new Map(),
253
+ });
254
+
255
+ nextNode.incoming.set(element, []);
256
+ currentNode.outgoing.set(nextElement, []);
257
+ });
258
+
259
+ return currentMap;
260
+ }, new Map());
261
+ }
262
+
263
+ /**
264
+ * Connects incoming and outgoing nodes directly, add current node to the path and remove node from graph.
265
+ */
266
+ function connectNodes(graph, node) {
267
+ const { element, incoming, outgoing } = node;
268
+
269
+ incoming.forEach((fromPath, fromKey) => {
270
+ outgoing.forEach((toPath, toKey) => {
271
+ const fromNode = graph.get(fromKey);
272
+ const toNode = graph.get(toKey);
273
+
274
+ if (!fromNode || !toNode) {
275
+ return;
276
+ }
277
+
278
+ // We only care about the shortest path, so we don't need to update the path if it's already set
279
+ setIfAbsent(fromNode.outgoing, toKey, [ ...fromPath, element, ...toPath ]);
280
+ setIfAbsent(toNode.incoming, fromKey, [ ...fromPath, element, ...toPath ]);
281
+ });
282
+ });
283
+
284
+ graph.delete(element);
285
+ }
286
+
287
+ /**
288
+ * Iterates over all nodes in the graph using breadth-first search.
289
+ *
290
+ * @param {Map<ModdleElement, GraphNode>} graph
291
+ * @param {Function} iterationCallback
292
+ */
293
+ function breadthFirstSearch(graph, iterationCallback) {
294
+ const unvisited = new Set(graph.values());
295
+
296
+ while (unvisited.size) {
297
+ let firstElement = unvisited.values().next().value;
298
+ unvisited.delete(firstElement);
299
+
300
+ const elementsToVisit = [ firstElement ];
301
+
302
+ while (elementsToVisit.length) {
303
+ const node = elementsToVisit.shift();
304
+
305
+ const nextElements = iterationCallback(node);
306
+
307
+ nextElements.forEach(nextElement => {
308
+ if (!unvisited.has(nextElement)) {
309
+ return;
310
+ }
311
+
312
+ unvisited.delete(nextElement);
313
+ elementsToVisit.push(nextElement);
314
+ });
315
+ }
316
+ }
146
317
  }
@@ -1,41 +1,41 @@
1
- const { is } = require('bpmnlint-utils');
2
-
3
- const { reportErrors } = require('../utils/reporter');
4
-
5
- const { ERROR_TYPES } = require('../utils/element');
6
-
7
- const { skipInNonExecutableProcess } = require('../utils/rule');
8
-
9
- module.exports = skipInNonExecutableProcess(function() {
10
- function check(node, reporter) {
11
- if (!is(node, 'bpmn:Process')) {
12
- return;
13
- }
14
-
15
- const flowElements = node.get('flowElements') || [];
16
-
17
- const noneStartEvents = flowElements.filter(flowElement => {
18
- return is(flowElement, 'bpmn:StartEvent') && !flowElement.get('eventDefinitions').length;
19
- });
20
-
21
- if (noneStartEvents.length <= 1) {
22
- return;
23
- }
24
-
25
- noneStartEvents.forEach(startEvent => {
26
- reportErrors(startEvent, reporter, {
27
- message: 'Multiple elements of type <bpmn:StartEvent> with no event definition not allowed as children of <bpmn:Process>',
28
- path: null,
29
- data: {
30
- type: ERROR_TYPES.ELEMENT_MULTIPLE_NOT_ALLOWED,
31
- node: startEvent,
32
- parent: null
33
- }
34
- });
35
- });
36
- }
37
-
38
- return {
39
- check
40
- };
41
- });
1
+ const { is } = require('bpmnlint-utils');
2
+
3
+ const { reportErrors } = require('../utils/reporter');
4
+
5
+ const { ERROR_TYPES } = require('../utils/element');
6
+
7
+ const { skipInNonExecutableProcess } = require('../utils/rule');
8
+
9
+ module.exports = skipInNonExecutableProcess(function() {
10
+ function check(node, reporter) {
11
+ if (!is(node, 'bpmn:Process')) {
12
+ return;
13
+ }
14
+
15
+ const flowElements = node.get('flowElements') || [];
16
+
17
+ const noneStartEvents = flowElements.filter(flowElement => {
18
+ return is(flowElement, 'bpmn:StartEvent') && !flowElement.get('eventDefinitions').length;
19
+ });
20
+
21
+ if (noneStartEvents.length <= 1) {
22
+ return;
23
+ }
24
+
25
+ noneStartEvents.forEach(startEvent => {
26
+ reportErrors(startEvent, reporter, {
27
+ message: 'Multiple elements of type <bpmn:StartEvent> with no event definition not allowed as children of <bpmn:Process>',
28
+ path: null,
29
+ data: {
30
+ type: ERROR_TYPES.ELEMENT_MULTIPLE_NOT_ALLOWED,
31
+ node: startEvent,
32
+ parent: null
33
+ }
34
+ });
35
+ });
36
+ }
37
+
38
+ return {
39
+ check
40
+ };
41
+ });
@@ -1,45 +1,45 @@
1
- const { is } = require('bpmnlint-utils');
2
-
3
- const {
4
- findExtensionElement,
5
- hasProperties
6
- } = require('../utils/element');
7
-
8
- const { reportErrors } = require('../utils/reporter');
9
-
10
- const { skipInNonExecutableProcess } = require('../utils/rule');
11
-
12
- module.exports = skipInNonExecutableProcess(function() {
13
- function check(node, reporter) {
14
- if (!is(node, 'bpmn:CallActivity')) {
15
- return;
16
- }
17
-
18
- const calledElement = findExtensionElement(node, 'zeebe:CalledElement');
19
-
20
- if (!calledElement) {
21
- return;
22
- }
23
-
24
- const errors = hasProperties(calledElement, {
25
- propagateAllParentVariables: {
26
- allowed: function(value) {
27
-
28
- // `propergateAllParentVariables` is not recognized by Camunda 8.1 and older
29
- // setting it to `true` is therefore allowed for all versions
30
- // setting it to `false` is only allowed for Camunda 8.2 and newer
31
- return value;
32
- },
33
- allowedVersion: '8.2'
34
- }
35
- }, node);
36
-
37
- if (errors && errors.length) {
38
- reportErrors(node, reporter, errors);
39
- }
40
- }
41
-
42
- return {
43
- check
44
- };
1
+ const { is } = require('bpmnlint-utils');
2
+
3
+ const {
4
+ findExtensionElement,
5
+ hasProperties
6
+ } = require('../utils/element');
7
+
8
+ const { reportErrors } = require('../utils/reporter');
9
+
10
+ const { skipInNonExecutableProcess } = require('../utils/rule');
11
+
12
+ module.exports = skipInNonExecutableProcess(function() {
13
+ function check(node, reporter) {
14
+ if (!is(node, 'bpmn:CallActivity')) {
15
+ return;
16
+ }
17
+
18
+ const calledElement = findExtensionElement(node, 'zeebe:CalledElement');
19
+
20
+ if (!calledElement) {
21
+ return;
22
+ }
23
+
24
+ const errors = hasProperties(calledElement, {
25
+ propagateAllParentVariables: {
26
+ allowed: function(value) {
27
+
28
+ // `propergateAllParentVariables` is not recognized by Camunda 8.1 and older
29
+ // setting it to `true` is therefore allowed for all versions
30
+ // setting it to `false` is only allowed for Camunda 8.2 and newer
31
+ return value;
32
+ },
33
+ allowedVersion: '8.2'
34
+ }
35
+ }, node);
36
+
37
+ if (errors && errors.length) {
38
+ reportErrors(node, reporter, errors);
39
+ }
40
+ }
41
+
42
+ return {
43
+ check
44
+ };
45
45
  });