@camunda/linting 0.3.5 → 0.4.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/CHANGELOG.md CHANGED
@@ -6,6 +6,10 @@ All notable changes to [@camunda/linting](https://github.com/camunda/linting) ar
6
6
 
7
7
  ___Note:__ Yet to be released changes appear here._
8
8
 
9
+ ## 0.4.0
10
+
11
+ * `FEAT`: add duplicate task headers rule ([#41](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/41))
12
+
9
13
  ## 0.3.5
10
14
 
11
15
  * `FIX`: ignore null properties ([#39](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/39))
package/lib/Linter.js CHANGED
@@ -12,7 +12,7 @@ import modelerModdle from 'modeler-moddle/resources/modeler.json';
12
12
  import zeebeModdle from 'zeebe-bpmn-moddle/resources/zeebe.json';
13
13
 
14
14
  import { getErrorMessage } from './utils/error-messages';
15
- import { getEntryId } from './utils/properties-panel';
15
+ import { getEntryIds } from './utils/properties-panel';
16
16
 
17
17
  const moddle = new BpmnModdle({
18
18
  modeler: modelerModdle,
@@ -63,6 +63,8 @@ export class Linter {
63
63
  return [
64
64
  ...allReports,
65
65
  ...reports.map(report => {
66
+ const entryIds = getEntryIds(report);
67
+
66
68
  return {
67
69
  ...report,
68
70
  message: getErrorMessage(
@@ -71,7 +73,7 @@ export class Linter {
71
73
  this._modeler
72
74
  ),
73
75
  propertiesPanel: {
74
- entryId: getEntryId(report)
76
+ entryId: entryIds[ Math.max(0, entryIds.length - 1) ]
75
77
  }
76
78
  };
77
79
  })
@@ -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
+ import { every } from 'min-dash';
4
5
 
5
6
  import { isArray } from 'min-dash';
6
7
 
@@ -46,6 +47,10 @@ export function getErrorMessage(report, executionPlatformLabel, modeler = 'deskt
46
47
  return getPropertyTypeNotAllowedErrorMessage(report, executionPlatformLabel);
47
48
  }
48
49
 
50
+ if (type === ERROR_TYPES.PROPERTY_VALUE_DUPLICATED) {
51
+ return getPropertyValueDuplicatedErrorMessage(report);
52
+ }
53
+
49
54
  return message;
50
55
  }
51
56
 
@@ -73,6 +78,28 @@ function getElementTypeNotAllowedErrorMessage(report, executionPlatformLabel) {
73
78
  return `${ getIndefiniteArticle(typeString) } <${ typeString }> is not supported by ${ executionPlatformLabel }`;
74
79
  }
75
80
 
81
+ function getPropertyValueDuplicatedErrorMessage(report) {
82
+ const {
83
+ error,
84
+ message
85
+ } = report;
86
+
87
+ const {
88
+ node,
89
+ parentNode,
90
+ duplicatedPropertyValue,
91
+ properties
92
+ } = error;
93
+
94
+ const typeString = getTypeString(parentNode || node);
95
+
96
+ if (is(node, 'zeebe:TaskHeaders') && every(properties, property => is(property, 'zeebe:Header'))) {
97
+ return `${ getIndefiniteArticle(typeString) } <${ typeString }> with two or more <Headers> with the same <Key> (${ duplicatedPropertyValue }) is not supported`;
98
+ }
99
+
100
+ return message;
101
+ }
102
+
76
103
  function getExtensionElementNotAllowedErrorMessage(report, executionPlatformLabel) {
77
104
  const {
78
105
  error,
@@ -4,107 +4,125 @@ import { is } from 'bpmnlint-utils';
4
4
 
5
5
  import { ERROR_TYPES } from 'bpmnlint-plugin-camunda-compat/rules/utils/error-types';
6
6
 
7
- export function getEntryId(report) {
7
+ /**
8
+ * Get errors for a given element.
9
+ *
10
+ * @param {Object[]} reports
11
+ * @param {Object} element
12
+ *
13
+ * @returns {Object}
14
+ */
15
+ export function getErrors(reports, element) {
16
+ return reports.reduce((errors, report) => {
17
+ if (!element || getBusinessObject(element).get('id') !== report.id) {
18
+ return errors;
19
+ }
20
+
21
+ const ids = getEntryIds(report);
22
+
23
+ if (!ids.length) {
24
+ return errors;
25
+ }
26
+
27
+ let { message } = report;
28
+
29
+ return {
30
+ ...errors,
31
+ ...ids.reduce((errors, id) => {
32
+ return {
33
+ ...errors,
34
+ [ id ]: getErrorMessage(id) || message
35
+ };
36
+ }, {})
37
+ };
38
+ }, {});
39
+ }
40
+
41
+ export function getEntryIds(report) {
8
42
  const {
9
- error = {}
43
+ error = {},
44
+ id
10
45
  } = report;
11
46
 
12
47
  if (isExtensionElementRequiredError(error, 'zeebe:CalledDecision', 'bpmn:BusinessRuleTask')) {
13
- return 'businessRuleImplementation';
48
+ return [ 'businessRuleImplementation' ];
14
49
  }
15
50
 
16
51
  if (isPropertyRequiredError(error, 'errorRef')) {
17
- return 'errorRef';
52
+ return [ 'errorRef' ];
18
53
  }
19
54
 
20
55
  if (isPropertyRequiredError(error, 'messageRef')) {
21
- return 'messageRef';
56
+ return [ 'messageRef' ];
22
57
  }
23
58
 
24
59
  if (isPropertyRequiredError(error, 'decisionId', 'zeebe:CalledDecision')) {
25
- return 'decisionId';
60
+ return [ 'decisionId' ];
26
61
  }
27
62
 
28
63
  if (isPropertyRequiredError(error, 'resultVariable', 'zeebe:CalledDecision')) {
29
- return 'resultVariable';
64
+ return [ 'resultVariable' ];
30
65
  }
31
66
 
32
67
  if (isPropertyRequiredError(error, 'errorCode', 'bpmn:Error')) {
33
- return 'errorCode';
68
+ return [ 'errorCode' ];
34
69
  }
35
70
 
36
71
  if (isPropertyRequiredError(error, 'name', 'bpmn:Message')) {
37
- return 'messageName';
72
+ return [ 'messageName' ];
38
73
  }
39
74
 
40
75
  if (isExtensionElementRequiredError(error, 'zeebe:LoopCharacteristics', 'bpmn:MultiInstanceLoopCharacteristics')
41
76
  || isPropertyRequiredError(error, 'inputCollection', 'zeebe:LoopCharacteristics')) {
42
- return 'multiInstance-inputCollection';
77
+ return [ 'multiInstance-inputCollection' ];
43
78
  }
44
79
 
45
80
  if (isPropertyDependendRequiredError(error, 'outputCollection', 'zeebe:LoopCharacteristics')) {
46
- return 'multiInstance-outputCollection';
81
+ return [ 'multiInstance-outputCollection' ];
47
82
  }
48
83
 
49
84
  if (isPropertyDependendRequiredError(error, 'outputElement', 'zeebe:LoopCharacteristics')) {
50
- return 'multiInstance-outputElement';
85
+ return [ 'multiInstance-outputElement' ];
51
86
  }
52
87
 
53
88
  if (isExtensionElementRequiredError(error, 'zeebe:CalledElement', 'bpmn:CallActivity')
54
89
  || isPropertyRequiredError(error, 'processId', 'zeebe:CalledElement')) {
55
- return 'targetProcessId';
90
+ return [ 'targetProcessId' ];
56
91
  }
57
92
 
58
93
  if (isExtensionElementRequiredError(error, 'zeebe:TaskDefinition')
59
94
  || isPropertyRequiredError(error, 'type', 'zeebe:TaskDefinition')) {
60
- return 'taskDefinitionType';
95
+ return [ 'taskDefinitionType' ];
61
96
  }
62
97
 
63
98
  if (isExtensionElementRequiredError(error, 'zeebe:Subscription')
64
99
  || isPropertyRequiredError(error, 'correlationKey', 'zeebe:Subscription')) {
65
- return 'messageSubscriptionCorrelationKey';
100
+ return [ 'messageSubscriptionCorrelationKey' ];
66
101
  }
67
102
 
68
103
  if (isPropertyRequiredError(error, 'formKey', 'zeebe:FormDefinition')) {
69
- return 'customFormKey';
104
+ return [ 'customFormKey' ];
70
105
  }
71
106
 
72
107
  if (isPropertyRequiredError(error, 'body', 'zeebe:UserTaskForm')) {
73
- return 'formConfiguration';
108
+ return [ 'formConfiguration' ];
74
109
  }
75
110
 
76
- return null;
77
- }
78
-
79
- /**
80
- * Get errors for a given element.
81
- *
82
- * @param {Object[]} reports
83
- * @param {Object} element
84
- *
85
- * @returns {Object}
86
- */
87
- export function getErrors(reports, element) {
88
- return reports.reduce((errors, report) => {
89
- if (!element || getBusinessObject(element).get('id') !== report.id) {
90
- return errors;
91
- }
92
-
93
- const id = getEntryId(report);
111
+ if (isPropertyValueDuplicatedError(error, 'values', 'key', 'zeebe:TaskHeaders')) {
112
+ const {
113
+ node,
114
+ properties,
115
+ propertiesName
116
+ } = error;
94
117
 
95
- if (!id) {
96
- return errors;
97
- }
98
-
99
- let { message } = report;
118
+ return properties.map(property => {
119
+ const index = node.get(propertiesName).indexOf(property);
100
120
 
101
- message = getErrorMessage(id) || message;
121
+ return `${ id }-header-${ index }-key`;
122
+ });
123
+ }
102
124
 
103
- return {
104
- ...errors,
105
- [ id ]: message
106
- };
107
- }, {});
125
+ return [];
108
126
  }
109
127
 
110
128
  export function getErrorMessage(id) {
@@ -167,6 +185,10 @@ export function getErrorMessage(id) {
167
185
  if (id === 'formConfiguration') {
168
186
  return 'Form JSON configuration must be defined.';
169
187
  }
188
+
189
+ if (/^.+-header-[0-9]+-key$/.test(id)) {
190
+ return 'Must be unique.';
191
+ }
170
192
  }
171
193
 
172
194
  function isExtensionElementRequiredError(error, requiredExtensionElement, type) {
@@ -188,6 +210,13 @@ function isPropertyRequiredError(error, requiredProperty, type) {
188
210
  && (!type || is(error.node, type));
189
211
  }
190
212
 
213
+ function isPropertyValueDuplicatedError(error, propertiesName, duplicatedProperty, type) {
214
+ return error.type === ERROR_TYPES.PROPERTY_VALUE_DUPLICATED
215
+ && error.propertiesName === propertiesName
216
+ && error.duplicatedProperty === duplicatedProperty
217
+ && (!type || is(error.node, type));
218
+ }
219
+
191
220
  function getBusinessObject(element) {
192
221
  return element.businessObject || element;
193
222
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/linting",
3
- "version": "0.3.5",
3
+ "version": "0.4.0",
4
4
  "description": "Linting for Camunda Platform",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -17,11 +17,15 @@
17
17
  "name": "Philipp Fromme",
18
18
  "url": "https://github.com/philippfromme"
19
19
  },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/camunda/linting"
23
+ },
20
24
  "license": "MIT",
21
25
  "dependencies": {
22
26
  "bpmn-moddle": "^7.1.2",
23
27
  "bpmnlint": "^7.8.0",
24
- "bpmnlint-plugin-camunda-compat": "^0.9.2",
28
+ "bpmnlint-plugin-camunda-compat": "^0.10.0",
25
29
  "bpmnlint-utils": "^1.0.2",
26
30
  "modeler-moddle": "^0.2.0",
27
31
  "zeebe-bpmn-moddle": "^0.12.1"