@camunda/linting 0.1.0 → 0.3.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 +18 -0
- package/README.md +16 -4
- package/lib/Linter.js +28 -8
- package/lib/utils/error-messages.js +39 -11
- package/lib/utils/properties-panel.js +193 -0
- package/package.json +10 -2
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.0
|
|
10
|
+
|
|
11
|
+
* `FEAT`: add properties panel entry ID to reports ([#7](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/7))
|
|
12
|
+
* `FEAT`: add #getErrors function that creates properties panel errors from reports ([#7](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/7))
|
|
13
|
+
* `FEAT`: adjust connectors error message ([#6](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/6))
|
|
14
|
+
|
|
15
|
+
### BREAKING CHANGES
|
|
16
|
+
|
|
17
|
+
* #lint is not static anymore, Linter must be instantiated
|
|
18
|
+
|
|
19
|
+
## 0.2.0
|
|
20
|
+
|
|
21
|
+
* `FEAT`: add templates rule ([#31](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/31))
|
|
22
|
+
|
|
23
|
+
## 0.1.1
|
|
24
|
+
|
|
25
|
+
* `FIX`: lint subscription only if start event child of sub process ([#34](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/34))
|
|
26
|
+
|
|
9
27
|
## 0.1.0
|
|
10
28
|
|
|
11
29
|
* `FEAT`: initial release bundling [bpmnlint](https://github.com/bpmn-io/bpmnlint) and [bpmnlint-plugin-camunda-compat](https://github.com/camunda/bpmnlint-plugin-camunda-compat/) and Camunda specific functionality ([#1](https://github.com/camunda/linting/pull/1))
|
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
|
-
*
|
|
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
|
|
24
|
+
const reports = await linter.lint(definitions);
|
|
20
25
|
|
|
21
26
|
// or passing XML
|
|
22
|
-
const reports = await
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
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.
|
|
3
|
+
"version": "0.3.0",
|
|
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.
|
|
23
|
+
"bpmnlint-plugin-camunda-compat": "^0.9.1",
|
|
24
24
|
"bpmnlint-utils": "^1.0.2"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
@@ -34,6 +34,14 @@
|
|
|
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
46
|
"lib"
|
|
39
47
|
],
|