Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ad-hoc-sub-process rule #160

Merged
merged 2 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/all.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const allRules = [
'ad-hoc-sub-process',
'conditional-flows',
'end-event-required',
'event-sub-process-typed-start-event',
Expand Down
1 change: 1 addition & 0 deletions config/recommended.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = {
rules: {
'ad-hoc-sub-process': 'error',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree!

'conditional-flows': 'error',
'end-event-required': 'error',
'event-sub-process-typed-start-event': 'error',
Expand Down
1 change: 1 addition & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

This library implements the following lint rules:

* [Ad-Hoc Sub-Process](./ad-hoc-sub-process.md)
* [Conditional Flows](./conditional-flows.md)
* [End Event Required](./end-event-required.md)
* [Event Sub-Process Typed Start Event](./event-sub-process-typed-start-event.md)
Expand Down
20 changes: 20 additions & 0 deletions docs/rules/ad-hoc-sub-process.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Ad-hoc Sub-Process (ad-hoc-sub-process)

Ensure that an Ad-Hoc Sub-Process is valid according to the BPMN specification:

- Must not contain start or end events.
- Every intermediate event must have an outgoing sequence flow.


Example of __incorrect__ usage for this rule:

![Incorrect usage example](./examples/ad-hoc-sub-process-incorrect.png)

Cf. [`ad-hoc-sub-process-incorrect.bpmn`](./examples/no-complex-gateway-incorrect.bpmn).


Example of __correct__ usage for this rule:

![Correct usage example](./examples/ad-hoc-sub-process-correct.png)

Cf. [`ad-hoc-sub-process-correct.bpmn`](./examples/no-complex-gateway-correct.bpmn).
58 changes: 58 additions & 0 deletions docs/rules/examples/ad-hoc-sub-process-correct.bpmn
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="sid-38422fae-e03e-43a3-bef4-bd33b32041b2" targetNamespace="http://bpmn.io/bpmn" exporter="Camunda Modeler" exporterVersion="5.32.0-rc.0">
<process id="Process_1" isExecutable="false">
<adHocSubProcess id="Activity_1yjcy2x">
<task id="Activity_0o1bgqx">
<outgoing>Flow_0njsews</outgoing>
</task>
<task id="Activity_142jk6n">
<incoming>Flow_0njsews</incoming>
</task>
<sequenceFlow id="Flow_0njsews" sourceRef="Activity_0o1bgqx" targetRef="Activity_142jk6n" />
<intermediateCatchEvent id="Event_0q40via">
<outgoing>Flow_1ykoger</outgoing>
<conditionalEventDefinition id="ConditionalEventDefinition_0q04q9v">
<condition xsi:type="tFormalExpression" />
</conditionalEventDefinition>
</intermediateCatchEvent>
<task id="Activity_12w8ibt" />
<task id="Activity_1u082wx">
<incoming>Flow_1ykoger</incoming>
</task>
<sequenceFlow id="Flow_1ykoger" sourceRef="Event_0q40via" targetRef="Activity_1u082wx" />
</adHocSubProcess>
</process>
<bpmndi:BPMNDiagram id="BpmnDiagram_1">
<bpmndi:BPMNPlane id="BpmnPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="Activity_02xy6cc_di" bpmnElement="Activity_1yjcy2x" isExpanded="true">
<omgdc:Bounds x="160" y="80" width="390" height="410" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_12w8ibt_di" bpmnElement="Activity_12w8ibt">
<omgdc:Bounds x="220" y="120" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0o1bgqx_di" bpmnElement="Activity_0o1bgqx">
<omgdc:Bounds x="220" y="240" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BPMNShape_1b353o5" bpmnElement="Activity_142jk6n">
<omgdc:Bounds x="380" y="240" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1u082wx_di" bpmnElement="Activity_1u082wx">
<omgdc:Bounds x="380" y="360" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_069bzsw_di" bpmnElement="Event_0q40via">
<omgdc:Bounds x="252" y="382" width="36" height="36" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="408" y="485" width="84" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0njsews_di" bpmnElement="Flow_0njsews">
<omgdi:waypoint x="320" y="280" />
<omgdi:waypoint x="380" y="280" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1ykoger_di" bpmnElement="Flow_1ykoger">
<omgdi:waypoint x="288" y="400" />
<omgdi:waypoint x="380" y="400" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions docs/rules/examples/ad-hoc-sub-process-incorrect.bpmn
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="sid-38422fae-e03e-43a3-bef4-bd33b32041b2" targetNamespace="http://bpmn.io/bpmn" exporter="Camunda Modeler" exporterVersion="5.32.0-rc.0">
<process id="Process_1" isExecutable="false">
<adHocSubProcess id="Activity_1yjcy2x">
<intermediateCatchEvent id="Event_0q40via">
<conditionalEventDefinition id="ConditionalEventDefinition_0q04q9v">
<condition xsi:type="tFormalExpression" />
</conditionalEventDefinition>
</intermediateCatchEvent>
<startEvent id="Event_03k6pnd">
<outgoing>Flow_0ej0cl8</outgoing>
</startEvent>
<endEvent id="Event_03gcp25">
<incoming>Flow_0ej0cl8</incoming>
</endEvent>
<sequenceFlow id="Flow_0ej0cl8" sourceRef="Event_03k6pnd" targetRef="Event_03gcp25" />
</adHocSubProcess>
</process>
<bpmndi:BPMNDiagram id="BpmnDiagram_1">
<bpmndi:BPMNPlane id="BpmnPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="Activity_02xy6cc_di" bpmnElement="Activity_1yjcy2x" isExpanded="true">
<omgdc:Bounds x="160" y="80" width="390" height="250" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_03k6pnd_di" bpmnElement="Event_03k6pnd">
<omgdc:Bounds x="252" y="132" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_03gcp25_di" bpmnElement="Event_03gcp25">
<omgdc:Bounds x="422" y="132" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_069bzsw_di" bpmnElement="Event_0q40via">
<omgdc:Bounds x="252" y="232" width="36" height="36" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="408" y="485" width="84" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0ej0cl8_di" bpmnElement="Flow_0ej0cl8">
<omgdi:waypoint x="288" y="150" />
<omgdi:waypoint x="422" y="150" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions rules/ad-hoc-sub-process.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const {
is,
isAny
} = require('bpmnlint-utils');

/**
* A rule that ensures that an Ad Hoc Sub Process is valid according to the BPMN spec:
*
* - No start or end events
* - Every intermediate event has an outgoing sequence flow
*/
module.exports = function() {

function check(node, reporter) {

if (!is(node, 'bpmn:AdHocSubProcess')) {
return;
}

const flowElements = node.flowElements || [];

flowElements.forEach(function(flowElement) {

if (is(flowElement, 'bpmn:StartEvent')) {
reporter.report(flowElement.id, 'A <Start Event> is not allowed in <Ad Hoc Sub Process>');
}

if (is(flowElement, 'bpmn:EndEvent')) {
reporter.report(flowElement.id, 'An <End Event> is not allowed in <Ad Hoc Sub Process>');
}

if (isAny(flowElement, [ 'bpmn:IntermediateCatchEvent', 'bpmn:IntermediateThrowEvent' ])) {
if (!flowElement.outgoing || flowElement.outgoing.length === 0) {
reporter.report(flowElement.id, 'An intermediate event inside <Ad Hoc Sub Process> must have an outgoing sequence flow');
}
}

});
}

return {
check
};

};
117 changes: 61 additions & 56 deletions test/integration/compilation/test/bpmnlintrc.expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Resolver.prototype.resolveConfig = function(pkg, configName) {
const resolver = new Resolver();

const rules = {
"ad-hoc-sub-process": "error",
"conditional-flows": "error",
"end-event-required": "error",
"event-sub-process-typed-start-event": "error",
Expand Down Expand Up @@ -72,114 +73,118 @@ export { resolver, config };

export default bundle;

import rule_0 from 'bpmnlint/rules/conditional-flows';
import rule_0 from 'bpmnlint/rules/ad-hoc-sub-process';

cache['bpmnlint/conditional-flows'] = rule_0;
cache['bpmnlint/ad-hoc-sub-process'] = rule_0;

import rule_1 from 'bpmnlint/rules/end-event-required';
import rule_1 from 'bpmnlint/rules/conditional-flows';

cache['bpmnlint/end-event-required'] = rule_1;
cache['bpmnlint/conditional-flows'] = rule_1;

import rule_2 from 'bpmnlint/rules/event-sub-process-typed-start-event';
import rule_2 from 'bpmnlint/rules/end-event-required';

cache['bpmnlint/event-sub-process-typed-start-event'] = rule_2;
cache['bpmnlint/end-event-required'] = rule_2;

import rule_3 from 'bpmnlint/rules/fake-join';
import rule_3 from 'bpmnlint/rules/event-sub-process-typed-start-event';

cache['bpmnlint/fake-join'] = rule_3;
cache['bpmnlint/event-sub-process-typed-start-event'] = rule_3;

import rule_4 from 'bpmnlint/rules/global';
import rule_4 from 'bpmnlint/rules/fake-join';

cache['bpmnlint/global'] = rule_4;
cache['bpmnlint/fake-join'] = rule_4;

import rule_5 from 'bpmnlint/rules/label-required';
import rule_5 from 'bpmnlint/rules/global';

cache['bpmnlint/label-required'] = rule_5;
cache['bpmnlint/global'] = rule_5;

import rule_6 from 'bpmnlint/rules/link-event';
import rule_6 from 'bpmnlint/rules/label-required';

cache['bpmnlint/link-event'] = rule_6;
cache['bpmnlint/label-required'] = rule_6;

import rule_7 from 'bpmnlint/rules/no-bpmndi';
import rule_7 from 'bpmnlint/rules/link-event';

cache['bpmnlint/no-bpmndi'] = rule_7;
cache['bpmnlint/link-event'] = rule_7;

import rule_8 from 'bpmnlint/rules/no-complex-gateway';
import rule_8 from 'bpmnlint/rules/no-bpmndi';

cache['bpmnlint/no-complex-gateway'] = rule_8;
cache['bpmnlint/no-bpmndi'] = rule_8;

import rule_9 from 'bpmnlint/rules/no-disconnected';
import rule_9 from 'bpmnlint/rules/no-complex-gateway';

cache['bpmnlint/no-disconnected'] = rule_9;
cache['bpmnlint/no-complex-gateway'] = rule_9;

import rule_10 from 'bpmnlint/rules/no-duplicate-sequence-flows';
import rule_10 from 'bpmnlint/rules/no-disconnected';

cache['bpmnlint/no-duplicate-sequence-flows'] = rule_10;
cache['bpmnlint/no-disconnected'] = rule_10;

import rule_11 from 'bpmnlint/rules/no-gateway-join-fork';
import rule_11 from 'bpmnlint/rules/no-duplicate-sequence-flows';

cache['bpmnlint/no-gateway-join-fork'] = rule_11;
cache['bpmnlint/no-duplicate-sequence-flows'] = rule_11;

import rule_12 from 'bpmnlint/rules/no-implicit-split';
import rule_12 from 'bpmnlint/rules/no-gateway-join-fork';

cache['bpmnlint/no-implicit-split'] = rule_12;
cache['bpmnlint/no-gateway-join-fork'] = rule_12;

import rule_13 from 'bpmnlint/rules/no-implicit-end';
import rule_13 from 'bpmnlint/rules/no-implicit-split';

cache['bpmnlint/no-implicit-end'] = rule_13;
cache['bpmnlint/no-implicit-split'] = rule_13;

import rule_14 from 'bpmnlint/rules/no-implicit-start';
import rule_14 from 'bpmnlint/rules/no-implicit-end';

cache['bpmnlint/no-implicit-start'] = rule_14;
cache['bpmnlint/no-implicit-end'] = rule_14;

import rule_15 from 'bpmnlint/rules/no-inclusive-gateway';
import rule_15 from 'bpmnlint/rules/no-implicit-start';

cache['bpmnlint/no-inclusive-gateway'] = rule_15;
cache['bpmnlint/no-implicit-start'] = rule_15;

import rule_16 from 'bpmnlint/rules/no-overlapping-elements';
import rule_16 from 'bpmnlint/rules/no-inclusive-gateway';

cache['bpmnlint/no-overlapping-elements'] = rule_16;
cache['bpmnlint/no-inclusive-gateway'] = rule_16;

import rule_17 from 'bpmnlint/rules/single-blank-start-event';
import rule_17 from 'bpmnlint/rules/no-overlapping-elements';

cache['bpmnlint/single-blank-start-event'] = rule_17;
cache['bpmnlint/no-overlapping-elements'] = rule_17;

import rule_18 from 'bpmnlint/rules/single-event-definition';
import rule_18 from 'bpmnlint/rules/single-blank-start-event';

cache['bpmnlint/single-event-definition'] = rule_18;
cache['bpmnlint/single-blank-start-event'] = rule_18;

import rule_19 from 'bpmnlint/rules/start-event-required';
import rule_19 from 'bpmnlint/rules/single-event-definition';

cache['bpmnlint/start-event-required'] = rule_19;
cache['bpmnlint/single-event-definition'] = rule_19;

import rule_20 from 'bpmnlint/rules/sub-process-blank-start-event';
import rule_20 from 'bpmnlint/rules/start-event-required';

cache['bpmnlint/sub-process-blank-start-event'] = rule_20;
cache['bpmnlint/start-event-required'] = rule_20;

import rule_21 from 'bpmnlint/rules/superfluous-gateway';
import rule_21 from 'bpmnlint/rules/sub-process-blank-start-event';

cache['bpmnlint/superfluous-gateway'] = rule_21;
cache['bpmnlint/sub-process-blank-start-event'] = rule_21;

import rule_22 from 'bpmnlint/rules/superfluous-termination';
import rule_22 from 'bpmnlint/rules/superfluous-gateway';

cache['bpmnlint/superfluous-termination'] = rule_22;
cache['bpmnlint/superfluous-gateway'] = rule_22;

import rule_23 from 'bpmnlint-plugin-test/rules/no-label-foo';
import rule_23 from 'bpmnlint/rules/superfluous-termination';

cache['bpmnlint-plugin-test/no-label-foo'] = rule_23;
cache['bpmnlint/superfluous-termination'] = rule_23;

import rule_24 from 'bpmnlint-plugin-exported/src/foo';
import rule_24 from 'bpmnlint-plugin-test/rules/no-label-foo';

cache['bpmnlint-plugin-exported/foo'] = rule_24;
cache['bpmnlint-plugin-test/no-label-foo'] = rule_24;

import rule_25 from 'bpmnlint-plugin-exported/src/bar';
import rule_25 from 'bpmnlint-plugin-exported/src/foo';

cache['bpmnlint-plugin-exported/bar'] = rule_25;
cache['bpmnlint-plugin-exported/foo'] = rule_25;

import rule_26 from 'bpmnlint-plugin-exported/rules/baz';
import rule_26 from 'bpmnlint-plugin-exported/src/bar';

cache['bpmnlint-plugin-exported/baz'] = rule_26;
cache['bpmnlint-plugin-exported/bar'] = rule_26;

import rule_27 from 'bpmnlint-plugin-exported/src/foo';
import rule_27 from 'bpmnlint-plugin-exported/rules/baz';

cache['bpmnlint-plugin-exported/foo-absolute'] = rule_27;
cache['bpmnlint-plugin-exported/baz'] = rule_27;

import rule_28 from 'bpmnlint-plugin-exported/src/foo';

cache['bpmnlint-plugin-exported/foo-absolute'] = rule_28;
Loading
Loading