@camunda/linting 0.1.1 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,24 @@ 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.3.1
10
+
11
+ * `FIX`: include `properties-panel.js` in published package ([a532ba5b](https://github.com/camunda/linting/commit/a532ba5b7bf0b126477c218484e668c418875b4e))
12
+
13
+ ## 0.3.0
14
+
15
+ * `FEAT`: add properties panel entry ID to reports ([#7](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/7))
16
+ * `FEAT`: add #getErrors function that creates properties panel errors from reports ([#7](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/7))
17
+ * `FEAT`: adjust connectors error message ([#6](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/6))
18
+
19
+ ### BREAKING CHANGES
20
+
21
+ * #lint is not static anymore, Linter must be instantiated
22
+
23
+ ## 0.2.0
24
+
25
+ * `FEAT`: add templates rule ([#31](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/31))
26
+
9
27
  ## 0.1.1
10
28
 
11
29
  * `FIX`: lint subscription only if start event child of sub process ([#34](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/34))
package/README.md CHANGED
@@ -6,20 +6,32 @@ The BPMN linter used by the Camunda Desktop and Web Modeler. Batteries included.
6
6
 
7
7
  * bundles [bpmnlint](https://github.com/bpmn-io/bpmnlint) and [bpmnlint-plugin-camunda-compat](https://github.com/camunda/bpmnlint-plugin-camunda-compat/)
8
8
  * configures linter based on `modeler:executionPlatform` and `modeler:executionPlatformVersion`
9
- * adjusts error messages to be shown in desktop and web modeler
9
+ * creates error messages to be shown in desktop and web modeler
10
+ * creates errors to be shown in properties panel
10
11
 
11
12
  # Usage
12
13
 
13
14
  ```javascript
14
15
  import { Linter } from '@camunda/linting';
16
+ import { getErrors } from '@camunda/linting/properties-panel';
15
17
 
16
- ...
18
+ // configure to be used with desktop or web modeler
19
+ const linter = new Linter({
20
+ modeler: 'web'
21
+ });
17
22
 
18
23
  // lint by passing definitions
19
- const reports = await Linter.lint(definitions);
24
+ const reports = await linter.lint(definitions);
20
25
 
21
26
  // or passing XML
22
- const reports = await Linter.lint(xml);
27
+ const reports = await linter.lint(xml);
28
+
29
+ ...
30
+
31
+ const errors = getErrors(reports, element);
32
+
33
+ // bpmn-js-properties-panel >=1.3.0
34
+ eventBus.fire('propertiesPanel.setErrors', { errors });
23
35
  ```
24
36
 
25
37
  # License
package/lib/Linter.js CHANGED
@@ -11,7 +11,8 @@ import { isString } from 'min-dash';
11
11
  import modelerModdle from 'modeler-moddle/resources/modeler.json';
12
12
  import zeebeModdle from 'zeebe-bpmn-moddle/resources/zeebe.json';
13
13
 
14
- import { adjustErrorMessages } from './utils/error-messages';
14
+ import { getErrorMessage } from './utils/error-messages';
15
+ import { getEntryId } from './utils/properties-panel';
15
16
 
16
17
  const moddle = new BpmnModdle({
17
18
  modeler: modelerModdle,
@@ -19,7 +20,13 @@ const moddle = new BpmnModdle({
19
20
  });
20
21
 
21
22
  export class Linter {
22
- static async lint(contents) {
23
+ constructor(options = {}) {
24
+ const { modeler = 'desktop' } = options;
25
+
26
+ this._modeler = modeler;
27
+ }
28
+
29
+ async lint(contents) {
23
30
  let rootElement;
24
31
 
25
32
  if (isString(contents)) {
@@ -50,12 +57,25 @@ export class Linter {
50
57
  resolver: new StaticResolver(rules)
51
58
  });
52
59
 
53
- let reports = await linter.lint(rootElement);
54
-
55
- reports = adjustErrorMessages(reports, getExecutionPlatformLabel(executionPlatform, toSemverMinor(executionPlatformVersion)));
56
-
57
- return Object.values(reports).reduce((reports, report) => {
58
- return [ ...reports, ...report ];
60
+ const allReports = await linter.lint(rootElement);
61
+
62
+ return Object.values(allReports).reduce((allReports, reports) => {
63
+ return [
64
+ ...allReports,
65
+ ...reports.map(report => {
66
+ return {
67
+ ...report,
68
+ message: getErrorMessage(
69
+ report,
70
+ getExecutionPlatformLabel(executionPlatform, toSemverMinor(executionPlatformVersion)),
71
+ this._modeler
72
+ ),
73
+ propertiesPanel: {
74
+ entryId: getEntryId(report)
75
+ }
76
+ };
77
+ })
78
+ ];
59
79
  }, []);
60
80
  }
61
81
  }
@@ -6,7 +6,7 @@ import { isArray } from 'min-dash';
6
6
 
7
7
  import { getTypeString } from './types';
8
8
 
9
- export function adjustErrorMessage(report, executionPlatformLabel) {
9
+ export function getErrorMessage(report, executionPlatformLabel, modeler = 'desktop') {
10
10
  const {
11
11
  error,
12
12
  message
@@ -34,6 +34,10 @@ export function adjustErrorMessage(report, executionPlatformLabel) {
34
34
  return getPropertyDependendRequiredErrorMessage(report);
35
35
  }
36
36
 
37
+ if (type === ERROR_TYPES.PROPERTY_NOT_ALLOWED) {
38
+ return getPropertyNotAllowedErrorMessage(report, executionPlatformLabel, modeler);
39
+ }
40
+
37
41
  if (type === ERROR_TYPES.PROPERTY_REQUIRED) {
38
42
  return getPropertyRequiredErrorMessage(report, executionPlatformLabel);
39
43
  }
@@ -45,16 +49,6 @@ export function adjustErrorMessage(report, executionPlatformLabel) {
45
49
  return message;
46
50
  }
47
51
 
48
- export function adjustErrorMessages(reports, executionPlatformLabel) {
49
- Object.values(reports).forEach(reportsForRule => {
50
- reportsForRule.forEach(report => {
51
- report.message = adjustErrorMessage(report, executionPlatformLabel);
52
- });
53
- });
54
-
55
- return reports;
56
- }
57
-
58
52
  function getIndefiniteArticle(type) {
59
53
  if ([
60
54
  'Error',
@@ -160,6 +154,32 @@ function getPropertyDependendRequiredErrorMessage(report) {
160
154
  return message;
161
155
  }
162
156
 
157
+ function getPropertyNotAllowedErrorMessage(report, executionPlatformLabel, modeler = 'desktop') {
158
+ const {
159
+ error,
160
+ message
161
+ } = report;
162
+
163
+ const {
164
+ node,
165
+ parentNode,
166
+ property
167
+ } = error;
168
+
169
+ if (property === 'modelerTemplate') {
170
+ const typeString = getTypeString(parentNode || node);
171
+
172
+ if (modeler === 'desktop') {
173
+ return `${ getIndefiniteArticle(typeString) } <Template ${ typeString }> is not supported by ${ executionPlatformLabel }`;
174
+ } else if (modeler === 'web') {
175
+ return `${ getIndefiniteArticle(typeString) } <Connector ${ typeString }> is not supported by ${ executionPlatformLabel }`;
176
+ }
177
+ }
178
+
179
+ return message;
180
+ }
181
+
182
+
163
183
  function getPropertyRequiredErrorMessage(report, executionPlatformLabel) {
164
184
  const {
165
185
  error,
@@ -222,6 +242,14 @@ function getPropertyRequiredErrorMessage(report, executionPlatformLabel) {
222
242
  return `${ getIndefiniteArticle(typeString) } <${ typeString }> must have a defined <Message Reference>`;
223
243
  }
224
244
 
245
+ if (is(node, 'zeebe:FormDefinition') && requiredProperty === 'formKey') {
246
+ return `${ getIndefiniteArticle(typeString) } <${ typeString }> with <Form type: Custom form key> must have a defined <Form key>`;
247
+ }
248
+
249
+ if (is(node, 'zeebe:UserTaskForm') && requiredProperty === 'body') {
250
+ return `${ getIndefiniteArticle(typeString) } <${ typeString }> with <Form type: Camunda forms> must have a defined <Form JSON configuration>`;
251
+ }
252
+
225
253
  return message;
226
254
  }
227
255
 
@@ -0,0 +1,193 @@
1
+ import { isArray } from 'min-dash';
2
+
3
+ import { is } from 'bpmnlint-utils';
4
+
5
+ import { ERROR_TYPES } from 'bpmnlint-plugin-camunda-compat/rules/utils/error-types';
6
+
7
+ export function getEntryId(report) {
8
+ const {
9
+ error = {}
10
+ } = report;
11
+
12
+ if (isExtensionElementRequiredError(error, 'zeebe:CalledDecision', 'bpmn:BusinessRuleTask')) {
13
+ return 'businessRuleImplementation';
14
+ }
15
+
16
+ if (isPropertyRequiredError(error, 'errorRef')) {
17
+ return 'errorRef';
18
+ }
19
+
20
+ if (isPropertyRequiredError(error, 'messageRef')) {
21
+ return 'messageRef';
22
+ }
23
+
24
+ if (isPropertyRequiredError(error, 'decisionId', 'zeebe:CalledDecision')) {
25
+ return 'decisionId';
26
+ }
27
+
28
+ if (isPropertyRequiredError(error, 'resultVariable', 'zeebe:CalledDecision')) {
29
+ return 'resultVariable';
30
+ }
31
+
32
+ if (isPropertyRequiredError(error, 'errorCode', 'bpmn:Error')) {
33
+ return 'errorCode';
34
+ }
35
+
36
+ if (isPropertyRequiredError(error, 'name', 'bpmn:Message')) {
37
+ return 'messageName';
38
+ }
39
+
40
+ if (isExtensionElementRequiredError(error, 'zeebe:LoopCharacteristics', 'bpmn:MultiInstanceLoopCharacteristics')
41
+ || isPropertyRequiredError(error, 'inputCollection', 'zeebe:LoopCharacteristics')) {
42
+ return 'multiInstance-inputCollection';
43
+ }
44
+
45
+ if (isPropertyDependendRequiredError(error, 'outputCollection', 'zeebe:LoopCharacteristics')) {
46
+ return 'multiInstance-outputCollection';
47
+ }
48
+
49
+ if (isPropertyDependendRequiredError(error, 'outputElement', 'zeebe:LoopCharacteristics')) {
50
+ return 'multiInstance-outputElement';
51
+ }
52
+
53
+ if (isExtensionElementRequiredError(error, 'zeebe:CalledElement', 'bpmn:CallActivity')
54
+ || isPropertyRequiredError(error, 'processId', 'zeebe:CalledElement')) {
55
+ return 'targetProcessId';
56
+ }
57
+
58
+ if (isExtensionElementRequiredError(error, 'zeebe:TaskDefinition')
59
+ || isPropertyRequiredError(error, 'type', 'zeebe:TaskDefinition')) {
60
+ return 'taskDefinitionType';
61
+ }
62
+
63
+ if (isExtensionElementRequiredError(error, 'zeebe:Subscription')
64
+ || isPropertyRequiredError(error, 'correlationKey', 'zeebe:Subscription')) {
65
+ return 'messageSubscriptionCorrelationKey';
66
+ }
67
+
68
+ if (isPropertyRequiredError(error, 'formKey', 'zeebe:FormDefinition')) {
69
+ return 'customFormKey';
70
+ }
71
+
72
+ if (isPropertyRequiredError(error, 'body', 'zeebe:UserTaskForm')) {
73
+ return 'formConfiguration';
74
+ }
75
+
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);
94
+
95
+ if (!id) {
96
+ return errors;
97
+ }
98
+
99
+ let { message } = report;
100
+
101
+ message = getErrorMessage(id) || message;
102
+
103
+ return {
104
+ ...errors,
105
+ [ id ]: message
106
+ };
107
+ }, {});
108
+ }
109
+
110
+ export function getErrorMessage(id) {
111
+ if (id === 'businessRuleTaskImplementation') {
112
+ return 'Implementation must be defined.';
113
+ }
114
+
115
+ if (id === 'errorRef') {
116
+ return 'Global error reference must be defined.';
117
+ }
118
+
119
+ if (id === 'messageRef') {
120
+ return 'Global message reference must be defined.';
121
+ }
122
+
123
+ if (id === 'decisionId') {
124
+ return 'Decision ID must be defined.';
125
+ }
126
+
127
+ if (id === 'resultVariable') {
128
+ return 'Result variable must be defined.';
129
+ }
130
+
131
+ if (id === 'errorCode') {
132
+ return 'Code must be defined.';
133
+ }
134
+
135
+ if (id === 'messageName') {
136
+ return 'Name must be defined.';
137
+ }
138
+
139
+ if (id === 'multiInstance-inputCollection') {
140
+ return 'Input collection must be defined.';
141
+ }
142
+
143
+ if (id === 'multiInstance-outputCollection') {
144
+ return 'Output collection must be defined.';
145
+ }
146
+
147
+ if (id === 'multiInstance-outputElement') {
148
+ return 'Output element must be defined.';
149
+ }
150
+
151
+ if (id === 'targetProcessId') {
152
+ return 'Process ID must be defined.';
153
+ }
154
+
155
+ if (id === 'taskDefinitionType') {
156
+ return 'Type must be defined.';
157
+ }
158
+
159
+ if (id === 'messageSubscriptionCorrelationKey') {
160
+ return 'Subscription correlation key must be defined.';
161
+ }
162
+
163
+ if (id === 'customFormKey') {
164
+ return 'Form key must be defined.';
165
+ }
166
+
167
+ if (id === 'formConfiguration') {
168
+ return 'Form JSON configuration must be defined.';
169
+ }
170
+ }
171
+
172
+ function isExtensionElementRequiredError(error, requiredExtensionElement, type) {
173
+ return error.type === ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED
174
+ && (isArray(error.requiredExtensionElement) && error.requiredExtensionElement.includes(requiredExtensionElement)
175
+ || error.requiredExtensionElement === requiredExtensionElement)
176
+ && (!type || is(error.node, type));
177
+ }
178
+
179
+ function isPropertyDependendRequiredError(error, dependendRequiredProperty, type) {
180
+ return error.type === ERROR_TYPES.PROPERTY_DEPENDEND_REQUIRED
181
+ && error.dependendRequiredProperty === dependendRequiredProperty
182
+ && (!type || is(error.node, type));
183
+ }
184
+
185
+ function isPropertyRequiredError(error, requiredProperty, type) {
186
+ return error.type === ERROR_TYPES.PROPERTY_REQUIRED
187
+ && error.requiredProperty === requiredProperty
188
+ && (!type || is(error.node, type));
189
+ }
190
+
191
+ function getBusinessObject(element) {
192
+ return element.businessObject || element;
193
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/linting",
3
- "version": "0.1.1",
3
+ "version": "0.3.1",
4
4
  "description": "Linting for Camunda Platform",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -20,7 +20,7 @@
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
22
  "bpmnlint": "^7.8.0",
23
- "bpmnlint-plugin-camunda-compat": "^0.7.1",
23
+ "bpmnlint-plugin-camunda-compat": "^0.9.1",
24
24
  "bpmnlint-utils": "^1.0.2"
25
25
  },
26
26
  "devDependencies": {
@@ -34,8 +34,17 @@
34
34
  "modeler-moddle": "^0.2.0",
35
35
  "zeebe-bpmn-moddle": "^0.12.1"
36
36
  },
37
+ "peerDependencies": {
38
+ "bpmn-js-properties-panel": "1.3.x"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "bpmn-js-properties-panel": {
42
+ "optional": true
43
+ }
44
+ },
37
45
  "files": [
38
- "lib"
46
+ "lib",
47
+ "properties-panel.js"
39
48
  ],
40
49
  "publishConfig": {
41
50
  "access": "public"
@@ -0,0 +1 @@
1
+ export * from './lib/utils/properties-panel';