diff --git a/index.js b/index.js
index 611b381..5bdb836 100644
--- a/index.js
+++ b/index.js
@@ -12,6 +12,7 @@ const camundaCloud10Rules = withConfig({
'loop-characteristics': 'error',
'message-reference': 'error',
'no-candidate-users': 'error',
+ 'no-execution-listeners': 'error',
'no-expression': 'error',
'no-loop': 'error',
'no-multiple-none-start-events': 'error',
@@ -75,8 +76,11 @@ const camundaCloud85Rules = withConfig({
'wait-for-completion': 'error'
}, { version: '8.5' });
-const camundaCloud86Rules = withConfig(
- omit(camundaCloud85Rules, 'inclusive-gateway'), { version: '8.6' });
+const camundaCloud86Rules = withConfig({
+ ...omit(camundaCloud85Rules, [ 'inclusive-gateway', 'no-execution-listeners' ]),
+ 'duplicate-execution-listeners': 'error',
+ 'execution-listener': 'error'
+}, { version: '8.6' });
const camundaPlatform719Rules = withConfig({
'history-time-to-live': 'info'
@@ -105,12 +109,14 @@ const rules = {
'called-element': './rules/camunda-cloud/called-element',
'collapsed-subprocess': './rules/camunda-cloud/collapsed-subprocess',
'connector-properties': './rules/camunda-cloud/connector-properties',
+ 'duplicate-execution-listeners': './rules/camunda-cloud/duplicate-execution-listeners',
'duplicate-task-headers': './rules/camunda-cloud/duplicate-task-headers',
'error-reference': './rules/camunda-cloud/error-reference',
'escalation-boundary-event-attached-to-ref': './rules/camunda-cloud/escalation-boundary-event-attached-to-ref',
'escalation-reference': './rules/camunda-cloud/escalation-reference',
'event-based-gateway-target': './rules/camunda-cloud/event-based-gateway-target',
'executable-process': './rules/camunda-cloud/executable-process',
+ 'execution-listener': './rules/camunda-cloud/execution-listener',
'feel': './rules/camunda-cloud/feel',
'history-time-to-live': './rules/camunda-platform/history-time-to-live',
'implementation': './rules/camunda-cloud/implementation',
@@ -119,6 +125,7 @@ const rules = {
'loop-characteristics': './rules/camunda-cloud/loop-characteristics',
'message-reference': './rules/camunda-cloud/message-reference',
'no-candidate-users': './rules/camunda-cloud/no-candidate-users',
+ 'no-execution-listeners': './rules/camunda-cloud/no-execution-listeners',
'no-expression': './rules/camunda-cloud/no-expression',
'no-loop': './rules/camunda-cloud/no-loop',
'no-multiple-none-start-events': './rules/camunda-cloud/no-multiple-none-start-events',
diff --git a/package-lock.json b/package-lock.json
index ee54dac..d275ee0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,7 +26,7 @@
"modeler-moddle": "^0.2.0",
"sinon": "^17.0.1",
"sinon-chai": "^3.7.0",
- "zeebe-bpmn-moddle": "^1.1.0"
+ "zeebe-bpmn-moddle": "^1.4.0"
},
"engines": {
"node": "*"
@@ -3118,10 +3118,11 @@
}
},
"node_modules/zeebe-bpmn-moddle": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/zeebe-bpmn-moddle/-/zeebe-bpmn-moddle-1.1.0.tgz",
- "integrity": "sha512-ES/UZFO0VmKvAzL4+cD3VcQpKvlmgLtnFKTyiv0DdDcxNrdQg1rI0OmUdrKMiybAbtAgPDkVXZCusE3kkXwEyQ==",
- "dev": true
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/zeebe-bpmn-moddle/-/zeebe-bpmn-moddle-1.4.0.tgz",
+ "integrity": "sha512-XSm0fMHPjQ5cmEGxga02du9arxb5NKH5ve7VQ0LFSes4wGcrZ/oJjaR3NlBqH6xTTD3g+Mcbh45+yesydPUQPg==",
+ "dev": true,
+ "license": "MIT"
}
},
"dependencies": {
@@ -5405,9 +5406,9 @@
"dev": true
},
"zeebe-bpmn-moddle": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/zeebe-bpmn-moddle/-/zeebe-bpmn-moddle-1.1.0.tgz",
- "integrity": "sha512-ES/UZFO0VmKvAzL4+cD3VcQpKvlmgLtnFKTyiv0DdDcxNrdQg1rI0OmUdrKMiybAbtAgPDkVXZCusE3kkXwEyQ==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/zeebe-bpmn-moddle/-/zeebe-bpmn-moddle-1.4.0.tgz",
+ "integrity": "sha512-XSm0fMHPjQ5cmEGxga02du9arxb5NKH5ve7VQ0LFSes4wGcrZ/oJjaR3NlBqH6xTTD3g+Mcbh45+yesydPUQPg==",
"dev": true
}
}
diff --git a/package.json b/package.json
index f16d0ea..6bc90f3 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
"modeler-moddle": "^0.2.0",
"sinon": "^17.0.1",
"sinon-chai": "^3.7.0",
- "zeebe-bpmn-moddle": "^1.1.0"
+ "zeebe-bpmn-moddle": "^1.4.0"
},
"dependencies": {
"@bpmn-io/feel-lint": "^1.2.0",
diff --git a/rules/camunda-cloud/duplicate-execution-listeners.js b/rules/camunda-cloud/duplicate-execution-listeners.js
new file mode 100644
index 0000000..278e1f5
--- /dev/null
+++ b/rules/camunda-cloud/duplicate-execution-listeners.js
@@ -0,0 +1,33 @@
+const {
+ findExtensionElement,
+ hasDuplicatedPropertiesValues
+} = require('../utils/element');
+
+const { reportErrors } = require('../utils/reporter');
+
+const { skipInNonExecutableProcess } = require('../utils/rule');
+
+module.exports = skipInNonExecutableProcess(function() {
+ function check(node, reporter) {
+ const executionListeners = findExtensionElement(node, 'zeebe:ExecutionListeners');
+
+ if (!executionListeners) {
+ return;
+ }
+
+ const errors = hasDuplicatedExecutionListeners(executionListeners, node);
+
+ if (errors && errors.length) {
+ reportErrors(node, reporter, errors);
+ }
+ }
+
+ return {
+ check
+ };
+});
+
+// helpers //////////
+function hasDuplicatedExecutionListeners(executionListeners, parentNode = null) {
+ return hasDuplicatedPropertiesValues(executionListeners, 'listeners', [ 'eventType', 'type' ], parentNode);
+}
diff --git a/rules/camunda-cloud/execution-listener.js b/rules/camunda-cloud/execution-listener.js
new file mode 100644
index 0000000..ac8bdc6
--- /dev/null
+++ b/rules/camunda-cloud/execution-listener.js
@@ -0,0 +1,34 @@
+const {
+ findExtensionElement,
+ hasProperties
+} = require('../utils/element');
+
+const { reportErrors } = require('../utils/reporter');
+
+const { skipInNonExecutableProcess } = require('../utils/rule');
+
+
+module.exports = skipInNonExecutableProcess(function() {
+ function check(node, reporter) {
+ const executionListeners = findExtensionElement(node, 'zeebe:ExecutionListeners');
+
+ if (!executionListeners) {
+ return;
+ }
+
+ const listeners = executionListeners.get('listeners');
+ const errors = listeners.flatMap(listener => hasProperties(listener, {
+ type: {
+ required: true
+ }
+ }, node));
+
+ if (errors.length) {
+ reportErrors(node, reporter, errors);
+ }
+ }
+
+ return {
+ check
+ };
+});
diff --git a/rules/camunda-cloud/no-execution-listeners.js b/rules/camunda-cloud/no-execution-listeners.js
new file mode 100644
index 0000000..1684023
--- /dev/null
+++ b/rules/camunda-cloud/no-execution-listeners.js
@@ -0,0 +1,21 @@
+const { reportErrors } = require('../utils/reporter');
+
+const { skipInNonExecutableProcess } = require('../utils/rule');
+
+const { hasNoExtensionElement } = require('../utils/element');
+
+const ALLOWED_VERSION = '8.6';
+
+module.exports = skipInNonExecutableProcess(function() {
+ function check(node, reporter) {
+ const errors = hasNoExtensionElement(node, 'zeebe:ExecutionListeners', node, ALLOWED_VERSION);
+
+ if (errors && errors.length) {
+ reportErrors(node, reporter, errors);
+ }
+ }
+
+ return {
+ check
+ };
+});
diff --git a/rules/utils/element.js b/rules/utils/element.js
index ea25e0e..9e5ede6 100644
--- a/rules/utils/element.js
+++ b/rules/utils/element.js
@@ -1,4 +1,5 @@
const {
+ filter,
isArray,
isDefined,
isFunction,
@@ -6,6 +7,7 @@ const {
isObject,
isString,
isUndefined,
+ matchPattern,
some
} = require('min-dash');
@@ -133,6 +135,53 @@ module.exports.hasDuplicatedPropertyValues = function(node, propertiesName, prop
return [];
};
+// @TODO(@barmac): use tree algorithm to reduce complexity
+module.exports.hasDuplicatedPropertiesValues = function(node, containerPropertyName, propertiesNames, parentNode = null) {
+ const properties = node.get(containerPropertyName);
+
+ // (1) find duplicates
+ const duplicates = properties.reduce((foundDuplicates, property, index) => {
+ const previous = properties.slice(0, index);
+ const isDuplicate = previous.find(p => propertiesNames.every(propertyName => p.get(propertyName) === property.get(propertyName)));
+
+ if (isDuplicate) {
+ return foundDuplicates.concat(property);
+ }
+
+ return foundDuplicates;
+ }, []);
+
+ // (2) report error for each duplicate
+ if (duplicates.length) {
+ return duplicates.map(duplicate => {
+ const propertiesMap = {};
+ for (const property of propertiesNames) {
+ propertiesMap[property] = duplicate.get(property);
+ }
+
+ // (3) find properties with duplicate
+ const duplicateProperties = filter(properties, matchPattern(propertiesMap));
+ const duplicatesSummary = propertiesNames.map(propertyName => `property <${ propertyName }> with duplicate value of <${ propertiesMap[propertyName] }>`).join(', ');
+
+ // (4) report error
+ return {
+ message: `Properties of type <${ duplicate.$type }> have properties with duplicate values (${ duplicatesSummary })`,
+ path: null,
+ data: {
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
+ node,
+ parentNode: parentNode == node ? null : parentNode,
+ duplicatedProperties: propertiesMap,
+ properties: duplicateProperties,
+ propertiesName: containerPropertyName
+ }
+ };
+ });
+ }
+
+ return [];
+};
+
module.exports.hasProperties = function(node, properties, parentNode = null) {
return Object.entries(properties).reduce((results, property) => {
const [ propertyName, propertyChecks ] = property;
diff --git a/rules/utils/error-types.js b/rules/utils/error-types.js
index 66b9987..185ba61 100644
--- a/rules/utils/error-types.js
+++ b/rules/utils/error-types.js
@@ -19,6 +19,7 @@ module.exports.ERROR_TYPES = Object.freeze({
PROPERTY_REQUIRED: 'camunda.propertyRequired',
PROPERTY_TYPE_NOT_ALLOWED: 'camunda.propertyTypeNotAllowed',
PROPERTY_VALUE_DUPLICATED: 'camunda.propertyValueDuplicated',
+ PROPERTY_VALUES_DUPLICATED: 'camunda.propertiesValuesDuplicated',
PROPERTY_VALUE_NOT_ALLOWED: 'camunda.propertyValueNotAllowed',
PROPERTY_VALUE_REQUIRED: 'camunda.propertyValueRequired',
SECRET_EXPRESSION_FORMAT_DEPRECATED: 'camunda.secretExpressionFormatDeprecated'
diff --git a/test/camunda-cloud/duplicate-execution-listeners.spec.js b/test/camunda-cloud/duplicate-execution-listeners.spec.js
new file mode 100644
index 0000000..85c520c
--- /dev/null
+++ b/test/camunda-cloud/duplicate-execution-listeners.spec.js
@@ -0,0 +1,295 @@
+const RuleTester = require('bpmnlint/lib/testers/rule-tester');
+
+const rule = require('../../rules/camunda-cloud/duplicate-execution-listeners');
+
+const {
+ createDefinitions,
+ createModdle,
+ createProcess
+} = require('../helper');
+
+const { ERROR_TYPES } = require('../../rules/utils/element');
+
+const valid = [
+ {
+ name: 'service task',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+ `))
+ },
+ {
+ name: 'send task',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+ `))
+ },
+ {
+ name: 'user task',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+ `))
+ },
+ {
+ name: 'business rule task',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+
+ `))
+ },
+ {
+ name: 'script task',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+
+
+ `))
+ },
+ {
+ name: 'message throw event',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+
+ `))
+ },
+ {
+ name: 'service task (non-executable process)',
+ config: { version: '8.6' },
+ moddleElement: createModdle(createDefinitions(`
+
+
+
+
+
+
+
+
+
+
+ `))
+ }
+];
+
+const invalid = [
+ {
+ name: 'service task',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+ `)),
+ report: {
+ id: 'ServiceTask_1',
+ message: 'Properties of type have properties with duplicate values (property with duplicate value of , property with duplicate value of )',
+ path: null,
+ data: {
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
+ node: 'zeebe:ExecutionListeners',
+ parentNode: 'ServiceTask_1',
+ 'duplicatedProperties': {
+ 'eventType': 'start',
+ 'type': 'duplicate'
+ },
+ properties: [
+ 'zeebe:ExecutionListener',
+ 'zeebe:ExecutionListener'
+ ],
+ propertiesName: 'listeners'
+ }
+ }
+ },
+ {
+ name: 'service task (multiple duplicates)',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+
+ `)),
+ report: [
+ {
+ id: 'ServiceTask_1',
+ message: 'Properties of type have properties with duplicate values (property with duplicate value of , property with duplicate value of )',
+ path: null,
+ data: {
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
+ node: 'zeebe:ExecutionListeners',
+ parentNode: 'ServiceTask_1',
+ 'duplicatedProperties': {
+ 'eventType': 'start',
+ 'type': 'duplicate'
+ },
+ properties: [
+ 'zeebe:ExecutionListener',
+ 'zeebe:ExecutionListener',
+ 'zeebe:ExecutionListener'
+ ],
+ propertiesName: 'listeners'
+ }
+ },
+ {
+ id: 'ServiceTask_1',
+ message: 'Properties of type have properties with duplicate values (property with duplicate value of , property with duplicate value of )',
+ path: null,
+ data: {
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
+ node: 'zeebe:ExecutionListeners',
+ parentNode: 'ServiceTask_1',
+ 'duplicatedProperties': {
+ 'eventType': 'start',
+ 'type': 'duplicate'
+ },
+ properties: [
+ 'zeebe:ExecutionListener',
+ 'zeebe:ExecutionListener',
+ 'zeebe:ExecutionListener'
+ ],
+ propertiesName: 'listeners'
+ }
+ }
+ ]
+ },
+ {
+ name: 'service task (multiple duplicates)',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+
+
+ `)),
+ report: [
+ {
+ id: 'ServiceTask_1',
+ message: 'Properties of type have properties with duplicate values (property with duplicate value of , property with duplicate value of )',
+ path: null,
+ data: {
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
+ node: 'zeebe:ExecutionListeners',
+ parentNode: 'ServiceTask_1',
+ 'duplicatedProperties': {
+ 'eventType': 'start',
+ 'type': 'duplicate'
+ },
+ properties: [
+ 'zeebe:ExecutionListener',
+ 'zeebe:ExecutionListener'
+ ],
+ propertiesName: 'listeners'
+ }
+ },
+ {
+ id: 'ServiceTask_1',
+ message: 'Properties of type have properties with duplicate values (property with duplicate value of , property with duplicate value of )',
+ path: null,
+ data: {
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
+ node: 'zeebe:ExecutionListeners',
+ parentNode: 'ServiceTask_1',
+ 'duplicatedProperties': {
+ 'eventType': 'start',
+ 'type': 'duplicate_2'
+ },
+ properties: [
+ 'zeebe:ExecutionListener',
+ 'zeebe:ExecutionListener'
+ ],
+ propertiesName: 'listeners'
+ }
+ }
+ ]
+ },
+ {
+ name: 'service task (no type)',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+ `)),
+ report: {
+ id: 'ServiceTask_1',
+ message: 'Properties of type have properties with duplicate values (property with duplicate value of , property with duplicate value of <>)',
+ path: null,
+ data: {
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
+ node: 'zeebe:ExecutionListeners',
+ parentNode: 'ServiceTask_1',
+ 'duplicatedProperties': {
+ 'eventType': 'start',
+ 'type': ''
+ },
+ properties: [
+ 'zeebe:ExecutionListener',
+ 'zeebe:ExecutionListener'
+ ],
+ propertiesName: 'listeners'
+ }
+ }
+ }
+];
+
+RuleTester.verify('duplicate-execution-listeners', rule, {
+ valid,
+ invalid
+});
\ No newline at end of file
diff --git a/test/camunda-cloud/execution-listener.spec.js b/test/camunda-cloud/execution-listener.spec.js
new file mode 100644
index 0000000..0030a5f
--- /dev/null
+++ b/test/camunda-cloud/execution-listener.spec.js
@@ -0,0 +1,186 @@
+const RuleTester = require('bpmnlint/lib/testers/rule-tester');
+
+const rule = require('../../rules/camunda-cloud/execution-listener');
+
+const {
+ createModdle,
+ createProcess
+} = require('../helper');
+
+const { ERROR_TYPES } = require('../../rules/utils/element');
+
+const valid = [
+ {
+ name: 'execution listener with type',
+ config: { version: '8.6' },
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+ `))
+ }
+];
+
+const invalid = [
+ {
+ name: 'execution listener with empty type',
+ config: { version: '8.6' },
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+ `)),
+ report: {
+ id: 'UserTask_1',
+ message: 'Element of type must have property ',
+ path: [
+ 'extensionElements',
+ 'values',
+ 0,
+ 'listeners',
+ 0,
+ 'type'
+ ],
+ data: {
+ type: ERROR_TYPES.PROPERTY_REQUIRED,
+ node: 'zeebe:ExecutionListener',
+ parentNode: 'UserTask_1',
+ requiredProperty: 'type'
+ }
+ }
+ },
+ {
+ name: 'execution listener with no type',
+ config: { version: '8.6' },
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+ `)),
+ report: {
+ id: 'UserTask_1',
+ message: 'Element of type must have property ',
+ path: [
+ 'extensionElements',
+ 'values',
+ 0,
+ 'listeners',
+ 0,
+ 'type'
+ ],
+ data: {
+ type: ERROR_TYPES.PROPERTY_REQUIRED,
+ node: 'zeebe:ExecutionListener',
+ parentNode: 'UserTask_1',
+ requiredProperty: 'type'
+ }
+ }
+ },
+ {
+ name: 'multiple execution listeners with no type',
+ config: { version: '8.6' },
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+
+
+
+ `)),
+ report: [
+ {
+ id: 'UserTask_1',
+ message: 'Element of type must have property ',
+ path: [
+ 'extensionElements',
+ 'values',
+ 0,
+ 'listeners',
+ 0,
+ 'type'
+ ],
+ data: {
+ type: ERROR_TYPES.PROPERTY_REQUIRED,
+ node: 'zeebe:ExecutionListener',
+ parentNode: 'UserTask_1',
+ requiredProperty: 'type'
+ }
+ },
+ {
+ id: 'UserTask_1',
+ message: 'Element of type must have property ',
+ path: [
+ 'extensionElements',
+ 'values',
+ 0,
+ 'listeners',
+ 1,
+ 'type'
+ ],
+ data: {
+ type: ERROR_TYPES.PROPERTY_REQUIRED,
+ node: 'zeebe:ExecutionListener',
+ parentNode: 'UserTask_1',
+ requiredProperty: 'type'
+ }
+ },
+ {
+ id: 'UserTask_1',
+ message: 'Element of type must have property ',
+ path: [
+ 'extensionElements',
+ 'values',
+ 0,
+ 'listeners',
+ 2,
+ 'type'
+ ],
+ data: {
+ type: ERROR_TYPES.PROPERTY_REQUIRED,
+ node: 'zeebe:ExecutionListener',
+ parentNode: 'UserTask_1',
+ requiredProperty: 'type'
+ }
+ },
+ {
+ id: 'UserTask_1',
+ message: 'Element of type must have property ',
+ path: [
+ 'extensionElements',
+ 'values',
+ 0,
+ 'listeners',
+ 3,
+ 'type'
+ ],
+ data: {
+ type: ERROR_TYPES.PROPERTY_REQUIRED,
+ node: 'zeebe:ExecutionListener',
+ parentNode: 'UserTask_1',
+ requiredProperty: 'type'
+ }
+ }
+ ]
+ }
+];
+
+RuleTester.verify('execution-listener', rule, {
+ valid,
+ invalid
+});
\ No newline at end of file
diff --git a/test/camunda-cloud/no-execution-listeners.spec.js b/test/camunda-cloud/no-execution-listeners.spec.js
new file mode 100644
index 0000000..aa51c77
--- /dev/null
+++ b/test/camunda-cloud/no-execution-listeners.spec.js
@@ -0,0 +1,57 @@
+const RuleTester = require('bpmnlint/lib/testers/rule-tester');
+
+const rule = require('../../rules/camunda-cloud/no-execution-listeners');
+
+const {
+ createModdle,
+ createProcess
+} = require('../helper');
+
+const { ERROR_TYPES } = require('../../rules/utils/element');
+
+const valid = [
+ {
+ name: 'user task without execution listeners',
+ config: { version: '8.5' },
+ moddleElement: createModdle(createProcess(`
+
+ `))
+ }
+];
+
+const invalid = [
+ {
+ name: 'no execution listeners (Camunda 8.5)',
+ config: { version: '8.5' },
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+
+
+ `)),
+ report: {
+ id: 'UserTask_1',
+ message: 'Extension element of type only allowed by Camunda 8.6',
+ path: [
+ 'extensionElements',
+ 'values',
+ 0
+ ],
+ data: {
+ type: ERROR_TYPES.EXTENSION_ELEMENT_NOT_ALLOWED,
+ node: 'UserTask_1',
+ parentNode: null,
+ extensionElement: 'zeebe:ExecutionListeners',
+ allowedVersion: '8.6'
+ }
+ }
+ }
+];
+
+RuleTester.verify('no-execution-listeners', rule, {
+ valid,
+ invalid
+});
\ No newline at end of file
diff --git a/test/config/configs.spec.js b/test/config/configs.spec.js
index db129f9..8c701b9 100644
--- a/test/config/configs.spec.js
+++ b/test/config/configs.spec.js
@@ -17,6 +17,7 @@ describe('configs', function() {
'loop-characteristics': [ 'error', { version: '1.0' } ],
'message-reference': [ 'error', { version: '1.0' } ],
'no-candidate-users': [ 'error', { version: '1.0' } ],
+ 'no-execution-listeners': [ 'error', { version: '1.0' } ],
'no-expression': [ 'error', { version: '1.0' } ],
'no-loop': [ 'error', { version: '1.0' } ],
'no-multiple-none-start-events' : [ 'error', { version: '1.0' } ],
@@ -47,6 +48,7 @@ describe('configs', function() {
'loop-characteristics': [ 'error', { version: '1.1' } ],
'message-reference': [ 'error', { version: '1.1' } ],
'no-candidate-users': [ 'error', { version: '1.1' } ],
+ 'no-execution-listeners': [ 'error', { version: '1.1' } ],
'no-expression': [ 'error', { version: '1.1' } ],
'no-loop': [ 'error', { version: '1.1' } ],
'no-multiple-none-start-events' : [ 'error', { version: '1.1' } ],
@@ -77,6 +79,7 @@ describe('configs', function() {
'loop-characteristics': [ 'error', { version: '1.2' } ],
'message-reference': [ 'error', { version: '1.2' } ],
'no-candidate-users': [ 'error', { version: '1.2' } ],
+ 'no-execution-listeners': [ 'error', { version: '1.2' } ],
'no-expression': [ 'error', { version: '1.2' } ],
'no-loop': [ 'error', { version: '1.2' } ],
'no-multiple-none-start-events' : [ 'error', { version: '1.2' } ],
@@ -107,6 +110,7 @@ describe('configs', function() {
'loop-characteristics': [ 'error', { version: '1.3' } ],
'message-reference': [ 'error', { version: '1.3' } ],
'no-candidate-users': [ 'error', { version: '1.3' } ],
+ 'no-execution-listeners': [ 'error', { version: '1.3' } ],
'no-expression': [ 'error', { version: '1.3' } ],
'no-loop': [ 'error', { version: '1.3' } ],
'no-multiple-none-start-events' : [ 'error', { version: '1.3' } ],
@@ -138,6 +142,7 @@ describe('configs', function() {
'loop-characteristics': [ 'error', { version: '8.0' } ],
'message-reference': [ 'error', { version: '8.0' } ],
'no-candidate-users': [ 'error', { version: '8.0' } ],
+ 'no-execution-listeners': [ 'error', { version: '8.0' } ],
'no-expression': [ 'error', { version: '8.0' } ],
'no-loop': [ 'error', { version: '8.0' } ],
'no-multiple-none-start-events' : [ 'error', { version: '8.0' } ],
@@ -169,6 +174,7 @@ describe('configs', function() {
'loop-characteristics': [ 'error', { version: '8.1' } ],
'message-reference': [ 'error', { version: '8.1' } ],
'no-candidate-users': [ 'error', { version: '8.1' } ],
+ 'no-execution-listeners': [ 'error', { version: '8.1' } ],
'no-expression': [ 'error', { version: '8.1' } ],
'no-loop': [ 'error', { version: '8.1' } ],
'no-multiple-none-start-events' : [ 'error', { version: '8.1' } ],
@@ -201,6 +207,7 @@ describe('configs', function() {
'link-event': [ 'error', { version: '8.2' } ],
'loop-characteristics': [ 'error', { version: '8.2' } ],
'message-reference': [ 'error', { version: '8.2' } ],
+ 'no-execution-listeners': [ 'error', { version: '8.2' } ],
'no-expression': [ 'error', { version: '8.2' } ],
'no-loop': [ 'error', { version: '8.2' } ],
'no-multiple-none-start-events' : [ 'error', { version: '8.2' } ],
@@ -233,6 +240,7 @@ describe('configs', function() {
'link-event': [ 'error', { version: '8.3' } ],
'loop-characteristics': [ 'error', { version: '8.3' } ],
'message-reference': [ 'error', { version: '8.3' } ],
+ 'no-execution-listeners': [ 'error', { version: '8.3' } ],
'no-expression': [ 'error', { version: '8.3' } ],
'no-loop': [ 'error', { version: '8.3' } ],
'no-multiple-none-start-events' : [ 'error', { version: '8.3' } ],
@@ -265,6 +273,7 @@ describe('configs', function() {
'link-event': [ 'error', { version: '8.4' } ],
'loop-characteristics': [ 'error', { version: '8.4' } ],
'message-reference': [ 'error', { version: '8.4' } ],
+ 'no-execution-listeners': [ 'error', { version: '8.4' } ],
'no-expression': [ 'error', { version: '8.4' } ],
'no-loop': [ 'error', { version: '8.4' } ],
'no-multiple-none-start-events' : [ 'error', { version: '8.4' } ],
@@ -297,6 +306,7 @@ describe('configs', function() {
'link-event': [ 'error', { version: '8.5' } ],
'loop-characteristics': [ 'error', { version: '8.5' } ],
'message-reference': [ 'error', { version: '8.5' } ],
+ 'no-execution-listeners': [ 'error', { version: '8.5' } ],
'no-expression': [ 'error', { version: '8.5' } ],
'no-loop': [ 'error', { version: '8.5' } ],
'no-multiple-none-start-events' : [ 'error', { version: '8.5' } ],
@@ -316,6 +326,7 @@ describe('configs', function() {
it('camunda-cloud-8-6', expectRules(configs, 'camunda-cloud-8-6', {
'called-element': [ 'error', { version: '8.6' } ],
'connector-properties': [ 'warn', { version: '8.6' } ],
+ 'duplicate-execution-listeners': [ 'error', { version: '8.6' } ],
'duplicate-task-headers': [ 'error', { version: '8.6' } ],
'element-type': [ 'error', { version: '8.6' } ],
'error-reference': [ 'error', { version: '8.6' } ],
@@ -323,6 +334,7 @@ describe('configs', function() {
'escalation-reference': [ 'error', { version: '8.6' } ],
'event-based-gateway-target': [ 'error', { version: '8.6' } ],
'executable-process': [ 'error', { version: '8.6' } ],
+ 'execution-listener': [ 'error', { version: '8.6' } ],
'feel': [ 'error', { version: '8.6' } ],
'implementation': [ 'error', { version: '8.6' } ],
'link-event': [ 'error', { version: '8.6' } ],
@@ -368,12 +380,14 @@ describe('configs', function() {
'called-element': 'error',
'collapsed-subprocess': 'error',
'connector-properties': 'warn',
+ 'duplicate-execution-listeners': 'error',
'duplicate-task-headers': 'error',
'error-reference': 'error',
'escalation-boundary-event-attached-to-ref': 'error',
'escalation-reference': 'error',
'event-based-gateway-target': 'error',
'executable-process': 'error',
+ 'execution-listener': 'error',
'feel': 'error',
'history-time-to-live': 'info',
'implementation': 'error',
@@ -382,6 +396,7 @@ describe('configs', function() {
'loop-characteristics': 'error',
'message-reference': 'error',
'no-candidate-users': 'error',
+ 'no-execution-listeners': 'error',
'no-expression': 'error',
'no-loop': 'error',
'no-multiple-none-start-events': 'error',
diff --git a/test/utils/element.spec.js b/test/utils/element.spec.js
index 16bd82a..bde6cad 100644
--- a/test/utils/element.spec.js
+++ b/test/utils/element.spec.js
@@ -4,6 +4,7 @@ const {
ERROR_TYPES,
formatNames,
hasDuplicatedPropertyValues,
+ hasDuplicatedPropertiesValues,
hasExpression,
hasExtensionElement,
hasNoExtensionElement,
@@ -450,6 +451,156 @@ describe('utils/element', function() {
});
+ describe('#hasDuplicatedPropertiesValues', function() {
+
+ it('should not return errors', function() {
+
+ // given
+ const executionListeners = createElement('zeebe:ExecutionListeners', {
+ listeners: [
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'foo'
+ }),
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'bar'
+ }),
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'baz'
+ })
+ ]
+ });
+
+ // when
+ const errors = hasDuplicatedPropertiesValues(executionListeners, 'listeners', [ 'eventType', 'type' ]);
+
+ // then
+ expect(errors).to.be.empty;
+ });
+
+
+ it('should return error', function() {
+
+ // given
+ const executionListeners = createElement('zeebe:ExecutionListeners', {
+ listeners: [
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'foo'
+ }),
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'foo'
+ }),
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'bar'
+ }),
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'baz'
+ })
+ ]
+ });
+
+ // when
+ const errors = hasDuplicatedPropertiesValues(executionListeners, 'listeners', [ 'eventType', 'type' ]);
+
+ // then
+ expect(errors).to.exist;
+ expect(errors).to.have.length(1);
+
+ expect(errors[ 0 ]).eql({
+ message: 'Properties of type have properties with duplicate values (property with duplicate value of , property with duplicate value of )',
+ path: null,
+ data: {
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
+ node: executionListeners,
+ parentNode: null,
+ duplicatedProperties: {
+ eventType: 'start',
+ type: 'foo'
+ },
+ properties: executionListeners.get('listeners').filter(listener => listener.get('type') === 'foo'),
+ propertiesName: 'listeners'
+ }
+ });
+ });
+
+
+ it('should return errors', function() {
+
+ // given
+ const executionListeners = createElement('zeebe:ExecutionListeners', {
+ listeners: [
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'foo'
+ }),
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'foo'
+ }),
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'bar'
+ }),
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'bar'
+ }),
+ createElement('zeebe:ExecutionListener', {
+ eventType: 'start',
+ type: 'baz'
+ })
+ ]
+ });
+
+ // when
+ const errors = hasDuplicatedPropertiesValues(executionListeners, 'listeners', [ 'eventType', 'type' ]);
+
+ // then
+ expect(errors).to.exist;
+ expect(errors).to.have.length(2);
+
+ expect(errors[ 0 ]).eql({
+ message: 'Properties of type have properties with duplicate values (property with duplicate value of , property with duplicate value of )',
+ path: null,
+ data: {
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
+ node: executionListeners,
+ parentNode: null,
+ duplicatedProperties: {
+ eventType: 'start',
+ type: 'foo'
+ },
+ properties: executionListeners.get('listeners').filter(listener => listener.get('type') === 'foo'),
+ propertiesName: 'listeners'
+ }
+ });
+
+ expect(errors[ 1 ]).eql({
+ message: 'Properties of type have properties with duplicate values (property with duplicate value of , property with duplicate value of )',
+ path: null,
+ data: {
+ type: ERROR_TYPES.PROPERTY_VALUES_DUPLICATED,
+ node: executionListeners,
+ parentNode: null,
+ duplicatedProperties: {
+ eventType: 'start',
+ type: 'bar'
+ },
+ properties: executionListeners.get('listeners').filter(listener => listener.get('type') === 'bar'),
+ propertiesName: 'listeners'
+ }
+ });
+ });
+
+ });
+
+
describe('#hasProperty', function() {
it('should not return errors (single property)', function() {