From a9253e925b84ebb17e59dd903bf0ffe4a62d1819 Mon Sep 17 00:00:00 2001 From: Jarek Danielak Date: Mon, 17 Feb 2025 20:30:59 +0100 Subject: [PATCH] feat: add `ad-hoc-sub-process-activity` rule This rules ensures that an Ad-Hoc Sub-Process contains at least one activity. Related to https://github.com/camunda/camunda-modeler/issues/4811 --- index.js | 4 +- .../ad-hoc-sub-process-activity.js | 34 ++++++++++ .../ad-hoc-sub-process-activity.spec.js | 59 ++++++++++++++++ .../ad-hoc-sub-process-activity-errors.bpmn | 13 ++++ .../ad-hoc-sub-process-activity.bpmn | 18 +++++ .../ad-hoc-sub-process-activity.spec.js | 68 +++++++++++++++++++ test/config/configs.spec.js | 3 + 7 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 rules/camunda-cloud/ad-hoc-sub-process-activity.js create mode 100644 test/camunda-cloud/ad-hoc-sub-process-activity.spec.js create mode 100644 test/camunda-cloud/integration/ad-hoc-sub-process-activity-errors.bpmn create mode 100644 test/camunda-cloud/integration/ad-hoc-sub-process-activity.bpmn create mode 100644 test/camunda-cloud/integration/ad-hoc-sub-process-activity.spec.js diff --git a/index.js b/index.js index 3bb922a..a0f35f8 100644 --- a/index.js +++ b/index.js @@ -96,7 +96,8 @@ const camundaCloud86Rules = withConfig({ }, { version: '8.6' }); const camundaCloud87Rules = withConfig({ - ...camundaCloud86Rules + ...camundaCloud86Rules, + 'ad-hoc-sub-process-activity': 'error', }, { version: '8.7' }); const camundaCloud88Rules = withConfig({ @@ -132,6 +133,7 @@ const camundaPlatform723Rules = withConfig(camundaPlatform721Rules, { }); const rules = { + 'ad-hoc-sub-process-activity': './rules/camunda-cloud/ad-hoc-sub-process-activity', 'element-type': './rules/camunda-cloud/element-type', 'called-element': './rules/camunda-cloud/called-element', 'collapsed-subprocess': './rules/camunda-cloud/collapsed-subprocess', diff --git a/rules/camunda-cloud/ad-hoc-sub-process-activity.js b/rules/camunda-cloud/ad-hoc-sub-process-activity.js new file mode 100644 index 0000000..0da4221 --- /dev/null +++ b/rules/camunda-cloud/ad-hoc-sub-process-activity.js @@ -0,0 +1,34 @@ +const { is, isAny } = require('bpmnlint-utils'); + +const { reportErrors } = require('../utils/reporter'); + +const { skipInNonExecutableProcess } = require('../utils/rule'); + +module.exports = skipInNonExecutableProcess(function() { + function check(node, reporter) { + if (!is(node, 'bpmn:AdHocSubProcess')) { + return; + } + + // Ad-Hoc Sub-Process must contain at least one activity + if (node.get('flowElements').some(isActivity)) { + return; + } + + reportErrors(node, reporter, { + message: 'Element of type must contain at least one activity', + data: { + node, + parentNode: null + } + }); + } + + return { + check + }; +}); + +function isActivity(element) { + return isAny(element, [ 'bpmn:Task', 'bpmn:SubProcess' ]); +} \ No newline at end of file diff --git a/test/camunda-cloud/ad-hoc-sub-process-activity.spec.js b/test/camunda-cloud/ad-hoc-sub-process-activity.spec.js new file mode 100644 index 0000000..fd0328c --- /dev/null +++ b/test/camunda-cloud/ad-hoc-sub-process-activity.spec.js @@ -0,0 +1,59 @@ +const RuleTester = require('bpmnlint/lib/testers/rule-tester'); + +const rule = require('../../rules/camunda-cloud/ad-hoc-sub-process-activity'); + +const { + createModdle, + createProcess +} = require('../helper'); + +const valid = [ + { + name: 'ad hoc sub process (with task)', + moddleElement: createModdle(createProcess(` + + + + `)) + } +]; + +const invalid = [ + { + name: 'ad hoc sub process (empty)', + moddleElement: createModdle(createProcess(` + + + `)), + report: { + id: 'Subprocess_1', + message: 'Element of type must contain at least one activity', + data: { + node: 'Subprocess_1', + parentNode: null + } + } + }, + { + name: 'ad hoc sub process (no activity)', + moddleElement: createModdle(createProcess(` + + + + + `)), + report: { + id: 'Subprocess_1', + message: 'Element of type must contain at least one activity', + data: { + node: 'Subprocess_1', + parentNode: null + } + } + } +]; + +RuleTester.verify('called-element', rule, { + valid, + invalid +}); \ No newline at end of file diff --git a/test/camunda-cloud/integration/ad-hoc-sub-process-activity-errors.bpmn b/test/camunda-cloud/integration/ad-hoc-sub-process-activity-errors.bpmn new file mode 100644 index 0000000..70dcb1d --- /dev/null +++ b/test/camunda-cloud/integration/ad-hoc-sub-process-activity-errors.bpmn @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/test/camunda-cloud/integration/ad-hoc-sub-process-activity.bpmn b/test/camunda-cloud/integration/ad-hoc-sub-process-activity.bpmn new file mode 100644 index 0000000..c11c903 --- /dev/null +++ b/test/camunda-cloud/integration/ad-hoc-sub-process-activity.bpmn @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/test/camunda-cloud/integration/ad-hoc-sub-process-activity.spec.js b/test/camunda-cloud/integration/ad-hoc-sub-process-activity.spec.js new file mode 100644 index 0000000..4fa57e4 --- /dev/null +++ b/test/camunda-cloud/integration/ad-hoc-sub-process-activity.spec.js @@ -0,0 +1,68 @@ +const { expect } = require('chai'); + +const Linter = require('bpmnlint/lib/linter'); + +const NodeResolver = require('bpmnlint/lib/resolver/node-resolver'); + +const { readModdle } = require('../../helper'); + +const versions = [ + '8.7', + '8.8', +]; + +describe('integration - ad-hoc-sub-process-activity', function() { + + let linter; + + versions.forEach(function(version) { + + 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/ad-hoc-sub-process-activity.bpmn'); + + // when + const reports = await linter.lint(root); + + // then + expect(reports[ 'camunda-compat/ad-hoc-sub-process-activity' ]).not.to.exist; + }); + + }); + + + describe('errors', function() { + + it('should have errors', async function() { + + // given + const { root } = await readModdle('test/camunda-cloud/integration/ad-hoc-sub-process-activity-errors.bpmn'); + + // when + const reports = await linter.lint(root); + + // then + expect(reports[ 'camunda-compat/ad-hoc-sub-process-activity' ]).to.exist; + }); + + }); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/config/configs.spec.js b/test/config/configs.spec.js index 619944a..9046390 100644 --- a/test/config/configs.spec.js +++ b/test/config/configs.spec.js @@ -401,6 +401,7 @@ describe('configs', function() { it('camunda-cloud-8-7', expectRules(configs, 'camunda-cloud-8-7', { + 'ad-hoc-sub-process-activity': [ 'error', { version: '8.7' } ], 'called-element': [ 'error', { version: '8.7' } ], 'connector-properties': [ 'warn', { version: '8.7' } ], 'duplicate-execution-listeners': [ 'error', { version: '8.7' } ], @@ -438,6 +439,7 @@ describe('configs', function() { it('camunda-cloud-8-8', expectRules(configs, 'camunda-cloud-8-8', { + 'ad-hoc-sub-process-activity': [ 'error', { version: '8.8' } ], 'called-element': [ 'error', { version: '8.8' } ], 'connector-properties': [ 'warn', { version: '8.8' } ], 'duplicate-execution-listeners': [ 'error', { version: '8.8' } ], @@ -499,6 +501,7 @@ describe('configs', function() { it('all', expectRules(configs, 'all', { + 'ad-hoc-sub-process-activity': 'error', 'element-type': 'error', 'called-element': 'error', 'collapsed-subprocess': 'error',