bpmnlint-plugin-camunda-compat 2.21.1 → 2.22.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/index.js CHANGED
@@ -12,6 +12,7 @@ const camundaCloud10Rules = withConfig({
12
12
  'loop-characteristics': 'error',
13
13
  'message-reference': 'error',
14
14
  'no-candidate-users': 'error',
15
+ 'no-execution-listeners': 'error',
15
16
  'no-expression': 'error',
16
17
  'no-loop': 'error',
17
18
  'no-multiple-none-start-events': 'error',
@@ -75,8 +76,11 @@ const camundaCloud85Rules = withConfig({
75
76
  'wait-for-completion': 'error'
76
77
  }, { version: '8.5' });
77
78
 
78
- const camundaCloud86Rules = withConfig(
79
- omit(camundaCloud85Rules, 'inclusive-gateway'), { version: '8.6' });
79
+ const camundaCloud86Rules = withConfig({
80
+ ...omit(camundaCloud85Rules, [ 'inclusive-gateway', 'no-execution-listeners' ]),
81
+ 'duplicate-execution-listeners': 'error',
82
+ 'execution-listener': 'error'
83
+ }, { version: '8.6' });
80
84
 
81
85
  const camundaPlatform719Rules = withConfig({
82
86
  'history-time-to-live': 'info'
@@ -105,12 +109,14 @@ const rules = {
105
109
  'called-element': './rules/camunda-cloud/called-element',
106
110
  'collapsed-subprocess': './rules/camunda-cloud/collapsed-subprocess',
107
111
  'connector-properties': './rules/camunda-cloud/connector-properties',
112
+ 'duplicate-execution-listeners': './rules/camunda-cloud/duplicate-execution-listeners',
108
113
  'duplicate-task-headers': './rules/camunda-cloud/duplicate-task-headers',
109
114
  'error-reference': './rules/camunda-cloud/error-reference',
110
115
  'escalation-boundary-event-attached-to-ref': './rules/camunda-cloud/escalation-boundary-event-attached-to-ref',
111
116
  'escalation-reference': './rules/camunda-cloud/escalation-reference',
112
117
  'event-based-gateway-target': './rules/camunda-cloud/event-based-gateway-target',
113
118
  'executable-process': './rules/camunda-cloud/executable-process',
119
+ 'execution-listener': './rules/camunda-cloud/execution-listener',
114
120
  'feel': './rules/camunda-cloud/feel',
115
121
  'history-time-to-live': './rules/camunda-platform/history-time-to-live',
116
122
  'implementation': './rules/camunda-cloud/implementation',
@@ -119,6 +125,7 @@ const rules = {
119
125
  'loop-characteristics': './rules/camunda-cloud/loop-characteristics',
120
126
  'message-reference': './rules/camunda-cloud/message-reference',
121
127
  'no-candidate-users': './rules/camunda-cloud/no-candidate-users',
128
+ 'no-execution-listeners': './rules/camunda-cloud/no-execution-listeners',
122
129
  'no-expression': './rules/camunda-cloud/no-expression',
123
130
  'no-loop': './rules/camunda-cloud/no-loop',
124
131
  'no-multiple-none-start-events': './rules/camunda-cloud/no-multiple-none-start-events',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bpmnlint-plugin-camunda-compat",
3
- "version": "2.21.1",
3
+ "version": "2.22.0",
4
4
  "description": "A bpmnlint plug-in for Camunda compatibility",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -34,7 +34,7 @@
34
34
  "modeler-moddle": "^0.2.0",
35
35
  "sinon": "^17.0.1",
36
36
  "sinon-chai": "^3.7.0",
37
- "zeebe-bpmn-moddle": "^1.1.0"
37
+ "zeebe-bpmn-moddle": "^1.4.0"
38
38
  },
39
39
  "dependencies": {
40
40
  "@bpmn-io/feel-lint": "^1.2.0",
@@ -0,0 +1,33 @@
1
+ const {
2
+ findExtensionElement,
3
+ hasDuplicatedPropertiesValues
4
+ } = require('../utils/element');
5
+
6
+ const { reportErrors } = require('../utils/reporter');
7
+
8
+ const { skipInNonExecutableProcess } = require('../utils/rule');
9
+
10
+ module.exports = skipInNonExecutableProcess(function() {
11
+ function check(node, reporter) {
12
+ const executionListeners = findExtensionElement(node, 'zeebe:ExecutionListeners');
13
+
14
+ if (!executionListeners) {
15
+ return;
16
+ }
17
+
18
+ const errors = hasDuplicatedExecutionListeners(executionListeners, node);
19
+
20
+ if (errors && errors.length) {
21
+ reportErrors(node, reporter, errors);
22
+ }
23
+ }
24
+
25
+ return {
26
+ check
27
+ };
28
+ });
29
+
30
+ // helpers //////////
31
+ function hasDuplicatedExecutionListeners(executionListeners, parentNode = null) {
32
+ return hasDuplicatedPropertiesValues(executionListeners, 'listeners', [ 'eventType', 'type' ], parentNode);
33
+ }
@@ -0,0 +1,34 @@
1
+ const {
2
+ findExtensionElement,
3
+ hasProperties
4
+ } = require('../utils/element');
5
+
6
+ const { reportErrors } = require('../utils/reporter');
7
+
8
+ const { skipInNonExecutableProcess } = require('../utils/rule');
9
+
10
+
11
+ module.exports = skipInNonExecutableProcess(function() {
12
+ function check(node, reporter) {
13
+ const executionListeners = findExtensionElement(node, 'zeebe:ExecutionListeners');
14
+
15
+ if (!executionListeners) {
16
+ return;
17
+ }
18
+
19
+ const listeners = executionListeners.get('listeners');
20
+ const errors = listeners.flatMap(listener => hasProperties(listener, {
21
+ type: {
22
+ required: true
23
+ }
24
+ }, node));
25
+
26
+ if (errors.length) {
27
+ reportErrors(node, reporter, errors);
28
+ }
29
+ }
30
+
31
+ return {
32
+ check
33
+ };
34
+ });
@@ -0,0 +1,21 @@
1
+ const { reportErrors } = require('../utils/reporter');
2
+
3
+ const { skipInNonExecutableProcess } = require('../utils/rule');
4
+
5
+ const { hasNoExtensionElement } = require('../utils/element');
6
+
7
+ const ALLOWED_VERSION = '8.6';
8
+
9
+ module.exports = skipInNonExecutableProcess(function() {
10
+ function check(node, reporter) {
11
+ const errors = hasNoExtensionElement(node, 'zeebe:ExecutionListeners', node, ALLOWED_VERSION);
12
+
13
+ if (errors && errors.length) {
14
+ reportErrors(node, reporter, errors);
15
+ }
16
+ }
17
+
18
+ return {
19
+ check
20
+ };
21
+ });
@@ -93,7 +93,7 @@ function simplifyGraph(flowElements) {
93
93
  connectNodes(graph, node);
94
94
  }
95
95
 
96
- return [ ...outgoing.keys() ].map(key => graph.get(key));
96
+ return Array.from(outgoing.keys(), key => graph.get(key));
97
97
  });
98
98
 
99
99
  // Clean up all references to removed elements
@@ -1,4 +1,5 @@
1
1
  const {
2
+ filter,
2
3
  isArray,
3
4
  isDefined,
4
5
  isFunction,
@@ -6,6 +7,7 @@ const {
6
7
  isObject,
7
8
  isString,
8
9
  isUndefined,
10
+ matchPattern,
9
11
  some
10
12
  } = require('min-dash');
11
13
 
@@ -133,6 +135,53 @@ module.exports.hasDuplicatedPropertyValues = function(node, propertiesName, prop
133
135
  return [];
134
136
  };
135
137
 
138
+ // @TODO(@barmac): use tree algorithm to reduce complexity
139
+ module.exports.hasDuplicatedPropertiesValues = function(node, containerPropertyName, propertiesNames, parentNode = null) {
140
+ const properties = node.get(containerPropertyName);
141
+
142
+ // (1) find duplicates
143
+ const duplicates = properties.reduce((foundDuplicates, property, index) => {
144
+ const previous = properties.slice(0, index);
145
+ const isDuplicate = previous.find(p => propertiesNames.every(propertyName => p.get(propertyName) === property.get(propertyName)));
146
+
147
+ if (isDuplicate) {
148
+ return foundDuplicates.concat(property);
149
+ }
150
+
151
+ return foundDuplicates;
152
+ }, []);
153
+
154
+ // (2) report error for each duplicate
155
+ if (duplicates.length) {
156
+ return duplicates.map(duplicate => {
157
+ const propertiesMap = {};
158
+ for (const property of propertiesNames) {
159
+ propertiesMap[property] = duplicate.get(property);
160
+ }
161
+
162
+ // (3) find properties with duplicate
163
+ const duplicateProperties = filter(properties, matchPattern(propertiesMap));
164
+ const duplicatesSummary = propertiesNames.map(propertyName => `property <${ propertyName }> with duplicate value of <${ propertiesMap[propertyName] }>`).join(', ');
165
+
166
+ // (4) report error
167
+ return {
168
+ message: `Properties of type <${ duplicate.$type }> have properties with duplicate values (${ duplicatesSummary })`,
169
+ path: null,
170
+ data: {
171
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
172
+ node,
173
+ parentNode: parentNode == node ? null : parentNode,
174
+ duplicatedProperties: propertiesMap,
175
+ properties: duplicateProperties,
176
+ propertiesName: containerPropertyName
177
+ }
178
+ };
179
+ });
180
+ }
181
+
182
+ return [];
183
+ };
184
+
136
185
  module.exports.hasProperties = function(node, properties, parentNode = null) {
137
186
  return Object.entries(properties).reduce((results, property) => {
138
187
  const [ propertyName, propertyChecks ] = property;
@@ -19,6 +19,7 @@ module.exports.ERROR_TYPES = Object.freeze({
19
19
  PROPERTY_REQUIRED: 'camunda.propertyRequired',
20
20
  PROPERTY_TYPE_NOT_ALLOWED: 'camunda.propertyTypeNotAllowed',
21
21
  PROPERTY_VALUE_DUPLICATED: 'camunda.propertyValueDuplicated',
22
+ PROPERTY_VALUES_DUPLICATED: 'camunda.propertiesValuesDuplicated',
22
23
  PROPERTY_VALUE_NOT_ALLOWED: 'camunda.propertyValueNotAllowed',
23
24
  PROPERTY_VALUE_REQUIRED: 'camunda.propertyValueRequired',
24
25
  SECRET_EXPRESSION_FORMAT_DEPRECATED: 'camunda.secretExpressionFormatDeprecated'