bpmnlint-plugin-camunda-compat 2.20.1 → 2.21.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 (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 -93
  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 +145 -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,485 +1,485 @@
1
- const {
2
- isArray,
3
- isDefined,
4
- isFunction,
5
- isNil,
6
- isObject,
7
- isString,
8
- isUndefined,
9
- some
10
- } = require('min-dash');
11
-
12
- const {
13
- is,
14
- isAny
15
- } = require('bpmnlint-utils');
16
-
17
- const { getPath } = require('@bpmn-io/moddle-utils');
18
-
19
- const { ERROR_TYPES } = require('./error-types');
20
-
21
- module.exports.ERROR_TYPES = ERROR_TYPES;
22
-
23
- function getEventDefinition(node) {
24
- const eventDefinitions = node.get('eventDefinitions');
25
-
26
- if (eventDefinitions) {
27
- return eventDefinitions[ 0 ];
28
- }
29
- }
30
-
31
- module.exports.getEventDefinition = getEventDefinition;
32
-
33
- module.exports.getMessageEventDefinition = function(node) {
34
- if (is(node, 'bpmn:ReceiveTask')) {
35
- return node;
36
- }
37
-
38
- return getEventDefinition(node);
39
- };
40
-
41
- function findExtensionElements(node, types) {
42
- const extensionElements = node.get('extensionElements');
43
-
44
- if (!extensionElements) {
45
- return;
46
- }
47
-
48
- const values = extensionElements.get('values');
49
-
50
- if (!values || !values.length) {
51
- return;
52
- }
53
-
54
- if (!isArray(types)) {
55
- types = [ types ];
56
- }
57
-
58
- return values.filter(value => isAny(value, types));
59
- }
60
-
61
- module.exports.findExtensionElements = findExtensionElements;
62
-
63
- function findExtensionElement(node, types) {
64
- const extensionElements = findExtensionElements(node, types);
65
-
66
- if (extensionElements && extensionElements.length) {
67
- return extensionElements[ 0 ];
68
- }
69
- }
70
-
71
- module.exports.findExtensionElement = findExtensionElement;
72
-
73
- function formatNames(names, exclusive = false) {
74
- return names.reduce((string, name, index) => {
75
-
76
- // first
77
- if (index === 0) {
78
- return `<${ name }>`;
79
- }
80
-
81
- // last
82
- if (index === names.length - 1) {
83
- return `${ string } ${ exclusive ? 'or' : 'and' } <${ name }>`;
84
- }
85
-
86
- return `${ string }, <${ name }>`;
87
- }, '');
88
- }
89
-
90
- module.exports.formatNames = formatNames;
91
-
92
- module.exports.hasDuplicatedPropertyValues = function(node, propertiesName, propertyName, parentNode = null) {
93
- const properties = node.get(propertiesName);
94
-
95
- const propertyValues = properties.map(property => property.get(propertyName));
96
-
97
- // (1) find duplicates
98
- const duplicates = propertyValues.reduce((duplicates, propertyValue, index) => {
99
- if (propertyValues.indexOf(propertyValue) !== index && !duplicates.includes(propertyValue)) {
100
- return [
101
- ...duplicates,
102
- propertyValue
103
- ];
104
- }
105
-
106
- return duplicates;
107
- }, []);
108
-
109
- // (2) report error for each duplicate
110
- if (duplicates.length) {
111
- return duplicates.map(duplicate => {
112
-
113
- // (3) find properties with duplicate
114
- const duplicateProperties = properties.filter(property => property.get(propertyName) === duplicate);
115
-
116
- // (4) report error
117
- return {
118
- message: `Properties of type <${ duplicateProperties[ 0 ].$type }> have property <${ propertyName }> with duplicate value of <${ duplicate }>`,
119
- path: null,
120
- data: {
121
- type: ERROR_TYPES.PROPERTY_VALUE_DUPLICATED,
122
- node,
123
- parentNode: parentNode == node ? null : parentNode,
124
- duplicatedProperty: propertyName,
125
- duplicatedPropertyValue: duplicate,
126
- properties: duplicateProperties,
127
- propertiesName
128
- }
129
- };
130
- });
131
- }
132
-
133
- return [];
134
- };
135
-
136
- module.exports.hasProperties = function(node, properties, parentNode = null) {
137
- return Object.entries(properties).reduce((results, property) => {
138
- const [ propertyName, propertyChecks ] = property;
139
-
140
- const { allowedVersion = null } = propertyChecks;
141
-
142
- const path = getPath(node, parentNode);
143
-
144
- const propertyValue = node.get(propertyName);
145
-
146
- if (propertyChecks.required && isEmptyValue(propertyValue)) {
147
- return [
148
- ...results,
149
- {
150
- message: allowedVersion
151
- ? `Element of type <${ node.$type }> without property <${ propertyName }> only allowed by Camunda ${ allowedVersion } or newer`
152
- : `Element of type <${ node.$type }> must have property <${ propertyName }>`,
153
- path: path
154
- ? [ ...path, propertyName ]
155
- : [ propertyName ],
156
- data: addAllowedVersion({
157
- type: ERROR_TYPES.PROPERTY_REQUIRED,
158
- node,
159
- parentNode: parentNode == node ? null : parentNode,
160
- requiredProperty: propertyName
161
- }, allowedVersion)
162
- }
163
- ];
164
- }
165
-
166
- if (propertyChecks.dependentRequired) {
167
- const dependency = node.get(propertyChecks.dependentRequired);
168
-
169
- if (dependency && isEmptyValue(propertyValue)) {
170
- return [
171
- ...results,
172
- {
173
- message: `Element of type <${ node.$type }> must have property <${ propertyName }> if it has property <${ propertyChecks.dependentRequired }>`,
174
- path: path
175
- ? [ ...path, propertyName ]
176
- : [ propertyName ],
177
- data: {
178
- type: ERROR_TYPES.PROPERTY_DEPENDENT_REQUIRED,
179
- node,
180
- parentNode: parentNode == node ? null : parentNode,
181
- property: propertyChecks.dependentRequired,
182
- dependentRequiredProperty: propertyName
183
- }
184
- }
185
- ];
186
- }
187
- }
188
-
189
- if (
190
- propertyChecks.type
191
- && propertyValue
192
- && (
193
- !propertyValue.$instanceOf
194
- || (!isArray(propertyChecks.type) && !propertyValue.$instanceOf(propertyChecks.type))
195
- || (isArray(propertyChecks.type) && !some(propertyChecks.type, type => propertyValue.$instanceOf(type)))
196
- )
197
- ) {
198
- return [
199
- ...results,
200
- {
201
- message: allowedVersion
202
- ? `Property <${ propertyName }> of type <${ propertyValue.$type }> only allowed by Camunda ${ allowedVersion } or newer`
203
- : `Property <${ propertyName }> of type <${ propertyValue.$type }> not allowed`,
204
- path: path
205
- ? [ ...path, propertyName ]
206
- : [ propertyName ],
207
- data: addAllowedVersion({
208
- type: ERROR_TYPES.PROPERTY_TYPE_NOT_ALLOWED,
209
- node,
210
- parentNode: parentNode == node ? null : parentNode,
211
- property: propertyName,
212
- allowedPropertyType: propertyChecks.type
213
- }, allowedVersion)
214
- }
215
- ];
216
- }
217
-
218
- if ('value' in propertyChecks && propertyChecks.value !== propertyValue) {
219
- return [
220
- ...results,
221
- {
222
- message: `Property <${ propertyName }> must have value of <${ propertyChecks.value }>`,
223
- path: path
224
- ? [ ...path, propertyName ]
225
- : [ propertyName ],
226
- data: {
227
- type: ERROR_TYPES.PROPERTY_VALUE_REQUIRED,
228
- node,
229
- parentNode: parentNode == node ? null : parentNode,
230
- property: propertyName,
231
- requiredValue: propertyChecks.value
232
- }
233
- }
234
- ];
235
- }
236
-
237
- if (propertyChecks.allowed === false && isDefined(propertyValue) && !isNil(propertyValue)) {
238
- return [
239
- ...results,
240
- {
241
- message: allowedVersion
242
- ? `Property <${ propertyName }> only allowed by Camunda ${ allowedVersion } or newer`
243
- : `Property <${ propertyName }> not allowed`,
244
- path: path
245
- ? [ ...path, propertyName ]
246
- : [ propertyName ],
247
- data: addAllowedVersion({
248
- type: ERROR_TYPES.PROPERTY_NOT_ALLOWED,
249
- node,
250
- parentNode: parentNode == node ? null : parentNode,
251
- property: propertyName
252
- }, allowedVersion)
253
- }
254
- ];
255
- }
256
-
257
- if (isFunction(propertyChecks.allowed) && !propertyChecks.allowed(propertyValue)) {
258
- return [
259
- ...results,
260
- {
261
- message: allowedVersion
262
- ? `Property value of <${ truncate(propertyValue) }> only allowed by Camunda ${ allowedVersion } or newer`
263
- : `Property value of <${ truncate(propertyValue) }> not allowed`,
264
- path: path
265
- ? [ ...path, propertyName ]
266
- : [ propertyName ],
267
- data: addAllowedVersion({
268
- type: ERROR_TYPES.PROPERTY_VALUE_NOT_ALLOWED,
269
- node,
270
- parentNode: parentNode == node ? null : parentNode,
271
- property: propertyName
272
- }, allowedVersion)
273
- }
274
- ];
275
- }
276
-
277
- return results;
278
- }, []);
279
- };
280
-
281
- module.exports.hasProperty = function(node, propertyNames, parentNode = null) {
282
- propertyNames = isArray(propertyNames) ? propertyNames : [ propertyNames ];
283
-
284
- const properties = findProperties(node, propertyNames);
285
-
286
- if (properties.length !== 1) {
287
- return [
288
- {
289
- message: `Element of type <${ node.$type }> must have property ${ formatNames(propertyNames, true) }`,
290
- path: getPath(node, parentNode),
291
- data: {
292
- type: ERROR_TYPES.PROPERTY_REQUIRED,
293
- node,
294
- parentNode: parentNode == node ? null : parentNode,
295
- requiredProperty: propertyNames
296
- }
297
- }
298
- ];
299
- }
300
-
301
- return [];
302
- };
303
-
304
- function findProperties(node, propertyNames) {
305
- const properties = [];
306
-
307
- for (const propertyName of propertyNames) {
308
- const propertyValue = node.get(propertyName);
309
-
310
- if (!isEmptyValue(propertyValue)) {
311
- properties.push(node.get(propertyName));
312
- }
313
- }
314
-
315
- return properties;
316
- }
317
-
318
- module.exports.hasExtensionElement = function(node, types, parentNode = null) {
319
- const typesArray = isArray(types) ? types : [ types ];
320
-
321
- const extensionElements = findExtensionElements(node, typesArray);
322
-
323
- if (!extensionElements || extensionElements.length !== 1) {
324
- return [
325
- {
326
- message: `Element of type <${ node.$type }> must have one extension element of type ${ formatNames(typesArray, true) }`,
327
- path: getPath(node, parentNode),
328
- data: {
329
- type: ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED,
330
- node,
331
- parentNode: parentNode == node ? null : parentNode,
332
- requiredExtensionElement: types
333
- }
334
- }
335
- ];
336
- }
337
-
338
- return [];
339
- };
340
-
341
- module.exports.hasNoExtensionElement = function(node, type, parentNode = null, allowedVersion = null) {
342
- const extensionElement = findExtensionElement(node, type);
343
-
344
- if (extensionElement) {
345
- return [
346
- {
347
- message: allowedVersion
348
- ? `Extension element of type <${ type }> only allowed by Camunda ${ allowedVersion }`
349
- : `Extension element of type <${ type }> not allowed`,
350
- path: getPath(extensionElement, parentNode),
351
- data: addAllowedVersion({
352
- type: ERROR_TYPES.EXTENSION_ELEMENT_NOT_ALLOWED,
353
- node,
354
- parentNode: parentNode == node ? null : parentNode,
355
- extensionElement
356
- }, allowedVersion)
357
- }
358
- ];
359
- }
360
-
361
- return [];
362
- };
363
-
364
- module.exports.hasExpression = function(node, propertyName, check, parentNode = null) {
365
- const expression = node.get(propertyName);
366
-
367
- if (!expression) {
368
- throw new Error('Expression not found');
369
- }
370
-
371
- let propertyValue = expression;
372
-
373
- if (is(expression, 'bpmn:Expression')) {
374
- propertyValue = expression.get('body');
375
- }
376
-
377
- const path = getPath(node, parentNode);
378
-
379
- if (!propertyValue) {
380
- if (check.required !== false) {
381
- return [
382
- {
383
- message: `Property <${ propertyName }> must have expression value`,
384
- path: path
385
- ? [ ...path, propertyName ]
386
- : null,
387
- data: {
388
- type: ERROR_TYPES.EXPRESSION_REQUIRED,
389
- node: is(expression, 'bpmn:Expression') ? expression : node,
390
- parentNode,
391
- property: propertyName
392
- }
393
- }
394
- ];
395
- }
396
-
397
- return [];
398
- }
399
-
400
- const allowed = check.allowed(propertyValue);
401
-
402
- if (allowed !== true) {
403
- let allowedVersion = null;
404
-
405
- if (isObject(allowed)) {
406
- ({ allowedVersion } = allowed);
407
- }
408
-
409
- return [
410
- {
411
- message: allowedVersion
412
- ? `Expression value of <${ propertyValue }> only allowed by Camunda ${ allowedVersion }`
413
- : `Expression value of <${ propertyValue }> not allowed`,
414
- path: path
415
- ? [ ...path, propertyName ]
416
- : null,
417
- data: addAllowedVersion({
418
- type: ERROR_TYPES.EXPRESSION_VALUE_NOT_ALLOWED,
419
- node: is(expression, 'bpmn:Expression') ? expression : node,
420
- parentNode,
421
- property: propertyName
422
- }, allowedVersion)
423
- }
424
- ];
425
- }
426
-
427
- return [];
428
- };
429
-
430
- function isExactly(node, type) {
431
- const { $model } = node;
432
-
433
- return $model.getType(node.$type) === $model.getType(type);
434
- }
435
-
436
- module.exports.isExactly = isExactly;
437
-
438
- module.exports.isAnyExactly = function(node, types) {
439
- return some(types, (type) => isExactly(node, type));
440
- };
441
-
442
- function truncate(string, maxLength = 10) {
443
- const stringified = `${ string }`;
444
-
445
- return stringified.length > maxLength ? `${ stringified.slice(0, maxLength) }...` : stringified;
446
- }
447
-
448
- function addAllowedVersion(data, allowedVersion) {
449
- if (!allowedVersion) {
450
- return data;
451
- }
452
-
453
- return {
454
- ...data,
455
- allowedVersion
456
- };
457
- }
458
-
459
- function findParent(node, type) {
460
- if (!node) {
461
- return null;
462
- }
463
-
464
- const parent = node.$parent;
465
-
466
- if (!parent) {
467
- return node;
468
- }
469
-
470
- if (is(parent, type)) {
471
- return parent;
472
- }
473
-
474
- return findParent(parent, type);
475
- }
476
-
477
- module.exports.findParent = findParent;
478
-
479
- function isEmptyString(value) {
480
- return isString(value) && value.trim() === '';
481
- }
482
-
483
- function isEmptyValue(value) {
484
- return isUndefined(value) || isNil(value) || isEmptyString(value);
1
+ const {
2
+ isArray,
3
+ isDefined,
4
+ isFunction,
5
+ isNil,
6
+ isObject,
7
+ isString,
8
+ isUndefined,
9
+ some
10
+ } = require('min-dash');
11
+
12
+ const {
13
+ is,
14
+ isAny
15
+ } = require('bpmnlint-utils');
16
+
17
+ const { getPath } = require('@bpmn-io/moddle-utils');
18
+
19
+ const { ERROR_TYPES } = require('./error-types');
20
+
21
+ module.exports.ERROR_TYPES = ERROR_TYPES;
22
+
23
+ function getEventDefinition(node) {
24
+ const eventDefinitions = node.get('eventDefinitions');
25
+
26
+ if (eventDefinitions) {
27
+ return eventDefinitions[ 0 ];
28
+ }
29
+ }
30
+
31
+ module.exports.getEventDefinition = getEventDefinition;
32
+
33
+ module.exports.getMessageEventDefinition = function(node) {
34
+ if (is(node, 'bpmn:ReceiveTask')) {
35
+ return node;
36
+ }
37
+
38
+ return getEventDefinition(node);
39
+ };
40
+
41
+ function findExtensionElements(node, types) {
42
+ const extensionElements = node.get('extensionElements');
43
+
44
+ if (!extensionElements) {
45
+ return;
46
+ }
47
+
48
+ const values = extensionElements.get('values');
49
+
50
+ if (!values || !values.length) {
51
+ return;
52
+ }
53
+
54
+ if (!isArray(types)) {
55
+ types = [ types ];
56
+ }
57
+
58
+ return values.filter(value => isAny(value, types));
59
+ }
60
+
61
+ module.exports.findExtensionElements = findExtensionElements;
62
+
63
+ function findExtensionElement(node, types) {
64
+ const extensionElements = findExtensionElements(node, types);
65
+
66
+ if (extensionElements && extensionElements.length) {
67
+ return extensionElements[ 0 ];
68
+ }
69
+ }
70
+
71
+ module.exports.findExtensionElement = findExtensionElement;
72
+
73
+ function formatNames(names, exclusive = false) {
74
+ return names.reduce((string, name, index) => {
75
+
76
+ // first
77
+ if (index === 0) {
78
+ return `<${ name }>`;
79
+ }
80
+
81
+ // last
82
+ if (index === names.length - 1) {
83
+ return `${ string } ${ exclusive ? 'or' : 'and' } <${ name }>`;
84
+ }
85
+
86
+ return `${ string }, <${ name }>`;
87
+ }, '');
88
+ }
89
+
90
+ module.exports.formatNames = formatNames;
91
+
92
+ module.exports.hasDuplicatedPropertyValues = function(node, propertiesName, propertyName, parentNode = null) {
93
+ const properties = node.get(propertiesName);
94
+
95
+ const propertyValues = properties.map(property => property.get(propertyName));
96
+
97
+ // (1) find duplicates
98
+ const duplicates = propertyValues.reduce((duplicates, propertyValue, index) => {
99
+ if (propertyValues.indexOf(propertyValue) !== index && !duplicates.includes(propertyValue)) {
100
+ return [
101
+ ...duplicates,
102
+ propertyValue
103
+ ];
104
+ }
105
+
106
+ return duplicates;
107
+ }, []);
108
+
109
+ // (2) report error for each duplicate
110
+ if (duplicates.length) {
111
+ return duplicates.map(duplicate => {
112
+
113
+ // (3) find properties with duplicate
114
+ const duplicateProperties = properties.filter(property => property.get(propertyName) === duplicate);
115
+
116
+ // (4) report error
117
+ return {
118
+ message: `Properties of type <${ duplicateProperties[ 0 ].$type }> have property <${ propertyName }> with duplicate value of <${ duplicate }>`,
119
+ path: null,
120
+ data: {
121
+ type: ERROR_TYPES.PROPERTY_VALUE_DUPLICATED,
122
+ node,
123
+ parentNode: parentNode == node ? null : parentNode,
124
+ duplicatedProperty: propertyName,
125
+ duplicatedPropertyValue: duplicate,
126
+ properties: duplicateProperties,
127
+ propertiesName
128
+ }
129
+ };
130
+ });
131
+ }
132
+
133
+ return [];
134
+ };
135
+
136
+ module.exports.hasProperties = function(node, properties, parentNode = null) {
137
+ return Object.entries(properties).reduce((results, property) => {
138
+ const [ propertyName, propertyChecks ] = property;
139
+
140
+ const { allowedVersion = null } = propertyChecks;
141
+
142
+ const path = getPath(node, parentNode);
143
+
144
+ const propertyValue = node.get(propertyName);
145
+
146
+ if (propertyChecks.required && isEmptyValue(propertyValue)) {
147
+ return [
148
+ ...results,
149
+ {
150
+ message: allowedVersion
151
+ ? `Element of type <${ node.$type }> without property <${ propertyName }> only allowed by Camunda ${ allowedVersion } or newer`
152
+ : `Element of type <${ node.$type }> must have property <${ propertyName }>`,
153
+ path: path
154
+ ? [ ...path, propertyName ]
155
+ : [ propertyName ],
156
+ data: addAllowedVersion({
157
+ type: ERROR_TYPES.PROPERTY_REQUIRED,
158
+ node,
159
+ parentNode: parentNode == node ? null : parentNode,
160
+ requiredProperty: propertyName
161
+ }, allowedVersion)
162
+ }
163
+ ];
164
+ }
165
+
166
+ if (propertyChecks.dependentRequired) {
167
+ const dependency = node.get(propertyChecks.dependentRequired);
168
+
169
+ if (dependency && isEmptyValue(propertyValue)) {
170
+ return [
171
+ ...results,
172
+ {
173
+ message: `Element of type <${ node.$type }> must have property <${ propertyName }> if it has property <${ propertyChecks.dependentRequired }>`,
174
+ path: path
175
+ ? [ ...path, propertyName ]
176
+ : [ propertyName ],
177
+ data: {
178
+ type: ERROR_TYPES.PROPERTY_DEPENDENT_REQUIRED,
179
+ node,
180
+ parentNode: parentNode == node ? null : parentNode,
181
+ property: propertyChecks.dependentRequired,
182
+ dependentRequiredProperty: propertyName
183
+ }
184
+ }
185
+ ];
186
+ }
187
+ }
188
+
189
+ if (
190
+ propertyChecks.type
191
+ && propertyValue
192
+ && (
193
+ !propertyValue.$instanceOf
194
+ || (!isArray(propertyChecks.type) && !propertyValue.$instanceOf(propertyChecks.type))
195
+ || (isArray(propertyChecks.type) && !some(propertyChecks.type, type => propertyValue.$instanceOf(type)))
196
+ )
197
+ ) {
198
+ return [
199
+ ...results,
200
+ {
201
+ message: allowedVersion
202
+ ? `Property <${ propertyName }> of type <${ propertyValue.$type }> only allowed by Camunda ${ allowedVersion } or newer`
203
+ : `Property <${ propertyName }> of type <${ propertyValue.$type }> not allowed`,
204
+ path: path
205
+ ? [ ...path, propertyName ]
206
+ : [ propertyName ],
207
+ data: addAllowedVersion({
208
+ type: ERROR_TYPES.PROPERTY_TYPE_NOT_ALLOWED,
209
+ node,
210
+ parentNode: parentNode == node ? null : parentNode,
211
+ property: propertyName,
212
+ allowedPropertyType: propertyChecks.type
213
+ }, allowedVersion)
214
+ }
215
+ ];
216
+ }
217
+
218
+ if ('value' in propertyChecks && propertyChecks.value !== propertyValue) {
219
+ return [
220
+ ...results,
221
+ {
222
+ message: `Property <${ propertyName }> must have value of <${ propertyChecks.value }>`,
223
+ path: path
224
+ ? [ ...path, propertyName ]
225
+ : [ propertyName ],
226
+ data: {
227
+ type: ERROR_TYPES.PROPERTY_VALUE_REQUIRED,
228
+ node,
229
+ parentNode: parentNode == node ? null : parentNode,
230
+ property: propertyName,
231
+ requiredValue: propertyChecks.value
232
+ }
233
+ }
234
+ ];
235
+ }
236
+
237
+ if (propertyChecks.allowed === false && isDefined(propertyValue) && !isNil(propertyValue)) {
238
+ return [
239
+ ...results,
240
+ {
241
+ message: allowedVersion
242
+ ? `Property <${ propertyName }> only allowed by Camunda ${ allowedVersion } or newer`
243
+ : `Property <${ propertyName }> not allowed`,
244
+ path: path
245
+ ? [ ...path, propertyName ]
246
+ : [ propertyName ],
247
+ data: addAllowedVersion({
248
+ type: ERROR_TYPES.PROPERTY_NOT_ALLOWED,
249
+ node,
250
+ parentNode: parentNode == node ? null : parentNode,
251
+ property: propertyName
252
+ }, allowedVersion)
253
+ }
254
+ ];
255
+ }
256
+
257
+ if (isFunction(propertyChecks.allowed) && !propertyChecks.allowed(propertyValue)) {
258
+ return [
259
+ ...results,
260
+ {
261
+ message: allowedVersion
262
+ ? `Property value of <${ truncate(propertyValue) }> only allowed by Camunda ${ allowedVersion } or newer`
263
+ : `Property value of <${ truncate(propertyValue) }> not allowed`,
264
+ path: path
265
+ ? [ ...path, propertyName ]
266
+ : [ propertyName ],
267
+ data: addAllowedVersion({
268
+ type: ERROR_TYPES.PROPERTY_VALUE_NOT_ALLOWED,
269
+ node,
270
+ parentNode: parentNode == node ? null : parentNode,
271
+ property: propertyName
272
+ }, allowedVersion)
273
+ }
274
+ ];
275
+ }
276
+
277
+ return results;
278
+ }, []);
279
+ };
280
+
281
+ module.exports.hasProperty = function(node, propertyNames, parentNode = null) {
282
+ propertyNames = isArray(propertyNames) ? propertyNames : [ propertyNames ];
283
+
284
+ const properties = findProperties(node, propertyNames);
285
+
286
+ if (properties.length !== 1) {
287
+ return [
288
+ {
289
+ message: `Element of type <${ node.$type }> must have property ${ formatNames(propertyNames, true) }`,
290
+ path: getPath(node, parentNode),
291
+ data: {
292
+ type: ERROR_TYPES.PROPERTY_REQUIRED,
293
+ node,
294
+ parentNode: parentNode == node ? null : parentNode,
295
+ requiredProperty: propertyNames
296
+ }
297
+ }
298
+ ];
299
+ }
300
+
301
+ return [];
302
+ };
303
+
304
+ function findProperties(node, propertyNames) {
305
+ const properties = [];
306
+
307
+ for (const propertyName of propertyNames) {
308
+ const propertyValue = node.get(propertyName);
309
+
310
+ if (!isEmptyValue(propertyValue)) {
311
+ properties.push(node.get(propertyName));
312
+ }
313
+ }
314
+
315
+ return properties;
316
+ }
317
+
318
+ module.exports.hasExtensionElement = function(node, types, parentNode = null) {
319
+ const typesArray = isArray(types) ? types : [ types ];
320
+
321
+ const extensionElements = findExtensionElements(node, typesArray);
322
+
323
+ if (!extensionElements || extensionElements.length !== 1) {
324
+ return [
325
+ {
326
+ message: `Element of type <${ node.$type }> must have one extension element of type ${ formatNames(typesArray, true) }`,
327
+ path: getPath(node, parentNode),
328
+ data: {
329
+ type: ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED,
330
+ node,
331
+ parentNode: parentNode == node ? null : parentNode,
332
+ requiredExtensionElement: types
333
+ }
334
+ }
335
+ ];
336
+ }
337
+
338
+ return [];
339
+ };
340
+
341
+ module.exports.hasNoExtensionElement = function(node, type, parentNode = null, allowedVersion = null) {
342
+ const extensionElement = findExtensionElement(node, type);
343
+
344
+ if (extensionElement) {
345
+ return [
346
+ {
347
+ message: allowedVersion
348
+ ? `Extension element of type <${ type }> only allowed by Camunda ${ allowedVersion }`
349
+ : `Extension element of type <${ type }> not allowed`,
350
+ path: getPath(extensionElement, parentNode),
351
+ data: addAllowedVersion({
352
+ type: ERROR_TYPES.EXTENSION_ELEMENT_NOT_ALLOWED,
353
+ node,
354
+ parentNode: parentNode == node ? null : parentNode,
355
+ extensionElement
356
+ }, allowedVersion)
357
+ }
358
+ ];
359
+ }
360
+
361
+ return [];
362
+ };
363
+
364
+ module.exports.hasExpression = function(node, propertyName, check, parentNode = null) {
365
+ const expression = node.get(propertyName);
366
+
367
+ if (!expression) {
368
+ throw new Error('Expression not found');
369
+ }
370
+
371
+ let propertyValue = expression;
372
+
373
+ if (is(expression, 'bpmn:Expression')) {
374
+ propertyValue = expression.get('body');
375
+ }
376
+
377
+ const path = getPath(node, parentNode);
378
+
379
+ if (!propertyValue) {
380
+ if (check.required !== false) {
381
+ return [
382
+ {
383
+ message: `Property <${ propertyName }> must have expression value`,
384
+ path: path
385
+ ? [ ...path, propertyName ]
386
+ : null,
387
+ data: {
388
+ type: ERROR_TYPES.EXPRESSION_REQUIRED,
389
+ node: is(expression, 'bpmn:Expression') ? expression : node,
390
+ parentNode,
391
+ property: propertyName
392
+ }
393
+ }
394
+ ];
395
+ }
396
+
397
+ return [];
398
+ }
399
+
400
+ const allowed = check.allowed(propertyValue);
401
+
402
+ if (allowed !== true) {
403
+ let allowedVersion = null;
404
+
405
+ if (isObject(allowed)) {
406
+ ({ allowedVersion } = allowed);
407
+ }
408
+
409
+ return [
410
+ {
411
+ message: allowedVersion
412
+ ? `Expression value of <${ propertyValue }> only allowed by Camunda ${ allowedVersion }`
413
+ : `Expression value of <${ propertyValue }> not allowed`,
414
+ path: path
415
+ ? [ ...path, propertyName ]
416
+ : null,
417
+ data: addAllowedVersion({
418
+ type: ERROR_TYPES.EXPRESSION_VALUE_NOT_ALLOWED,
419
+ node: is(expression, 'bpmn:Expression') ? expression : node,
420
+ parentNode,
421
+ property: propertyName
422
+ }, allowedVersion)
423
+ }
424
+ ];
425
+ }
426
+
427
+ return [];
428
+ };
429
+
430
+ function isExactly(node, type) {
431
+ const { $model } = node;
432
+
433
+ return $model.getType(node.$type) === $model.getType(type);
434
+ }
435
+
436
+ module.exports.isExactly = isExactly;
437
+
438
+ module.exports.isAnyExactly = function(node, types) {
439
+ return some(types, (type) => isExactly(node, type));
440
+ };
441
+
442
+ function truncate(string, maxLength = 10) {
443
+ const stringified = `${ string }`;
444
+
445
+ return stringified.length > maxLength ? `${ stringified.slice(0, maxLength) }...` : stringified;
446
+ }
447
+
448
+ function addAllowedVersion(data, allowedVersion) {
449
+ if (!allowedVersion) {
450
+ return data;
451
+ }
452
+
453
+ return {
454
+ ...data,
455
+ allowedVersion
456
+ };
457
+ }
458
+
459
+ function findParent(node, type) {
460
+ if (!node) {
461
+ return null;
462
+ }
463
+
464
+ const parent = node.$parent;
465
+
466
+ if (!parent) {
467
+ return node;
468
+ }
469
+
470
+ if (is(parent, type)) {
471
+ return parent;
472
+ }
473
+
474
+ return findParent(parent, type);
475
+ }
476
+
477
+ module.exports.findParent = findParent;
478
+
479
+ function isEmptyString(value) {
480
+ return isString(value) && value.trim() === '';
481
+ }
482
+
483
+ function isEmptyValue(value) {
484
+ return isUndefined(value) || isNil(value) || isEmptyString(value);
485
485
  }