bpmnlint-plugin-camunda-compat 0.9.2 → 0.10.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,11 @@ All notable changes to [bpmnlint-plugin-camunda-compat](https://github.com/camun
6
6
 
7
7
  ___Note:__ Yet to be released changes appear here._
8
8
 
9
+ ## 0.10.0
10
+
11
+ * `FEAT`: add duplicate task headers rule ([#41](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/41))
12
+
13
+
9
14
  ## 0.9.2
10
15
 
11
16
  * `FIX`: ignore null properties ([#39](https://github.com/camunda/bpmnlint-plugin-camunda-compat/pull/39))
package/index.js CHANGED
@@ -7,6 +7,7 @@ module.exports = {
7
7
  rules: {
8
8
  'called-decision-or-task-definition': [ 'error', calledDecisionOrTaskDefinitionConfig.camundaCloud10 ],
9
9
  'called-element': 'error',
10
+ 'duplicate-task-headers': 'error',
10
11
  'element-type': [ 'error', elementTypeConfig.camundaCloud10 ],
11
12
  'error-reference': 'error',
12
13
  'loop-characteristics': 'error',
@@ -20,6 +21,7 @@ module.exports = {
20
21
  rules: {
21
22
  'called-decision-or-task-definition': [ 'error', calledDecisionOrTaskDefinitionConfig.camundaCloud11 ],
22
23
  'called-element': 'error',
24
+ 'duplicate-task-headers': 'error',
23
25
  'element-type': [ 'error', elementTypeConfig.camundaCloud11 ],
24
26
  'error-reference': 'error',
25
27
  'loop-characteristics': 'error',
@@ -33,6 +35,7 @@ module.exports = {
33
35
  rules: {
34
36
  'called-decision-or-task-definition': [ 'error', calledDecisionOrTaskDefinitionConfig.camundaCloud12 ],
35
37
  'called-element': 'error',
38
+ 'duplicate-task-headers': 'error',
36
39
  'element-type': [ 'error', elementTypeConfig.camundaCloud12 ],
37
40
  'error-reference': 'error',
38
41
  'loop-characteristics': 'error',
@@ -46,6 +49,7 @@ module.exports = {
46
49
  rules: {
47
50
  'called-decision-or-task-definition': [ 'error', calledDecisionOrTaskDefinitionConfig.camundaCloud13 ],
48
51
  'called-element': 'error',
52
+ 'duplicate-task-headers': 'error',
49
53
  'element-type': [ 'error', elementTypeConfig.camundaCloud12 ],
50
54
  'error-reference': 'error',
51
55
  'loop-characteristics': 'error',
@@ -59,6 +63,7 @@ module.exports = {
59
63
  rules: {
60
64
  'called-decision-or-task-definition': [ 'error', calledDecisionOrTaskDefinitionConfig.camundaCloud13 ],
61
65
  'called-element': 'error',
66
+ 'duplicate-task-headers': 'error',
62
67
  'element-type': [ 'error', elementTypeConfig.camundaCloud12 ],
63
68
  'error-reference': 'error',
64
69
  'loop-characteristics': 'error',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bpmnlint-plugin-camunda-compat",
3
- "version": "0.9.2",
3
+ "version": "0.10.0",
4
4
  "description": "A bpmnlint plug-in for Camunda Platform compatibility",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,57 @@
1
+ const {
2
+ is,
3
+ isAny
4
+ } = require('bpmnlint-utils');
5
+
6
+ const {
7
+ findExtensionElement,
8
+ getMessageEventDefinition,
9
+ hasDuplicatedPropertyValues
10
+ } = require('./utils/element');
11
+
12
+ const { reportErrors } = require('./utils/reporter');
13
+
14
+ module.exports = function() {
15
+ function check(node, reporter) {
16
+ if (!is(node, 'bpmn:UserTask') && !isZeebeServiceTask(node)) {
17
+ return;
18
+ }
19
+
20
+ const taskHeaders = findExtensionElement(node, 'zeebe:TaskHeaders');
21
+
22
+ if (!taskHeaders) {
23
+ return;
24
+ }
25
+
26
+ const errors = hasDuplicatedPropertyValues(taskHeaders, 'values', 'key', node);
27
+
28
+ if (errors && errors.length) {
29
+ reportErrors(node, reporter, errors);
30
+ }
31
+ }
32
+
33
+ return {
34
+ check
35
+ };
36
+ };
37
+
38
+ // helpers //////////
39
+
40
+ function isZeebeServiceTask(element) {
41
+ if (is(element, 'zeebe:ZeebeServiceTask')) {
42
+ return true;
43
+ }
44
+
45
+ if (isAny(element, [
46
+ 'bpmn:EndEvent',
47
+ 'bpmn:IntermediateThrowEvent'
48
+ ])) {
49
+ return getMessageEventDefinition(element);
50
+ }
51
+
52
+ if (is(element, 'bpmn:BusinessRuleTask')) {
53
+ return findExtensionElement(element, 'zeebe:TaskDefinition');
54
+ }
55
+
56
+ return false;
57
+ }
@@ -56,6 +56,8 @@ module.exports = function() {
56
56
  };
57
57
  };
58
58
 
59
+ // helpers //////////
60
+
59
61
  function findUserTaskForm(node, formKey) {
60
62
  const process = findParent(node, 'bpmn:Process');
61
63
 
@@ -5,7 +5,10 @@ const {
5
5
  some
6
6
  } = require('min-dash');
7
7
 
8
- const { isAny } = require('bpmnlint-utils');
8
+ const {
9
+ is,
10
+ isAny
11
+ } = require('bpmnlint-utils');
9
12
 
10
13
  const { getPath } = require('@bpmn-io/moddle-utils');
11
14
 
@@ -13,12 +16,22 @@ const { ERROR_TYPES } = require('./error-types');
13
16
 
14
17
  module.exports.ERROR_TYPES = ERROR_TYPES;
15
18
 
16
- module.exports.getEventDefinition = function(node) {
19
+ function getEventDefinition(node) {
17
20
  const eventDefinitions = node.get('eventDefinitions');
18
21
 
19
22
  if (eventDefinitions) {
20
23
  return eventDefinitions[ 0 ];
21
24
  }
25
+ }
26
+
27
+ module.exports.getEventDefinition = getEventDefinition;
28
+
29
+ module.exports.getMessageEventDefinition = function(node) {
30
+ if (is(node, 'bpmn:ReceiveTask')) {
31
+ return node;
32
+ }
33
+
34
+ return getEventDefinition(node);
22
35
  };
23
36
 
24
37
  function findExtensionElements(node, types) {
@@ -72,6 +85,50 @@ function formatTypes(types, exclusive = false) {
72
85
 
73
86
  module.exports.formatTypes = formatTypes;
74
87
 
88
+ module.exports.hasDuplicatedPropertyValues = function(node, propertiesName, propertyName, parentNode = null) {
89
+ const properties = node.get(propertiesName);
90
+
91
+ const propertyValues = properties.map(property => property.get(propertyName));
92
+
93
+ // (1) find duplicates
94
+ const duplicates = propertyValues.reduce((duplicates, propertyValue, index) => {
95
+ if (propertyValues.indexOf(propertyValue) !== index && !duplicates.includes(propertyValue)) {
96
+ return [
97
+ ...duplicates,
98
+ propertyValue
99
+ ];
100
+ }
101
+
102
+ return duplicates;
103
+ }, []);
104
+
105
+ // (2) report error for each duplicate
106
+ if (duplicates.length) {
107
+ return duplicates.map(duplicate => {
108
+
109
+ // (3) find properties with duplicate
110
+ const duplicateProperties = properties.filter(property => property.get(propertyName) === duplicate);
111
+
112
+ // (4) report error
113
+ return {
114
+ message: `Properties of type <${ duplicateProperties[ 0 ].$type }> have property <${ propertyName }> with duplicate value of <${ duplicate }>`,
115
+ path: null,
116
+ error: {
117
+ type: ERROR_TYPES.PROPERTY_VALUE_DUPLICATED,
118
+ node,
119
+ parentNode: parentNode == node ? null : parentNode,
120
+ duplicatedProperty: propertyName,
121
+ duplicatedPropertyValue: duplicate,
122
+ properties: duplicateProperties,
123
+ propertiesName
124
+ }
125
+ };
126
+ });
127
+ }
128
+
129
+ return [];
130
+ };
131
+
75
132
  module.exports.hasProperties = function(node, properties, parentNode = null) {
76
133
  return Object.entries(properties).reduce((results, property) => {
77
134
  const [ propertyName, propertyChecks ] = property;
@@ -5,5 +5,6 @@ module.exports.ERROR_TYPES = Object.freeze({
5
5
  PROPERTY_DEPENDEND_REQUIRED: 'propertyDependendRequired',
6
6
  PROPERTY_NOT_ALLOWED: 'propertyNotAllowed',
7
7
  PROPERTY_REQUIRED: 'propertyRequired',
8
- PROPERTY_TYPE_NOT_ALLOWED: 'propertyTypeNotAllowed'
8
+ PROPERTY_TYPE_NOT_ALLOWED: 'propertyTypeNotAllowed',
9
+ PROPERTY_VALUE_DUPLICATED: 'propertyValueDuplicated'
9
10
  });