@camunda/linting 0.8.0 → 0.9.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.
package/lib/Linter.js CHANGED
@@ -10,7 +10,7 @@ import { isString } from 'min-dash';
10
10
  import modelerModdle from 'modeler-moddle/resources/modeler.json';
11
11
  import zeebeModdle from 'zeebe-bpmn-moddle/resources/zeebe.json';
12
12
 
13
- import { getErrorMessage, getExecutionPlatformLabel } from './utils/error-messages';
13
+ import { getErrorMessage } from './utils/error-messages';
14
14
  import { getEntryIds } from './utils/properties-panel';
15
15
 
16
16
  const moddle = new BpmnModdle({
@@ -68,7 +68,8 @@ export class Linter {
68
68
  ...report,
69
69
  message: getErrorMessage(
70
70
  report,
71
- getExecutionPlatformLabel(executionPlatform, toSemverMinor(executionPlatformVersion)),
71
+ executionPlatform,
72
+ executionPlatformVersion,
72
73
  this._modeler
73
74
  ),
74
75
  propertiesPanel: {
@@ -1,6 +1,7 @@
1
1
  import { ERROR_TYPES } from 'bpmnlint-plugin-camunda-compat/rules/utils/element';
2
2
 
3
3
  import { is } from 'bpmnlint-utils';
4
+
4
5
  import {
5
6
  every,
6
7
  isArray
@@ -9,29 +10,75 @@ import {
9
10
  import { getTypeString } from './types';
10
11
 
11
12
  const TIMER_PROPERTIES = [
13
+ 'timeCycle',
12
14
  'timeDate',
13
- 'timeDuration',
14
- 'timeCycle'
15
+ 'timeDuration'
15
16
  ];
16
17
 
17
- export function getErrorMessage(report, executionPlatformLabel, modeler = 'desktop') {
18
+ const executionPlatformLabels = {
19
+ 'Camunda Cloud': {
20
+ 'default': 'Camunda',
21
+ '1.0': 'Camunda 8 (Zeebe 1.0)',
22
+ '1.1': 'Camunda 8 (Zeebe 1.1)',
23
+ '1.2': 'Camunda 8 (Zeebe 1.2)',
24
+ '1.3': 'Camunda 8 (Zeebe 1.3)'
25
+ }
26
+ };
27
+
28
+ export function getExecutionPlatformLabel(executionPlatform, executionPlatformVersion) {
29
+ const executionPlatformLabel = executionPlatformLabels[ executionPlatform ]
30
+ && executionPlatformLabels[ executionPlatform ][ executionPlatformVersion ];
31
+
32
+ if (executionPlatformLabel) {
33
+ return executionPlatformLabel;
34
+ }
35
+
36
+ if (executionPlatformLabels[ executionPlatform ]
37
+ && executionPlatformLabels[ executionPlatform ][ 'default' ]) {
38
+ executionPlatform = executionPlatformLabels[ executionPlatform ][ 'default' ];
39
+ }
40
+
41
+ return `${ executionPlatform } ${ executionPlatformVersion }`;
42
+ }
43
+
44
+ function getIndefiniteArticle(type) {
45
+ if ([
46
+ 'Ad',
47
+ 'Error',
48
+ 'Escalation',
49
+ 'Event',
50
+ 'Inclusive',
51
+ 'Intermediate',
52
+ 'Undefined'
53
+ ].includes(type.split(' ')[ 0 ])) {
54
+ return 'An';
55
+ }
56
+
57
+ return 'A';
58
+ }
59
+
60
+ export function getErrorMessage(report, executionPlatform, executionPlatformVersion, modeler = 'desktop') {
18
61
  const {
19
- error,
62
+ data,
20
63
  message
21
64
  } = report;
22
65
 
23
- if (!error) {
66
+ if (!data) {
24
67
  return message;
25
68
  }
26
69
 
27
- const { type } = error;
70
+ const { type } = data;
71
+
72
+ if (type === ERROR_TYPES.ELEMENT_COLLAPSED_NOT_ALLOWED) {
73
+ return getElementCollapsedNotAllowedErrorMessage(report);
74
+ }
28
75
 
29
76
  if (type === ERROR_TYPES.ELEMENT_TYPE_NOT_ALLOWED) {
30
- return getElementTypeNotAllowedErrorMessage(report, executionPlatformLabel);
77
+ return getElementTypeNotAllowedErrorMessage(report, executionPlatform, executionPlatformVersion);
31
78
  }
32
79
 
33
80
  if (type === ERROR_TYPES.EXTENSION_ELEMENT_NOT_ALLOWED) {
34
- return getExtensionElementNotAllowedErrorMessage(report, executionPlatformLabel);
81
+ return getExtensionElementNotAllowedErrorMessage(report, executionPlatform, executionPlatformVersion);
35
82
  }
36
83
 
37
84
  if (type === ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED) {
@@ -43,80 +90,61 @@ export function getErrorMessage(report, executionPlatformLabel, modeler = 'deskt
43
90
  }
44
91
 
45
92
  if (type === ERROR_TYPES.PROPERTY_NOT_ALLOWED) {
46
- return getPropertyNotAllowedErrorMessage(report, executionPlatformLabel, modeler);
93
+ return getPropertyNotAllowedErrorMessage(report, executionPlatform, executionPlatformVersion, modeler);
47
94
  }
48
95
 
49
96
  if (type === ERROR_TYPES.PROPERTY_REQUIRED) {
50
- return getPropertyRequiredErrorMessage(report, executionPlatformLabel);
51
- }
52
-
53
- if (type === ERROR_TYPES.PROPERTY_TYPE_NOT_ALLOWED) {
54
- return getPropertyTypeNotAllowedErrorMessage(report, executionPlatformLabel);
97
+ return getPropertyRequiredErrorMessage(report);
55
98
  }
56
99
 
57
100
  if (type === ERROR_TYPES.PROPERTY_VALUE_DUPLICATED) {
58
101
  return getPropertyValueDuplicatedErrorMessage(report);
59
102
  }
60
103
 
61
- if (type === ERROR_TYPES.PROPERTY_VALUE_NOT_ALLOWED) {
62
- return getPropertyValueNotAllowedErrorMessage(report, executionPlatformLabel);
104
+ if (type === ERROR_TYPES.EXPRESSION_REQUIRED) {
105
+ return getExpressionRequiredErrorMessage(report);
63
106
  }
64
107
 
65
- return message;
66
- }
67
-
68
- const executionPlatformLabels = {
69
- 'Camunda Cloud': {
70
- 'defaultPlatformName': 'Camunda',
71
- '1.0': 'Camunda 8 (Zeebe 1.0)',
72
- '1.1': 'Camunda 8 (Zeebe 1.1)',
73
- '1.2': 'Camunda 8 (Zeebe 1.2)',
74
- '1.3': 'Camunda 8 (Zeebe 1.3)'
108
+ if (type === ERROR_TYPES.EXPRESSION_VALUE_NOT_ALLOWED) {
109
+ return getExpressionValueNotAllowedErrorMessage(report);
75
110
  }
76
- };
77
111
 
78
- export function getExecutionPlatformLabel(executionPlatform, executionPlatformVersion) {
79
- const translatedLabel = executionPlatformLabels[ executionPlatform ] && executionPlatformLabels[ executionPlatform ][ executionPlatformVersion ];
112
+ return message;
113
+ }
80
114
 
81
- if (translatedLabel) {
82
- return translatedLabel;
83
- }
115
+ function getElementCollapsedNotAllowedErrorMessage(report) {
116
+ const {
117
+ data,
118
+ message
119
+ } = report;
84
120
 
85
- if (executionPlatformLabels[executionPlatform] && executionPlatformLabels[executionPlatform]['defaultPlatformName']) {
86
- executionPlatform = executionPlatformLabels[executionPlatform]['defaultPlatformName'];
87
- }
121
+ const { node } = data;
88
122
 
89
- return `${ executionPlatform } ${ executionPlatformVersion }`;
90
- }
123
+ const typeString = getTypeString(node);
91
124
 
92
- function getIndefiniteArticle(type) {
93
- if ([
94
- 'Error',
95
- 'Escalation',
96
- 'Event',
97
- 'Inclusive',
98
- 'Intermediate',
99
- 'Undefined'
100
- ].includes(type.split(' ')[ 0 ])) {
101
- return 'An';
125
+ if (is(node, 'bpmn:SubProcess')) {
126
+ return `${ getIndefiniteArticle(typeString) } <${ typeString }> must be expanded`;
102
127
  }
103
128
 
104
- return 'A';
129
+ return message;
105
130
  }
106
131
 
107
- function getElementTypeNotAllowedErrorMessage(report, executionPlatformLabel) {
108
- const { error } = report;
132
+ function getElementTypeNotAllowedErrorMessage(report, executionPlatform, executionPlatformVersion) {
133
+ const { data } = report;
109
134
 
110
- const { node } = error;
135
+ const {
136
+ allowedVersion,
137
+ node
138
+ } = data;
111
139
 
112
140
  const typeString = getTypeString(node);
113
141
 
114
- return `${ getIndefiniteArticle(typeString) } <${ typeString }> is not supported by ${ executionPlatformLabel }`;
142
+ return getSupportedMessage(`${ getIndefiniteArticle(typeString) } <${ typeString }>`, executionPlatform, executionPlatformVersion, allowedVersion);
115
143
  }
116
144
 
117
145
  function getPropertyValueDuplicatedErrorMessage(report) {
118
146
  const {
119
- error,
147
+ data,
120
148
  message
121
149
  } = report;
122
150
 
@@ -125,7 +153,7 @@ function getPropertyValueDuplicatedErrorMessage(report) {
125
153
  parentNode,
126
154
  duplicatedPropertyValue,
127
155
  properties
128
- } = error;
156
+ } = data;
129
157
 
130
158
  const typeString = getTypeString(parentNode || node);
131
159
 
@@ -136,26 +164,27 @@ function getPropertyValueDuplicatedErrorMessage(report) {
136
164
  return message;
137
165
  }
138
166
 
139
- function getExtensionElementNotAllowedErrorMessage(report, executionPlatformLabel) {
167
+ function getExtensionElementNotAllowedErrorMessage(report, executionPlatform, executionPlatformVersion) {
140
168
  const {
141
- error,
169
+ data,
142
170
  message
143
171
  } = report;
144
172
 
145
173
  const {
174
+ allowedVersion,
146
175
  node,
147
176
  parentNode,
148
177
  extensionElement
149
- } = error;
178
+ } = data;
150
179
 
151
180
  const typeString = getTypeString(parentNode || node);
152
181
 
153
182
  if (is(node, 'bpmn:BusinessRuleTask') && is(extensionElement, 'zeebe:CalledDecision')) {
154
- return `A <Business Rule Task> with <Implementation: DMN decision> is not supported by ${ executionPlatformLabel }`;
183
+ return getSupportedMessage('A <Business Rule Task> with <Implementation: DMN decision>', executionPlatform, executionPlatformVersion, allowedVersion);
155
184
  }
156
185
 
157
186
  if (is(extensionElement, 'zeebe:Properties')) {
158
- return `${ getIndefiniteArticle(typeString) } <${ typeString }> with <Extension properties> is not supported by ${ executionPlatformLabel }`;
187
+ return getSupportedMessage(`${ getIndefiniteArticle(typeString) } <${ typeString }> with <Extension properties>`, executionPlatform, executionPlatformVersion, allowedVersion);
159
188
  }
160
189
 
161
190
  return message;
@@ -163,7 +192,7 @@ function getExtensionElementNotAllowedErrorMessage(report, executionPlatformLabe
163
192
 
164
193
  function getExtensionElementRequiredErrorMessage(report) {
165
194
  const {
166
- error,
195
+ data,
167
196
  message
168
197
  } = report;
169
198
 
@@ -171,7 +200,7 @@ function getExtensionElementRequiredErrorMessage(report) {
171
200
  node,
172
201
  parentNode,
173
202
  requiredExtensionElement
174
- } = error;
203
+ } = data;
175
204
 
176
205
  const typeString = getTypeString(parentNode || node);
177
206
 
@@ -200,7 +229,7 @@ function getExtensionElementRequiredErrorMessage(report) {
200
229
 
201
230
  function getPropertyDependendRequiredErrorMessage(report) {
202
231
  const {
203
- error,
232
+ data,
204
233
  message
205
234
  } = report;
206
235
 
@@ -209,7 +238,7 @@ function getPropertyDependendRequiredErrorMessage(report) {
209
238
  parentNode,
210
239
  property,
211
240
  dependendRequiredProperty
212
- } = error;
241
+ } = data;
213
242
 
214
243
  const typeString = getTypeString(parentNode || node);
215
244
 
@@ -224,39 +253,40 @@ function getPropertyDependendRequiredErrorMessage(report) {
224
253
  return message;
225
254
  }
226
255
 
227
- function getPropertyNotAllowedErrorMessage(report, executionPlatformLabel, modeler = 'desktop') {
256
+ function getPropertyNotAllowedErrorMessage(report, executionPlatform, executionPlatformVersion, modeler = 'desktop') {
228
257
  const {
229
- error,
258
+ data,
230
259
  message
231
260
  } = report;
232
261
 
233
262
  const {
263
+ allowedVersion,
234
264
  node,
235
265
  parentNode,
236
266
  property
237
- } = error;
267
+ } = data;
238
268
 
239
269
  const typeString = getTypeString(parentNode || node);
240
270
 
241
271
  if (property === 'modelerTemplate') {
242
272
  if (modeler === 'desktop') {
243
- return `${ getIndefiniteArticle(typeString) } <Template ${ typeString }> is not supported by ${ executionPlatformLabel }`;
273
+ return getSupportedMessage(`${ getIndefiniteArticle(typeString) } <Template ${ typeString }>`, executionPlatform, executionPlatformVersion, allowedVersion);
244
274
  } else if (modeler === 'web') {
245
- return `${ getIndefiniteArticle(typeString) } <Connector ${ typeString }> is not supported by ${ executionPlatformLabel }`;
275
+ return getSupportedMessage(`${ getIndefiniteArticle(typeString) } <Connector ${ typeString }>`, executionPlatform, executionPlatformVersion, allowedVersion);
246
276
  }
247
277
  }
248
278
 
249
279
  if (is(node, 'bpmn:InclusiveGateway') && property === 'incoming') {
250
- return `${ getIndefiniteArticle(typeString) } <${ typeString }> with more than one incoming <Sequence Flow> is not supported by ${ executionPlatformLabel }`;
280
+ return `${ getIndefiniteArticle(typeString) } <${ typeString }> with more than one incoming <Sequence Flow> is not supported by ${ getExecutionPlatformLabel(executionPlatform, executionPlatformVersion) }`;
251
281
  }
252
282
 
253
283
  return message;
254
284
  }
255
285
 
256
286
 
257
- function getPropertyRequiredErrorMessage(report, executionPlatformLabel) {
287
+ function getPropertyRequiredErrorMessage(report) {
258
288
  const {
259
- error,
289
+ data,
260
290
  message
261
291
  } = report;
262
292
 
@@ -264,7 +294,7 @@ function getPropertyRequiredErrorMessage(report, executionPlatformLabel) {
264
294
  node,
265
295
  parentNode,
266
296
  requiredProperty
267
- } = error;
297
+ } = data;
268
298
 
269
299
  const typeString = getTypeString(parentNode || node);
270
300
 
@@ -288,10 +318,6 @@ function getPropertyRequiredErrorMessage(report, executionPlatformLabel) {
288
318
  return `${ getIndefiniteArticle(typeString) } <${ typeString }> with <Error Reference> must have a defined <Error code>`;
289
319
  }
290
320
 
291
- if (is(node, 'bpmn:Event') && requiredProperty === 'eventDefinitions') {
292
- return `${ getIndefiniteArticle(typeString) } <${ typeString }> is not supported by ${ executionPlatformLabel }`;
293
- }
294
-
295
321
  if (is(node, 'zeebe:LoopCharacteristics') && requiredProperty === 'inputCollection') {
296
322
  return `${ getIndefiniteArticle(typeString) } <${ typeString }> with <Multi-instance marker> must have a defined <Input collection>`;
297
323
  }
@@ -339,72 +365,57 @@ function getPropertyRequiredErrorMessage(report, executionPlatformLabel) {
339
365
  return `${ getIndefiniteArticle(typeString) } <${ typeString }> must have a defined <Timer duration>`;
340
366
  }
341
367
 
342
- if (is(node, 'bpmn:FormalExpression')
343
- && requiredProperty === 'body'
344
- && TIMER_PROPERTIES.includes(secondLast(report.path))
345
- ) {
346
- return `${ getIndefiniteArticle(typeString) } <${ typeString }> must have a defined <Timer value>`;
347
- }
348
-
349
368
  return message;
350
369
  }
351
370
 
352
- function getPropertyTypeNotAllowedErrorMessage(report, executionPlatformLabel) {
371
+ function getExpressionRequiredErrorMessage(report) {
353
372
  const {
354
- error,
355
- message
373
+ data
356
374
  } = report;
357
375
 
358
376
  const {
359
377
  node,
360
378
  parentNode,
361
379
  property
362
- } = error;
380
+ } = data;
363
381
 
364
382
  const typeString = getTypeString(parentNode || node);
365
383
 
366
- if (is(node, 'bpmn:Event') && property === 'eventDefinitions') {
367
- return `${ getIndefiniteArticle(typeString) } <${ typeString }> is not supported by ${ executionPlatformLabel }`;
384
+ if (is(node, 'bpmn:FormalExpression') && TIMER_PROPERTIES.includes(property)) {
385
+ return `${ getIndefiniteArticle(typeString) } <${ typeString }> must have a defined <Timer value>`;
368
386
  }
369
-
370
- return message;
371
387
  }
372
388
 
373
- function getPropertyValueNotAllowedErrorMessage(report, executionPlatformLabel) {
389
+ function getExpressionValueNotAllowedErrorMessage(report) {
374
390
  const {
375
- error
391
+ data
376
392
  } = report;
377
393
 
378
394
  const {
379
395
  node,
380
396
  parentNode,
381
397
  property
382
- } = error;
398
+ } = data;
383
399
 
384
400
  const typeString = getTypeString(parentNode || node);
385
401
 
386
- if (is(node, 'bpmn:FormalExpression')
387
- && property === 'body'
388
- && secondLast(report.path) === 'timeCycle'
389
- ) {
402
+ if (is(node, 'bpmn:FormalExpression') && property === 'timeCycle') {
390
403
  return `${ getIndefiniteArticle(typeString) } <${ typeString }> <Time cycle> should be an expression, an ISO 8601 repeating interval, or a cron expression (cron requires Camunda Platform 8.1 or newer)`;
391
404
  }
392
405
 
393
- if (is(node, 'bpmn:FormalExpression')
394
- && property === 'body'
395
- && secondLast(report.path) === 'timeDate'
396
- ) {
406
+ if (is(node, 'bpmn:FormalExpression') && property === 'timeDate') {
397
407
  return `${ getIndefiniteArticle(typeString) } <${ typeString }> <Time date> should be an expression, or an ISO 8601 date`;
398
408
  }
399
409
 
400
- if (is(node, 'bpmn:FormalExpression')
401
- && property === 'body'
402
- && secondLast(report.path) === 'timeDuration'
403
- ) {
410
+ if (is(node, 'bpmn:FormalExpression') && property === 'timeDuration') {
404
411
  return `${ getIndefiniteArticle(typeString) } <${ typeString }> <Time duration> should be an expression, or an ISO 8601 interval`;
405
412
  }
406
413
  }
407
414
 
408
- function secondLast(array) {
409
- return array[array.length - 2];
410
- }
415
+ function getSupportedMessage(prefix, executionPlatform, executionPlatformVersion, allowedVersion) {
416
+ if (allowedVersion) {
417
+ return `${ prefix } is only supported by ${ getExecutionPlatformLabel(executionPlatform, allowedVersion) } or newer`;
418
+ }
419
+
420
+ return `${ prefix } is not supported by ${ getExecutionPlatformLabel(executionPlatform, executionPlatformVersion) }`;
421
+ }
@@ -46,80 +46,85 @@ export function getErrors(reports, element) {
46
46
 
47
47
  export function getEntryIds(report) {
48
48
  const {
49
- error = {},
50
- id
49
+ data = {},
50
+ id,
51
+ path
51
52
  } = report;
52
53
 
53
- if (isExtensionElementRequiredError(error, 'zeebe:CalledDecision', 'bpmn:BusinessRuleTask')) {
54
+ if (isExtensionElementRequiredError(data, 'zeebe:CalledDecision', 'bpmn:BusinessRuleTask')) {
54
55
  return [ 'businessRuleImplementation' ];
55
56
  }
56
57
 
57
- if (isPropertyRequiredError(error, 'errorRef')) {
58
+ if (isPropertyError(data, 'errorRef')) {
58
59
  return [ 'errorRef' ];
59
60
  }
60
61
 
61
- if (isPropertyRequiredError(error, 'messageRef')) {
62
+ if (isPropertyError(data, 'messageRef')) {
62
63
  return [ 'messageRef' ];
63
64
  }
64
65
 
65
- if (isPropertyRequiredError(error, 'decisionId', 'zeebe:CalledDecision')) {
66
+ if (isPropertyError(data, 'decisionId', 'zeebe:CalledDecision')) {
66
67
  return [ 'decisionId' ];
67
68
  }
68
69
 
69
- if (isPropertyRequiredError(error, 'resultVariable', 'zeebe:CalledDecision')) {
70
+ if (isPropertyError(data, 'resultVariable', 'zeebe:CalledDecision')) {
70
71
  return [ 'resultVariable' ];
71
72
  }
72
73
 
73
- if (isPropertyRequiredError(error, 'errorCode', 'bpmn:Error')) {
74
+ if (isPropertyError(data, 'errorCode', 'bpmn:Error')) {
74
75
  return [ 'errorCode' ];
75
76
  }
76
77
 
77
- if (isPropertyRequiredError(error, 'name', 'bpmn:Message')) {
78
+ if (isPropertyError(data, 'name', 'bpmn:Message')) {
78
79
  return [ 'messageName' ];
79
80
  }
80
81
 
81
- if (isExtensionElementRequiredError(error, 'zeebe:LoopCharacteristics', 'bpmn:MultiInstanceLoopCharacteristics')
82
- || isPropertyRequiredError(error, 'inputCollection', 'zeebe:LoopCharacteristics')) {
82
+ if (isExtensionElementRequiredError(data, 'zeebe:LoopCharacteristics', 'bpmn:MultiInstanceLoopCharacteristics')
83
+ || isPropertyError(data, 'inputCollection', 'zeebe:LoopCharacteristics')) {
83
84
  return [ 'multiInstance-inputCollection' ];
84
85
  }
85
86
 
86
- if (isPropertyDependendRequiredError(error, 'outputCollection', 'zeebe:LoopCharacteristics')) {
87
+ if (isPropertyDependendRequiredError(data, 'outputCollection', 'zeebe:LoopCharacteristics')) {
87
88
  return [ 'multiInstance-outputCollection' ];
88
89
  }
89
90
 
90
- if (isPropertyDependendRequiredError(error, 'outputElement', 'zeebe:LoopCharacteristics')) {
91
+ if (isPropertyDependendRequiredError(data, 'outputElement', 'zeebe:LoopCharacteristics')) {
91
92
  return [ 'multiInstance-outputElement' ];
92
93
  }
93
94
 
94
- if (isExtensionElementRequiredError(error, 'zeebe:CalledElement', 'bpmn:CallActivity')
95
- || isPropertyRequiredError(error, 'processId', 'zeebe:CalledElement')) {
95
+ if (isExtensionElementRequiredError(data, 'zeebe:CalledElement', 'bpmn:CallActivity')
96
+ || isPropertyError(data, 'processId', 'zeebe:CalledElement')) {
96
97
  return [ 'targetProcessId' ];
97
98
  }
98
99
 
99
- if (isExtensionElementRequiredError(error, 'zeebe:TaskDefinition')
100
- || isPropertyRequiredError(error, 'type', 'zeebe:TaskDefinition')) {
100
+ if (isExtensionElementRequiredError(data, 'zeebe:TaskDefinition')
101
+ || isPropertyError(data, 'type', 'zeebe:TaskDefinition')) {
101
102
  return [ 'taskDefinitionType' ];
102
103
  }
103
104
 
104
- if (isExtensionElementRequiredError(error, 'zeebe:Subscription')
105
- || isPropertyRequiredError(error, 'correlationKey', 'zeebe:Subscription')) {
105
+ if (isPropertyError(data, 'retries', 'zeebe:TaskDefinition')) {
106
+ return [ 'taskDefinitionRetries' ];
107
+ }
108
+
109
+ if (isExtensionElementRequiredError(data, 'zeebe:Subscription')
110
+ || isPropertyError(data, 'correlationKey', 'zeebe:Subscription')) {
106
111
  return [ 'messageSubscriptionCorrelationKey' ];
107
112
  }
108
113
 
109
- if (isPropertyRequiredError(error, 'formKey', 'zeebe:FormDefinition')) {
114
+ if (isPropertyError(data, 'formKey', 'zeebe:FormDefinition')) {
110
115
  return [ 'customFormKey' ];
111
116
  }
112
117
 
113
- if (isPropertyRequiredError(error, 'body', 'zeebe:UserTaskForm')) {
118
+ if (isPropertyError(data, 'body', 'zeebe:UserTaskForm')) {
114
119
  return [ 'formConfiguration' ];
115
120
  }
116
121
 
117
- if (isPropertyValueDuplicatedError(error, 'values', 'key', 'zeebe:TaskHeaders')) {
122
+ if (isPropertyValueDuplicatedError(data, 'values', 'key', 'zeebe:TaskHeaders')) {
118
123
  const {
119
124
  node,
120
125
  properties,
121
126
  propertiesName
122
- } = error;
127
+ } = data;
123
128
 
124
129
  return properties.map(property => {
125
130
  const index = node.get(propertiesName).indexOf(property);
@@ -128,40 +133,76 @@ export function getEntryIds(report) {
128
133
  });
129
134
  }
130
135
 
131
- if (isExtensionElementNotAllowedError(error, 'zeebe:Properties')) {
132
- const { extensionElement } = error;
136
+ if (isExtensionElementNotAllowedError(data, 'zeebe:Properties')) {
137
+ const { extensionElement } = data;
133
138
 
134
139
  return extensionElement.get('zeebe:properties').map((zeebeProperty, index) => {
135
140
  return `${ id }-extensionProperty-${ index }-name`;
136
141
  });
137
142
  }
138
143
 
139
- if (isPropertyRequiredError(error, 'conditionExpression', 'bpmn:SequenceFlow')) {
144
+ if (isPropertyError(data, 'conditionExpression', 'bpmn:SequenceFlow')) {
140
145
  return [ 'conditionExpression' ];
141
146
  }
142
147
 
143
- if (isPropertyRequiredError(error, 'timeDuration', 'bpmn:TimerEventDefinition')) {
148
+ if (isPropertyError(data, 'timeDuration', 'bpmn:TimerEventDefinition')) {
144
149
  return [ 'timerEventDefinitionDurationValue' ];
145
150
  }
146
151
 
152
+ if (isPropertyError(data, 'completionCondition', 'bpmn:MultiInstanceLoopCharacteristics')) {
153
+ return [ 'multiInstance-completionCondition' ];
154
+ }
155
+
147
156
  if (TIMER_PROPERTIES.some(property =>
148
- isOneOfPropertiesRequiredError(error, property , 'bpmn:TimerEventDefinition'))
157
+ isOneOfPropertiesRequiredError(data, property, 'bpmn:TimerEventDefinition'))
149
158
  ) {
150
159
  return [ 'timerEventDefinitionType' ];
151
160
  }
152
161
 
153
- if (isPropertyRequiredError(error, 'body', 'bpmn:FormalExpression') && TIMER_PROPERTIES.includes(secondLast(report.path))) {
154
- return hasOnlyDurationTimer(error.parentNode) ? [ 'timerEventDefinitionDurationValue' ] : [ 'timerEventDefinitionValue' ];
162
+ if (isExpressionRequiredError(data, 'timeCycle', 'bpmn:FormalExpression')
163
+ || isExpressionRequiredError(data, 'timeDate', 'bpmn:FormalExpression')
164
+ || isExpressionRequiredError(data, 'timeDuration', 'bpmn:FormalExpression')) {
165
+ return hasOnlyDurationTimer(data.parentNode) ? [ 'timerEventDefinitionDurationValue' ] : [ 'timerEventDefinitionValue' ];
155
166
  }
156
167
 
157
- if (isPropertyValueNotAllowedError(error, 'body', 'bpmn:FormalExpression') && TIMER_PROPERTIES.includes(secondLast(report.path))) {
158
- return hasOnlyDurationTimer(error.parentNode) ? [ 'timerEventDefinitionDurationValue' ] : [ 'timerEventDefinitionValue' ];
168
+ if (isExpressionValueNotAllowedError(data, 'timeCycle', 'bpmn:FormalExpression')
169
+ || isExpressionValueNotAllowedError(data, 'timeDate', 'bpmn:FormalExpression')
170
+ || isExpressionValueNotAllowedError(data, 'timeDuration', 'bpmn:FormalExpression')) {
171
+ return hasOnlyDurationTimer(data.parentNode) ? [ 'timerEventDefinitionDurationValue' ] : [ 'timerEventDefinitionValue' ];
172
+ }
173
+
174
+ const LIST_PROPERTIES = [
175
+ [ 'zeebe:Input', 'input' ],
176
+ [ 'zeebe:Output', 'output' ],
177
+ [ 'zeebe:Property', 'extensionProperty' ],
178
+ [ 'zeebe:Header', 'header' ]
179
+ ];
180
+
181
+ for (const [ type, prefix ] of LIST_PROPERTIES) {
182
+ if (hasType(data, type)
183
+ && getPropertyName(data)) {
184
+
185
+ const index = path[path.length - 2];
186
+
187
+ return [ `${ id }-${prefix}-${index}-${ getPropertyName(data) }` ];
188
+ }
189
+ }
190
+
191
+ if (hasType(data, 'zeebe:LoopCharacteristics')) {
192
+ return [ `multiInstance-${getPropertyName(data)}` ];
159
193
  }
160
194
 
161
195
  return [];
162
196
  }
163
197
 
164
198
  export function getErrorMessage(id, report) {
199
+ const { data } = report;
200
+
201
+ // do not override FEEL message
202
+ if (data.type === ERROR_TYPES.FEEL_EXPRESSION_INVALID) {
203
+ return;
204
+ }
205
+
165
206
  if (id === 'businessRuleImplementation') {
166
207
  return 'Implementation must be defined.';
167
208
  }
@@ -235,16 +276,16 @@ export function getErrorMessage(id, report) {
235
276
  }
236
277
 
237
278
  if (id === 'timerEventDefinitionDurationValue') {
238
- return report.error.type === ERROR_TYPES.PROPERTY_REQUIRED ?
279
+ return data.type === ERROR_TYPES.EXPRESSION_REQUIRED ?
239
280
  'Duration must be defined.' : 'Must be an expression, or an ISO 8601 interval.';
240
281
  }
241
282
 
242
283
  if (id === 'timerEventDefinitionValue') {
243
- if (report.error.type === ERROR_TYPES.PROPERTY_REQUIRED) {
284
+ if (data.type === ERROR_TYPES.EXPRESSION_REQUIRED) {
244
285
  return 'Value must be defined.';
245
286
  }
246
287
 
247
- const property = secondLast(report.path);
288
+ const { property } = data;
248
289
 
249
290
  if (property === 'timeCycle') {
250
291
  return 'Must be an expression, an ISO 8601 repeating interval, or a cron expression (cron requires Camunda Platform 8.1 or newer).';
@@ -260,56 +301,68 @@ export function getErrorMessage(id, report) {
260
301
  }
261
302
  }
262
303
 
263
- function isExtensionElementNotAllowedError(error, extensionElement, type) {
264
- return error.type === ERROR_TYPES.EXTENSION_ELEMENT_NOT_ALLOWED
265
- && is(error.extensionElement, extensionElement)
266
- && (!type || is(error.node, type));
304
+ function isExtensionElementNotAllowedError(data, extensionElement, type) {
305
+ return data.type === ERROR_TYPES.EXTENSION_ELEMENT_NOT_ALLOWED
306
+ && is(data.extensionElement, extensionElement)
307
+ && (!type || is(data.node, type));
267
308
  }
268
309
 
269
- function isExtensionElementRequiredError(error, requiredExtensionElement, type) {
270
- return error.type === ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED
271
- && (isArray(error.requiredExtensionElement) && error.requiredExtensionElement.includes(requiredExtensionElement)
272
- || error.requiredExtensionElement === requiredExtensionElement)
273
- && (!type || is(error.node, type));
310
+ function isExtensionElementRequiredError(data, requiredExtensionElement, type) {
311
+ return data.type === ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED
312
+ && (isArray(data.requiredExtensionElement) && data.requiredExtensionElement.includes(requiredExtensionElement)
313
+ || data.requiredExtensionElement === requiredExtensionElement)
314
+ && (!type || is(data.node, type));
274
315
  }
275
316
 
276
- function isPropertyDependendRequiredError(error, dependendRequiredProperty, type) {
277
- return error.type === ERROR_TYPES.PROPERTY_DEPENDEND_REQUIRED
278
- && error.dependendRequiredProperty === dependendRequiredProperty
279
- && (!type || is(error.node, type));
317
+ function isPropertyDependendRequiredError(data, dependendRequiredProperty, type) {
318
+ return data.type === ERROR_TYPES.PROPERTY_DEPENDEND_REQUIRED
319
+ && data.dependendRequiredProperty === dependendRequiredProperty
320
+ && (!type || is(data.node, type));
280
321
  }
281
322
 
282
- function isPropertyRequiredError(error, requiredProperty, type) {
283
- return error.type === ERROR_TYPES.PROPERTY_REQUIRED
284
- && (error.requiredProperty === requiredProperty)
285
- && (!type || is(error.node, type));
323
+
324
+ function isPropertyError(data, property, type) {
325
+ return getPropertyName(data) === property
326
+ && (!type || is(data.node, type));
286
327
  }
287
328
 
288
- function isOneOfPropertiesRequiredError(error, requiredProperty, type) {
289
- return error.type === ERROR_TYPES.PROPERTY_REQUIRED
290
- && (isArray(error.requiredProperty) && error.requiredProperty.includes(requiredProperty))
291
- && (!type || is(error.node, type));
329
+ function hasType(data, type) {
330
+ return data.node && is(data.node, type);
292
331
  }
293
332
 
294
- function isPropertyValueDuplicatedError(error, propertiesName, duplicatedProperty, type) {
295
- return error.type === ERROR_TYPES.PROPERTY_VALUE_DUPLICATED
296
- && error.propertiesName === propertiesName
297
- && error.duplicatedProperty === duplicatedProperty
298
- && (!type || is(error.node, type));
333
+ function getPropertyName(data) {
334
+ const propertyKey = data.type === ERROR_TYPES.PROPERTY_REQUIRED ? 'requiredProperty' : 'property';
335
+
336
+ return data[ propertyKey ];
299
337
  }
300
338
 
301
- function isPropertyValueNotAllowedError(error, propertyName, type) {
302
- return error.type === ERROR_TYPES.PROPERTY_VALUE_NOT_ALLOWED
303
- && error.property === propertyName
304
- && (!type || is(error.node, type));
339
+ function isOneOfPropertiesRequiredError(data, requiredProperty, type) {
340
+ return data.type === ERROR_TYPES.PROPERTY_REQUIRED
341
+ && (isArray(data.requiredProperty) && data.requiredProperty.includes(requiredProperty))
342
+ && (!type || is(data.node, type));
305
343
  }
306
344
 
307
- function getBusinessObject(element) {
308
- return element.businessObject || element;
345
+ function isPropertyValueDuplicatedError(data, propertiesName, duplicatedProperty, type) {
346
+ return data.type === ERROR_TYPES.PROPERTY_VALUE_DUPLICATED
347
+ && data.propertiesName === propertiesName
348
+ && data.duplicatedProperty === duplicatedProperty
349
+ && (!type || is(data.node, type));
309
350
  }
310
351
 
311
- function secondLast(array) {
312
- return array[array.length - 2];
352
+ function isExpressionRequiredError(data, propertyName, type) {
353
+ return data.type === ERROR_TYPES.EXPRESSION_REQUIRED
354
+ && data.property === propertyName
355
+ && (!type || is(data.node, type));
356
+ }
357
+
358
+ function isExpressionValueNotAllowedError(data, propertyName, type) {
359
+ return data.type === ERROR_TYPES.EXPRESSION_VALUE_NOT_ALLOWED
360
+ && data.property === propertyName
361
+ && (!type || is(data.node, type));
362
+ }
363
+
364
+ function getBusinessObject(element) {
365
+ return element.businessObject || element;
313
366
  }
314
367
 
315
368
  function hasOnlyDurationTimer(node) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/linting",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Linting for Camunda Platform",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -27,7 +27,7 @@
27
27
  "dependencies": {
28
28
  "bpmn-moddle": "^7.1.3",
29
29
  "bpmnlint": "^8.0.0",
30
- "bpmnlint-plugin-camunda-compat": "^0.13.1",
30
+ "bpmnlint-plugin-camunda-compat": "^0.14.1",
31
31
  "bpmnlint-utils": "^1.0.2",
32
32
  "min-dash": "^4.0.0",
33
33
  "min-dom": "^4.0.1",