From a3cb077fb3b85cbf246c70b8bcdbceab3afc200c Mon Sep 17 00:00:00 2001 From: Philipp Fromme Date: Thu, 1 Aug 2024 14:09:43 +0200 Subject: [PATCH] feat: lint `zeebe:bindingType` Related to https://github.com/camunda/camunda-modeler/issues/4386 --- index.js | 8 +- rules/camunda-cloud/no-binding-type.js | 44 ++++++ .../integration/no-binding-type-errors.bpmn | 25 ++++ .../integration/no-binding-type.bpmn | 25 ++++ .../integration/no-binding-type.spec.js | 77 +++++++++++ test/camunda-cloud/no-binding-type.spec.js | 126 ++++++++++++++++++ test/config/configs.spec.js | 11 ++ 7 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 rules/camunda-cloud/no-binding-type.js create mode 100644 test/camunda-cloud/integration/no-binding-type-errors.bpmn create mode 100644 test/camunda-cloud/integration/no-binding-type.bpmn create mode 100644 test/camunda-cloud/integration/no-binding-type.spec.js create mode 100644 test/camunda-cloud/no-binding-type.spec.js diff --git a/index.js b/index.js index 5bdb836..4553d14 100644 --- a/index.js +++ b/index.js @@ -11,6 +11,7 @@ const camundaCloud10Rules = withConfig({ 'executable-process': 'error', 'loop-characteristics': 'error', 'message-reference': 'error', + 'no-binding-type': 'error', 'no-candidate-users': 'error', 'no-execution-listeners': 'error', 'no-expression': 'error', @@ -77,7 +78,11 @@ const camundaCloud85Rules = withConfig({ }, { version: '8.5' }); const camundaCloud86Rules = withConfig({ - ...omit(camundaCloud85Rules, [ 'inclusive-gateway', 'no-execution-listeners' ]), + ...omit(camundaCloud85Rules, [ + 'inclusive-gateway', + 'no-binding-type', + 'no-execution-listeners' + ]), 'duplicate-execution-listeners': 'error', 'execution-listener': 'error' }, { version: '8.6' }); @@ -124,6 +129,7 @@ const rules = { 'link-event': './rules/camunda-cloud/link-event', 'loop-characteristics': './rules/camunda-cloud/loop-characteristics', 'message-reference': './rules/camunda-cloud/message-reference', + 'no-binding-type': './rules/camunda-cloud/no-binding-type', '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', diff --git a/rules/camunda-cloud/no-binding-type.js b/rules/camunda-cloud/no-binding-type.js new file mode 100644 index 0000000..cd4b8d5 --- /dev/null +++ b/rules/camunda-cloud/no-binding-type.js @@ -0,0 +1,44 @@ +const { isAny } = require('bpmnlint-utils'); + +const { hasProperties, findExtensionElement } = require('../utils/element'); + +const { reportErrors } = require('../utils/reporter'); + +const { skipInNonExecutableProcess } = require('../utils/rule'); + +module.exports = skipInNonExecutableProcess(function() { + function check(node, reporter) { + if (!isAny(node, [ + 'bpmn:BusinessRuleTask', + 'bpmn:CallActivity', + 'bpmn:UserTask' + ])) { + return; + } + + const extensionElement = findExtensionElement(node, [ + 'zeebe:CalledDecision', + 'zeebe:CalledElement', + 'zeebe:FormDefinition' + ]); + + if (!extensionElement) { + return; + } + + const errors = hasProperties(extensionElement, { + bindingType: { + allowed: (value) => !value || value === 'latest', + allowedVersion: '8.6' + } + }, node); + + if (errors && errors.length) { + reportErrors(node, reporter, errors); + } + } + + return { + check + }; +}); \ No newline at end of file diff --git a/test/camunda-cloud/integration/no-binding-type-errors.bpmn b/test/camunda-cloud/integration/no-binding-type-errors.bpmn new file mode 100644 index 0000000..f8368a2 --- /dev/null +++ b/test/camunda-cloud/integration/no-binding-type-errors.bpmn @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/camunda-cloud/integration/no-binding-type.bpmn b/test/camunda-cloud/integration/no-binding-type.bpmn new file mode 100644 index 0000000..0a67007 --- /dev/null +++ b/test/camunda-cloud/integration/no-binding-type.bpmn @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/camunda-cloud/integration/no-binding-type.spec.js b/test/camunda-cloud/integration/no-binding-type.spec.js new file mode 100644 index 0000000..6fdb822 --- /dev/null +++ b/test/camunda-cloud/integration/no-binding-type.spec.js @@ -0,0 +1,77 @@ +const { expect } = require('chai'); + +const Linter = require('bpmnlint/lib/linter'); + +const NodeResolver = require('bpmnlint/lib/resolver/node-resolver'); + +const { readModdle } = require('../../helper'); + +const versions = [ + '1.0', + '1.1', + '1.2', + '1.3', + '8.0', + '8.1', + '8.2', + '8.3', + '8.4', + '8.5' +]; + +describe('integration - no-binding-type', function() { + + versions.forEach(function(version) { + + let linter; + + beforeEach(function() { + linter = new Linter({ + config: { + extends: `plugin:camunda-compat/camunda-cloud-${ version.replace('.', '-') }` + }, + resolver: new NodeResolver() + }); + }); + + + describe(`Camunda Cloud ${ version }`, function() { + + describe('no errors', function() { + + it('should not have errors', async function() { + + // given + const { root } = await readModdle('test/camunda-cloud/integration/no-binding-type.bpmn'); + + // when + const reports = await linter.lint(root); + + // then + expect(reports[ 'camunda-compat/no-binding-type' ]).not.to.exist; + }); + + }); + + + describe('errors', function() { + + it('should have errors', async function() { + + // given + const { root } = await readModdle('test/camunda-cloud/integration/no-binding-type-errors.bpmn'); + + // when + const reports = await linter.lint(root); + + // then + expect(reports[ 'camunda-compat/no-binding-type' ]).to.exist; + }); + + }); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/camunda-cloud/no-binding-type.spec.js b/test/camunda-cloud/no-binding-type.spec.js new file mode 100644 index 0000000..7a9b667 --- /dev/null +++ b/test/camunda-cloud/no-binding-type.spec.js @@ -0,0 +1,126 @@ +const RuleTester = require('bpmnlint/lib/testers/rule-tester'); + +const rule = require('../../rules/camunda-cloud/no-binding-type'); + +const { + createDefinitions, + createModdle, + createProcess +} = require('../helper'); + +const { ERROR_TYPES } = require('../../rules/utils/element'); + +const valid = [ + { + name: 'called element (latest)', + moddleElement: createModdle(createProcess(` + + + + + + `)) + }, + { + name: 'called element (deployment) (non-executable process)', + config: { version: '8.2' }, + moddleElement: createModdle(createDefinitions(` + + + + + + + + `)) + } +]; + +const invalid = [ + { + name: 'called decision (deployment)', + moddleElement: createModdle(createProcess(` + + + + + + `)), + report: { + id: 'BusinessRuleTask_1', + message: 'Property value of only allowed by Camunda 8.6 or newer', + path: [ + 'extensionElements', + 'values', + 0, + 'bindingType' + ], + data: { + type: ERROR_TYPES.PROPERTY_VALUE_NOT_ALLOWED, + node: 'zeebe:CalledDecision', + parentNode: 'BusinessRuleTask_1', + property: 'bindingType', + allowedVersion: '8.6' + } + } + }, + { + name: 'called element (deployment)', + moddleElement: createModdle(createProcess(` + + + + + + `)), + report: { + id: 'CallActivity_1', + message: 'Property value of only allowed by Camunda 8.6 or newer', + path: [ + 'extensionElements', + 'values', + 0, + 'bindingType' + ], + data: { + type: ERROR_TYPES.PROPERTY_VALUE_NOT_ALLOWED, + node: 'zeebe:CalledElement', + parentNode: 'CallActivity_1', + property: 'bindingType', + allowedVersion: '8.6' + } + } + }, + { + name: 'form definition (deployment)', + moddleElement: createModdle(createProcess(` + + + + + + `)), + report: { + id: 'UserTask_1', + message: 'Property value of only allowed by Camunda 8.6 or newer', + path: [ + 'extensionElements', + 'values', + 0, + 'bindingType' + ], + data: { + type: ERROR_TYPES.PROPERTY_VALUE_NOT_ALLOWED, + node: 'zeebe:FormDefinition', + parentNode: 'UserTask_1', + property: 'bindingType', + allowedVersion: '8.6' + } + } + } +]; + +RuleTester.verify('no-binding-type', rule, { + valid, + invalid +}); \ No newline at end of file diff --git a/test/config/configs.spec.js b/test/config/configs.spec.js index 8c701b9..d595518 100644 --- a/test/config/configs.spec.js +++ b/test/config/configs.spec.js @@ -16,6 +16,7 @@ describe('configs', function() { 'feel': [ 'error', { version: '1.0' } ], 'loop-characteristics': [ 'error', { version: '1.0' } ], 'message-reference': [ 'error', { version: '1.0' } ], + 'no-binding-type': [ 'error', { version: '1.0' } ], 'no-candidate-users': [ 'error', { version: '1.0' } ], 'no-execution-listeners': [ 'error', { version: '1.0' } ], 'no-expression': [ 'error', { version: '1.0' } ], @@ -47,6 +48,7 @@ describe('configs', function() { 'feel': [ 'error', { version: '1.1' } ], 'loop-characteristics': [ 'error', { version: '1.1' } ], 'message-reference': [ 'error', { version: '1.1' } ], + 'no-binding-type': [ 'error', { version: '1.1' } ], 'no-candidate-users': [ 'error', { version: '1.1' } ], 'no-execution-listeners': [ 'error', { version: '1.1' } ], 'no-expression': [ 'error', { version: '1.1' } ], @@ -78,6 +80,7 @@ describe('configs', function() { 'feel': [ 'error', { version: '1.2' } ], 'loop-characteristics': [ 'error', { version: '1.2' } ], 'message-reference': [ 'error', { version: '1.2' } ], + 'no-binding-type': [ 'error', { version: '1.2' } ], 'no-candidate-users': [ 'error', { version: '1.2' } ], 'no-execution-listeners': [ 'error', { version: '1.2' } ], 'no-expression': [ 'error', { version: '1.2' } ], @@ -109,6 +112,7 @@ describe('configs', function() { 'feel': [ 'error', { version: '1.3' } ], 'loop-characteristics': [ 'error', { version: '1.3' } ], 'message-reference': [ 'error', { version: '1.3' } ], + 'no-binding-type': [ 'error', { version: '1.3' } ], 'no-candidate-users': [ 'error', { version: '1.3' } ], 'no-execution-listeners': [ 'error', { version: '1.3' } ], 'no-expression': [ 'error', { version: '1.3' } ], @@ -141,6 +145,7 @@ describe('configs', function() { 'implementation': [ 'error', { version: '8.0' } ], 'loop-characteristics': [ 'error', { version: '8.0' } ], 'message-reference': [ 'error', { version: '8.0' } ], + 'no-binding-type': [ 'error', { version: '8.0' } ], 'no-candidate-users': [ 'error', { version: '8.0' } ], 'no-execution-listeners': [ 'error', { version: '8.0' } ], 'no-expression': [ 'error', { version: '8.0' } ], @@ -173,6 +178,7 @@ describe('configs', function() { 'inclusive-gateway': [ 'error', { version: '8.1' } ], 'loop-characteristics': [ 'error', { version: '8.1' } ], 'message-reference': [ 'error', { version: '8.1' } ], + 'no-binding-type': [ 'error', { version: '8.1' } ], 'no-candidate-users': [ 'error', { version: '8.1' } ], 'no-execution-listeners': [ 'error', { version: '8.1' } ], 'no-expression': [ 'error', { version: '8.1' } ], @@ -207,6 +213,7 @@ describe('configs', function() { 'link-event': [ 'error', { version: '8.2' } ], 'loop-characteristics': [ 'error', { version: '8.2' } ], 'message-reference': [ 'error', { version: '8.2' } ], + 'no-binding-type': [ 'error', { version: '8.2' } ], 'no-execution-listeners': [ 'error', { version: '8.2' } ], 'no-expression': [ 'error', { version: '8.2' } ], 'no-loop': [ 'error', { version: '8.2' } ], @@ -240,6 +247,7 @@ describe('configs', function() { 'link-event': [ 'error', { version: '8.3' } ], 'loop-characteristics': [ 'error', { version: '8.3' } ], 'message-reference': [ 'error', { version: '8.3' } ], + 'no-binding-type': [ 'error', { version: '8.3' } ], 'no-execution-listeners': [ 'error', { version: '8.3' } ], 'no-expression': [ 'error', { version: '8.3' } ], 'no-loop': [ 'error', { version: '8.3' } ], @@ -273,6 +281,7 @@ describe('configs', function() { 'link-event': [ 'error', { version: '8.4' } ], 'loop-characteristics': [ 'error', { version: '8.4' } ], 'message-reference': [ 'error', { version: '8.4' } ], + 'no-binding-type': [ 'error', { version: '8.4' } ], 'no-execution-listeners': [ 'error', { version: '8.4' } ], 'no-expression': [ 'error', { version: '8.4' } ], 'no-loop': [ 'error', { version: '8.4' } ], @@ -306,6 +315,7 @@ describe('configs', function() { 'link-event': [ 'error', { version: '8.5' } ], 'loop-characteristics': [ 'error', { version: '8.5' } ], 'message-reference': [ 'error', { version: '8.5' } ], + 'no-binding-type': [ 'error', { version: '8.5' } ], 'no-execution-listeners': [ 'error', { version: '8.5' } ], 'no-expression': [ 'error', { version: '8.5' } ], 'no-loop': [ 'error', { version: '8.5' } ], @@ -395,6 +405,7 @@ describe('configs', function() { 'link-event': 'error', 'loop-characteristics': 'error', 'message-reference': 'error', + 'no-binding-type': 'error', 'no-candidate-users': 'error', 'no-execution-listeners': 'error', 'no-expression': 'error',