bpmnlint-plugin-camunda-compat 0.4.0 → 0.6.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.
@@ -0,0 +1,659 @@
1
+ const {
2
+ isArray,
3
+ isObject,
4
+ isString,
5
+ some
6
+ } = require('min-dash');
7
+
8
+ const {
9
+ is,
10
+ isAny
11
+ } = require('bpmnlint-utils');
12
+
13
+ const { getPath } = require('@philippfromme/moddle-helpers');
14
+
15
+ const ERROR_TYPES = Object.freeze({
16
+ ELEMENT_TYPE: 'elementType',
17
+ EXTENSION_ELEMENT_REQUIRED: 'extensionElementRequired',
18
+ PROPERTY_DEPENDEND_REQUIRED: 'propertyDependendRequired',
19
+ PROPERTY_REQUIRED: 'propertyRequired',
20
+ PROPERTY_TYPE: 'propertyType'
21
+ });
22
+
23
+ module.exports.ERROR_TYPES = ERROR_TYPES;
24
+
25
+ /**
26
+ * @param {ModdleElement} node
27
+ *
28
+ * @returns {boolean|string}
29
+ */
30
+ function hasNoEventDefinition(node) {
31
+ const eventDefinitions = node.get('eventDefinitions');
32
+
33
+ return !eventDefinitions
34
+ || !eventDefinitions.length
35
+ || getElementNotSupportedError(node.$type, eventDefinitions[ 0 ].$type, [ 'eventDefinitions', 0 ]);
36
+ }
37
+
38
+ module.exports.hasNoEventDefinition = hasNoEventDefinition;
39
+
40
+ /**
41
+ * @param {ModdleElement} node
42
+ * @param {string[]} types
43
+ *
44
+ * @returns {boolean|string}
45
+ */
46
+ function hasEventDefinitionOfType(node, types) {
47
+ if (!isArray(types)) {
48
+ types = [ types ];
49
+ }
50
+
51
+ const eventDefinitions = node.get('eventDefinitions');
52
+
53
+ if (!eventDefinitions || eventDefinitions.length !== 1) {
54
+ return getElementNotSupportedError(node.$type, null);
55
+ }
56
+
57
+ const eventDefinition = eventDefinitions[ 0 ];
58
+
59
+ return isAny(eventDefinition, types)
60
+ || getElementNotSupportedError(node.$type, eventDefinition.$type, [ 'eventDefinitions', 0 ]);
61
+ }
62
+
63
+ /**
64
+ * @param {string[]} types
65
+ *
66
+ * @returns {Function}
67
+ */
68
+ module.exports.hasEventDefinitionOfType = function(types) {
69
+ return function(node) {
70
+ return hasEventDefinitionOfType(node, types);
71
+ };
72
+ };
73
+
74
+ /**
75
+ * @param {string[]} types
76
+ *
77
+ * @returns {Function}
78
+ */
79
+ module.exports.hasEventDefinitionOfTypeOrNone = function(types) {
80
+ return function(node) {
81
+ const results = [
82
+ hasNoEventDefinition(node),
83
+ hasEventDefinitionOfType(node, types)
84
+ ];
85
+
86
+ return some(results, result => result === true)
87
+ || results.find(result => isArray(result) || isObject(result) || isString(result));
88
+ };
89
+ };
90
+
91
+ module.exports.hasLoopCharacteristics = function(node) {
92
+ return !!node.get('loopCharacteristics');
93
+ };
94
+
95
+ /**
96
+ * @param {string[]} types
97
+ *
98
+ * @returns {boolean|string}
99
+ */
100
+ module.exports.hasLoopCharacteristicsOfTypeOrNone = function(type) {
101
+ return function(node) {
102
+ const loopCharacteristics = node.get('loopCharacteristics');
103
+
104
+ if (!loopCharacteristics) {
105
+ return true;
106
+ }
107
+
108
+ return is(loopCharacteristics, type)
109
+ || getElementNotSupportedError(node.$type, loopCharacteristics.$type, [ 'loopCharacteristics' ]);
110
+ };
111
+ };
112
+
113
+ /**
114
+ * @param {ModdleElement} node
115
+ *
116
+ * @returns {boolean|Object|Object[]}
117
+ */
118
+ module.exports.hasMultiInstanceLoopCharacteristics = function(node) {
119
+ const results = checkProperties(node, {
120
+ loopCharacteristics: {
121
+ type: 'bpmn:MultiInstanceLoopCharacteristics'
122
+ }
123
+ }, node);
124
+
125
+ if (results.length === 1) {
126
+ return results[ 0 ];
127
+ } else if (results.length > 1) {
128
+ return results;
129
+ }
130
+
131
+ return true;
132
+ };
133
+
134
+ module.exports.isNotBpmn = function(node) {
135
+ return !is(node, 'bpmn:BaseElement');
136
+ };
137
+
138
+ function findExtensionElement(node, types) {
139
+ const extensionElements = findExtensionElements(node, types);
140
+
141
+ if (extensionElements && extensionElements.length) {
142
+ return extensionElements[ 0 ];
143
+ }
144
+ }
145
+
146
+ function findExtensionElements(node, types) {
147
+ const extensionElements = node.get('extensionElements');
148
+
149
+ if (!extensionElements) {
150
+ return;
151
+ }
152
+
153
+ const values = extensionElements.get('values');
154
+
155
+ if (!values || !values.length) {
156
+ return;
157
+ }
158
+
159
+ if (!isArray(types)) {
160
+ types = [ types ];
161
+ }
162
+
163
+ return values.filter(value => isAny(value, types));
164
+ }
165
+
166
+ module.exports.findExtensionElement = findExtensionElement;
167
+
168
+ function getElementNotSupportedError(type, propertyType, path = null) {
169
+ if (propertyType) {
170
+ return {
171
+ message: `Element of type <${ type }> (<${ propertyType }>) not supported by {{ executionPlatform }}`,
172
+ path,
173
+ error: {
174
+ type: ERROR_TYPES.ELEMENT_TYPE,
175
+ elementType: type,
176
+ propertyType
177
+ }
178
+ };
179
+ }
180
+
181
+ return {
182
+ message: `Element of type <${ type }> not supported by {{ executionPlatform }}`,
183
+ path,
184
+ error: {
185
+ type: ERROR_TYPES.ELEMENT_TYPE,
186
+ elementType: type
187
+ }
188
+ };
189
+ }
190
+
191
+ /**
192
+ * @param {string|Object} type
193
+ *
194
+ * @returns {string}
195
+ */
196
+ function getType(type) {
197
+ if (isObject(type)) {
198
+ return type.type;
199
+ }
200
+
201
+ return type;
202
+ }
203
+
204
+ /**
205
+ * @param {string|Object} type
206
+ *
207
+ * @returns {string|Object}
208
+ */
209
+ function getProperties(type) {
210
+ if (isObject(type)) {
211
+ return type.properties;
212
+ }
213
+ }
214
+
215
+ /**
216
+ * @param {Function} check
217
+ * @param {Function} ifFn
218
+ * @param {*} [elseReturnValue]
219
+ *
220
+ * @returns {Function}
221
+ */
222
+ module.exports.checkIf = function(check, ifFn, elseReturnValue = true) {
223
+ return function(node) {
224
+ if (ifFn(node) === true) {
225
+ return check(node);
226
+ }
227
+
228
+ return elseReturnValue;
229
+ };
230
+ };
231
+
232
+ function formatTypes(types, exclusive = false) {
233
+ return types.reduce((string, type, index) => {
234
+ type = getType(type);
235
+
236
+ // first
237
+ if (index === 0) {
238
+ return `<${ type }>`;
239
+ }
240
+
241
+ // last
242
+ if (index === types.length - 1) {
243
+ return `${ string } ${ exclusive ? 'or' : 'and' } <${ type }>`;
244
+ }
245
+
246
+ return `${ string }, <${ type }>`;
247
+ }, '');
248
+ }
249
+
250
+ module.exports.formatTypes = formatTypes;
251
+
252
+ function checkProperties(node, properties, parentNode) {
253
+ return Object.entries(properties).reduce((results, property) => {
254
+ const [ propertyName, propertyChecks ] = property;
255
+
256
+ const path = getPath(node, parentNode);
257
+
258
+ const propertyValue = node.get(propertyName);
259
+
260
+ if (propertyChecks.required && !propertyValue) {
261
+ return [
262
+ ...results,
263
+ {
264
+ message: `Element of type <${ node.$type }> must have property <${ propertyName }>`,
265
+ path: path
266
+ ? [ ...path, propertyName ]
267
+ : [ propertyName ],
268
+ error: {
269
+ type: ERROR_TYPES.PROPERTY_REQUIRED,
270
+ requiredProperty: propertyName
271
+ }
272
+ }
273
+ ];
274
+ }
275
+
276
+ if (propertyChecks.dependendRequired) {
277
+ const dependency = node.get(propertyChecks.dependendRequired);
278
+
279
+ if (dependency && !propertyValue) {
280
+ return [
281
+ ...results,
282
+ {
283
+ message: `Element of type <${ node.$type }> must have property <${ propertyName }> if it has property <${ propertyChecks.dependendRequired }>`,
284
+ path: path
285
+ ? [ ...path, propertyName ]
286
+ : [ propertyName ],
287
+ error: {
288
+ type: ERROR_TYPES.PROPERTY_DEPENDEND_REQUIRED,
289
+ dependendRequiredProperty: propertyName
290
+ }
291
+ }
292
+ ];
293
+ }
294
+ }
295
+
296
+ if (propertyChecks.type && propertyValue && (!propertyValue.$instanceOf || !propertyValue.$instanceOf(propertyChecks.type))) {
297
+ return [
298
+ ...results,
299
+ {
300
+ message: `Element of type <${ node.$type }> must have property <${ propertyName }> of type <${ propertyChecks.type }>`,
301
+ path: path
302
+ ? [ ...path, propertyName ]
303
+ : [ propertyName ],
304
+ error: {
305
+ type: ERROR_TYPES.PROPERTY_TYPE,
306
+ propertyType: propertyChecks.type
307
+ }
308
+ }
309
+ ];
310
+ }
311
+
312
+ return results;
313
+ }, []);
314
+ }
315
+
316
+ module.exports.checkProperties = checkProperties;
317
+
318
+ /**
319
+ * @example
320
+ *
321
+ * const check = hasExtensionElementsOfTypes([ 'zeebe:CalledDecision', 'zeebe:TaskDefinition' ]);
322
+ *
323
+ * check(node, parentNode);
324
+ *
325
+ * @example
326
+ *
327
+ * const check = hasExtensionElementsOfTypes([
328
+ * {
329
+ * type: 'zeebe:CalledDecision',
330
+ * properties: {
331
+ * decisionId: { required: true },
332
+ * resultVariable: { required: true }
333
+ * }
334
+ * },
335
+ * {
336
+ * type: 'zeebe:TaskDefinition',
337
+ * properties: {
338
+ * type: { required: true }
339
+ * }
340
+ * }
341
+ * ]);
342
+ *
343
+ * check(node, parentNode);
344
+ *
345
+ * @param {string[]|Object[]} types
346
+ *
347
+ * @returns {Function}
348
+ */
349
+ module.exports.hasExtensionElementsOfTypes = function(types, exclusive = false) {
350
+ return function(node, parentNode) {
351
+ const extensionElements = findExtensionElements(node, types.map(type => getType(type)));
352
+
353
+ if (!extensionElements || !extensionElements.length) {
354
+ return {
355
+ message: `Element of type <${ node.$type }> must have one or many extension elements of type ${ formatTypes(types, true) }`,
356
+ path: getPath(node, parentNode),
357
+ error: {
358
+ type: ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED,
359
+ requiredExtensionElement: getType(types[ 0 ])
360
+ }
361
+ };
362
+ }
363
+
364
+ if (exclusive && extensionElements.length > 1) {
365
+ return {
366
+ message: `Element of type <${ node.$type }> must have one extension element of type ${ formatTypes(types, true) }`,
367
+ path: getPath(node, parentNode)
368
+ };
369
+ }
370
+
371
+ const results = extensionElements.reduce((errors, extensionElement) => {
372
+ const type = types.find(type => is(extensionElement, getType(type)));
373
+
374
+ const properties = getProperties(type);
375
+
376
+ if (properties) {
377
+ return [
378
+ ...errors,
379
+ ...checkProperties(extensionElement, properties, parentNode)
380
+ ];
381
+ }
382
+
383
+ return errors;
384
+ }, []);
385
+
386
+ if (results.length === 1) {
387
+ return results[ 0 ];
388
+ } else if (results.length > 1) {
389
+ return results;
390
+ }
391
+
392
+ return true;
393
+ };
394
+ };
395
+
396
+ /**
397
+ * @example
398
+ *
399
+ * const check = hasExtensionElementOfType('zeebe:TaskDefinition');
400
+ *
401
+ * check(node, parentNode);
402
+ *
403
+ * @example
404
+ *
405
+ * const check = hasExtensionElementOfType(
406
+ * {
407
+ * type: 'zeebe:TaskDefinition',
408
+ * properties: {
409
+ * type: { required: true }
410
+ * }
411
+ * }
412
+ * );
413
+ *
414
+ * check(node, parentNode);
415
+ *
416
+ * @param {string[]|Object[]} types
417
+ *
418
+ * @returns {Function}
419
+ */
420
+ module.exports.hasExtensionElementOfType = function(type) {
421
+ return function(node, parentNode) {
422
+ const extensionElement = findExtensionElement(node, getType(type));
423
+
424
+ if (!extensionElement) {
425
+ return {
426
+ message: `Element of type <${ node.$type }> must have extension element of type <${ getType(type) }>`,
427
+ path: getPath(node, parentNode),
428
+ error: {
429
+ type: ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED,
430
+ requiredExtensionElement: getType(type)
431
+ }
432
+ };
433
+ }
434
+
435
+ const properties = getProperties(type);
436
+
437
+ if (properties) {
438
+ const results = checkProperties(extensionElement, properties, parentNode);
439
+
440
+ if (results.length === 1) {
441
+ return results[ 0 ];
442
+ } else if (results.length > 1) {
443
+ return results;
444
+ }
445
+ }
446
+
447
+ return true;
448
+ };
449
+ };
450
+
451
+ /**
452
+ * @example
453
+ *
454
+ * const check = checkError((error) => { ... });
455
+ *
456
+ * check(errorEventDefinition);
457
+ *
458
+ * @param {Function} check
459
+ *
460
+ * @returns {Function}
461
+ */
462
+ function checkError(check) {
463
+ return function(node, parentNode) {
464
+ const results = checkProperties(node, {
465
+ errorRef: {
466
+ required: true
467
+ }
468
+ }, parentNode);
469
+
470
+ if (results.length === 1) {
471
+ return results[ 0 ];
472
+ } else if (results.length > 1) {
473
+ return results;
474
+ }
475
+
476
+ const error = node.get('errorRef');
477
+
478
+ return check(error);
479
+ };
480
+ }
481
+
482
+ module.exports.checkError = checkError;
483
+
484
+ /**
485
+ * @example
486
+ *
487
+ * const check = checkEventDefinition((eventDefinition, event) => { ... });
488
+ *
489
+ * check(startEvent);
490
+ *
491
+ * @param {Function} check
492
+ *
493
+ * @returns {Function}
494
+ */
495
+ module.exports.checkEventDefinition = function(check) {
496
+ return function(node) {
497
+ const results = checkProperties(node, {
498
+ eventDefinitions: {
499
+ required: true
500
+ }
501
+ });
502
+
503
+ if (results.length === 1) {
504
+ return results[ 0 ];
505
+ } else if (results.length > 1) {
506
+ return results;
507
+ }
508
+
509
+ const eventDefinitions = node.get('eventDefinitions');
510
+
511
+ return check(eventDefinitions[ 0 ], node);
512
+ };
513
+ };
514
+
515
+ /**
516
+ * @example
517
+ *
518
+ * const check = checkFlowNode((node, parentNode) => { ... });
519
+ *
520
+ * check(serviceTask);
521
+ *
522
+ * @param {Function} check
523
+ *
524
+ * @returns {Function}
525
+ */
526
+ module.exports.checkFlowNode = function(check) {
527
+ return function(node) {
528
+ return check(node, node);
529
+ };
530
+ };
531
+
532
+ /**
533
+ * @example
534
+ *
535
+ * const check = checkMessage((message) => { ... });
536
+ *
537
+ * check(messageEventDefinition);
538
+ *
539
+ * @param {Function} check
540
+ *
541
+ * @returns {Function}
542
+ */
543
+ function checkMessage(check) {
544
+ return function(node, parentNode) {
545
+ const results = checkProperties(node, {
546
+ messageRef: {
547
+ required: true
548
+ }
549
+ }, parentNode);
550
+
551
+ if (results.length === 1) {
552
+ return results[ 0 ];
553
+ } else if (results.length > 1) {
554
+ return results;
555
+ }
556
+
557
+ const message = node.get('messageRef');
558
+
559
+ return check(message);
560
+ };
561
+ }
562
+
563
+ module.exports.checkMessage = checkMessage;
564
+
565
+ /**
566
+ * @example
567
+ *
568
+ * const check = checkLoopCharacteristics((loopCharacteristics, activity) => { ... });
569
+ *
570
+ * check(serviceTask);
571
+ *
572
+ * @param {Function} check
573
+ *
574
+ * @returns {Function}
575
+ */
576
+ module.exports.checkLoopCharacteristics = function(check) {
577
+ return function(node) {
578
+ const results = checkProperties(node, {
579
+ loopCharacteristics: {
580
+ required: true
581
+ }
582
+ });
583
+
584
+ if (results.length === 1) {
585
+ return results[ 0 ];
586
+ } else if (results.length > 1) {
587
+ return results;
588
+ }
589
+
590
+ const loopCharacteristics = node.get('loopCharacteristics');
591
+
592
+ return check(loopCharacteristics, node);
593
+ };
594
+ };
595
+
596
+ module.exports.hasErrorReference = checkError(
597
+ (node) => {
598
+ const results = checkProperties(node, {
599
+ errorCode: {
600
+ required: true
601
+ }
602
+ });
603
+
604
+ if (results.length === 1) {
605
+ return results[ 0 ];
606
+ } else if (results.length > 1) {
607
+ return results;
608
+ }
609
+
610
+ return true;
611
+ }
612
+ );
613
+
614
+ module.exports.hasMessageReference = checkMessage(
615
+ (node) => {
616
+ const results = checkProperties(node, {
617
+ name: {
618
+ required: true
619
+ }
620
+ });
621
+
622
+ if (results.length === 1) {
623
+ return results[ 0 ];
624
+ } else if (results.length > 1) {
625
+ return results;
626
+ }
627
+
628
+ return true;
629
+ }
630
+ );
631
+
632
+ function translate(result, translations) {
633
+ if (isString(result)) {
634
+ return translations[result] || result;
635
+ }
636
+
637
+ const { message } = result;
638
+
639
+ return {
640
+ ...result,
641
+ message: translations[message] || message
642
+ };
643
+ }
644
+
645
+ module.exports.withTranslations = function(check, translations) {
646
+ return function(node) {
647
+ const results = check(node);
648
+
649
+ if (isObject(results) || isString(results)) {
650
+ return translate(results, translations);
651
+ }
652
+
653
+ if (isArray(results)) {
654
+ return results.map((result) => translate(result, translations));
655
+ }
656
+
657
+ return results;
658
+ };
659
+ };
@@ -7,4 +7,4 @@
7
7
  */
8
8
  module.exports.toSemverMinor = function(string) {
9
9
  return string && string.split(/\./).slice(0, 2).join('.');
10
- }
10
+ };