Skip to content

Commit

Permalink
feat: add link event data in ShapeBpmnSemantic (#2911)
Browse files Browse the repository at this point in the history
Add new `linkEventSourceIds` an `linkEventTargetId` properties in
`ShapeBpmnSemantic` to include data related to the link events that are
stored in the internal model.

---------

Co-authored-by: Thomas Bouffard <[email protected]>
  • Loading branch information
csouchet and tbouffard authored Oct 13, 2023
1 parent 85c8b20 commit 3d52258
Show file tree
Hide file tree
Showing 13 changed files with 328 additions and 30 deletions.
13 changes: 11 additions & 2 deletions src/component/registry/bpmn-model-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@ import type { Edge } from '../../model/bpmn/internal/edge/edge';
import type Shape from '../../model/bpmn/internal/shape/Shape';
import type { ModelFilter } from '../options';

import { ShapeBpmnMarkerKind, ShapeUtil } from '../../model/bpmn/internal';
import { ShapeBpmnEventDefinitionKind, ShapeBpmnMarkerKind, ShapeUtil } from '../../model/bpmn/internal';
import { Flow } from '../../model/bpmn/internal/edge/flows';
import ShapeBpmnElement, { ShapeBpmnCallActivity, ShapeBpmnEvent, ShapeBpmnSubProcess } from '../../model/bpmn/internal/shape/ShapeBpmnElement';
import ShapeBpmnElement, {
ShapeBpmnCallActivity,
ShapeBpmnEvent,
ShapeBpmnIntermediateCatchEvent,
ShapeBpmnIntermediateThrowEvent,
ShapeBpmnSubProcess,
} from '../../model/bpmn/internal/shape/ShapeBpmnElement';

import { ModelFiltering } from './bpmn-model-filters';

Expand Down Expand Up @@ -68,6 +74,9 @@ export class BpmnModelRegistry {
callActivityGlobalTaskKind: bpmnElement instanceof ShapeBpmnCallActivity ? bpmnElement.globalTaskKind : undefined,
callActivityKind: bpmnElement instanceof ShapeBpmnCallActivity ? bpmnElement.callActivityKind : undefined,
eventDefinitionKind: bpmnElement instanceof ShapeBpmnEvent ? bpmnElement.eventDefinitionKind : undefined,
linkEventSourceIds:
bpmnElement instanceof ShapeBpmnIntermediateCatchEvent && bpmnElement.eventDefinitionKind == ShapeBpmnEventDefinitionKind.LINK ? bpmnElement.sourceIds : undefined,
linkEventTargetId: bpmnElement instanceof ShapeBpmnIntermediateThrowEvent ? bpmnElement.targetId : undefined,
incomingIds: bpmnElement.incomingIds,
outgoingIds: bpmnElement.outgoingIds,
parentId: bpmnElement.parentId,
Expand Down
8 changes: 8 additions & 0 deletions src/component/registry/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,13 +370,21 @@ export interface EdgeBpmnSemantic extends BaseBpmnSemantic {
* @category Custom Behavior
*/
export interface ShapeBpmnSemantic extends BaseBpmnSemantic {
// IMPORTANT: keep properties in alphabetical order for consistency

/** Set when the {@link BaseBpmnSemantic.kind} relates to a BPMN Call Activity calling a global task. */
callActivityGlobalTaskKind?: GlobalTaskKind;
/** Set when the {@link BaseBpmnSemantic.kind} relates to a BPMN Call Activity. */
callActivityKind?: ShapeBpmnCallActivityKind;
/** Set when the {@link BaseBpmnSemantic.kind} relates to a BPMN event. */
eventDefinitionKind?: ShapeBpmnEventDefinitionKind;
/** IDs of the incoming flows/edges. */
incomingIds: string[];
/** Set when the {@link BaseBpmnSemantic.kind} relates to a BPMN intermediate catch event with {@link ShapeBpmnSemantic.eventDefinitionKind} set to {@link ShapeBpmnEventDefinitionKind.LINK}. */
linkEventSourceIds?: string[];
/** Set when the {@link BaseBpmnSemantic.kind} relates to a BPMN intermediate throw event with {@link ShapeBpmnSemantic.eventDefinitionKind} set to {@link ShapeBpmnEventDefinitionKind.LINK}. */
linkEventTargetId?: string;
/** IDs of the outgoing flows/edges. */
outgoingIds: string[];
/**
* This is the ID of the direct parent of the current element, which can be a:
Expand Down
11 changes: 8 additions & 3 deletions test/fixtures/bpmn/model-complete-semantic.bpmn
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@
<semantic:timerEventDefinition id="top_timer_definition_id" />
<semantic:signalEventDefinition id="top_signal_definition_id" />
<semantic:terminateEventDefinition id="top_terminate_definition_id" />
<semantic:linkEventDefinition id="top_link_definition_id" />
<semantic:linkEventDefinition id="top_throw_link_definition_id">
<semantic:target>top_catch_link_definition_id</semantic:target>
</semantic:linkEventDefinition>
<semantic:linkEventDefinition id="top_catch_link_definition_id">
<semantic:source>top_throw_link_definition_id</semantic:source>
</semantic:linkEventDefinition>
<semantic:errorEventDefinition id="top_error_definition_id" />
<semantic:compensateEventDefinition id="top_compensate_definition_id" />
<semantic:cancelEventDefinition id="top_cancel_definition_id" />
Expand Down Expand Up @@ -635,7 +640,7 @@
<semantic:linkEventDefinition />
</semantic:intermediateThrowEvent>
<semantic:intermediateThrowEvent id="intermediate_throw_event_link_on_top_id" name="Throw Link Intermediate Event On Top">
<semantic:eventDefinitionRef>top_link_definition_id</semantic:eventDefinitionRef>
<semantic:eventDefinitionRef>top_throw_link_definition_id</semantic:eventDefinitionRef>
</semantic:intermediateThrowEvent>
<semantic:intermediateThrowEvent id="intermediate_throw_event_compensate_id" name="Throw Compensate Intermediate Event">
<semantic:compensateEventDefinition />
Expand Down Expand Up @@ -673,7 +678,7 @@
<semantic:linkEventDefinition />
</semantic:intermediateCatchEvent>
<semantic:intermediateCatchEvent id="intermediate_catch_event_link_on_top_id" name="Catch Link Intermediate Event On Top">
<semantic:eventDefinitionRef>top_link_definition_id</semantic:eventDefinitionRef>
<semantic:eventDefinitionRef>top_catch_link_definition_id</semantic:eventDefinitionRef>
</semantic:intermediateCatchEvent>
<semantic:intermediateCatchEvent id="intermediate_catch_event_conditional_id" name="Catch Conditional Intermediate Event">
<semantic:conditionalEventDefinition />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_0jsncq9" targetNamespace="http://example.bpmn.com/schema/bpmn" exporter="bpmn-js (https://demo.bpmn.io)" exporterVersion="14.0.0">
<bpmn:collaboration id="Collaboration_03068dc">
<bpmn:participant id="Participant_1" name="Pool 1" processRef="Process_0vbjbkf" />
</bpmn:collaboration>
<bpmn:process id="Process_0vbjbkf" isExecutable="false">
<bpmn:laneSet id="LaneSet_1e2wb07">
<bpmn:lane id="lane_01" name="Lane 1">
<bpmn:flowNodeRef>Event_1q818hp</bpmn:flowNodeRef>
<bpmn:flowNodeRef>startEvent_lane_1</bpmn:flowNodeRef>
</bpmn:lane>
<bpmn:lane id="lane_02" name="Lane 2">
<bpmn:flowNodeRef>Event_1wihmdr</bpmn:flowNodeRef>
<bpmn:flowNodeRef>Event_0snpz9d</bpmn:flowNodeRef>
</bpmn:lane>
</bpmn:laneSet>
<bpmn:sequenceFlow id="sequenceFlow_lane_1_elt_1" sourceRef="startEvent_lane_1" targetRef="Event_1q818hp" />
<bpmn:intermediateThrowEvent id="Event_1q818hp" name="link throw intermediate">
<bpmn:incoming>sequenceFlow_lane_1_elt_1</bpmn:incoming>
<bpmn:linkEventDefinition id="LinkEventDefinition_11kxj5k">
<bpmn:target>LinkEventDefinition_077j2qu</bpmn:target>
</bpmn:linkEventDefinition>
</bpmn:intermediateThrowEvent>
<bpmn:startEvent id="startEvent_lane_1" name="start 1">
<bpmn:outgoing>sequenceFlow_lane_1_elt_1</bpmn:outgoing>
<bpmn:messageEventDefinition id="MessageEventDefinition_17xfjtr" />
</bpmn:startEvent>
<bpmn:intermediateCatchEvent id="Event_1wihmdr" name="link catch intermediate">
<bpmn:outgoing>Flow_18jrbeb</bpmn:outgoing>
<bpmn:linkEventDefinition id="LinkEventDefinition_077j2qu">
<bpmn:source>LinkEventDefinition_11kxj5k</bpmn:source>
</bpmn:linkEventDefinition>
</bpmn:intermediateCatchEvent>
<bpmn:endEvent id="Event_0snpz9d">
<bpmn:incoming>Flow_18jrbeb</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_18jrbeb" sourceRef="Event_1wihmdr" targetRef="Event_0snpz9d" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_03068dc">
<bpmndi:BPMNShape id="Participant_1_di" bpmnElement="Participant_1" isHorizontal="true">
<dc:Bounds x="158" y="50" width="682" height="240" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_01_di" bpmnElement="lane_01" isHorizontal="true">
<dc:Bounds x="188" y="50" width="652" height="125" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_02_di" bpmnElement="lane_02" isHorizontal="true">
<dc:Bounds x="188" y="175" width="652" height="115" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_14o73vn_di" bpmnElement="Event_1q818hp">
<dc:Bounds x="442" y="92" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="431" y="135" width="61" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="startEvent_lane_1_di" bpmnElement="startEvent_lane_1">
<dc:Bounds x="262" y="92" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="265" y="135" width="31" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_00seehq_di" bpmnElement="Event_1wihmdr">
<dc:Bounds x="442" y="202" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="430" y="245" width="61" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0snpz9d_di" bpmnElement="Event_0snpz9d">
<dc:Bounds x="622" y="202" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="sequenceFlow_lane_1_elt_1_di" bpmnElement="sequenceFlow_lane_1_elt_1">
<di:waypoint x="298" y="110" />
<di:waypoint x="442" y="110" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_18jrbeb_di" bpmnElement="Flow_18jrbeb">
<di:waypoint x="478" y="220" />
<di:waypoint x="622" y="220" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
26 changes: 26 additions & 0 deletions test/integration/dom.bpmn.elements.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
} from './helpers/bpmn-visualization-initialization';
import {
expectEndEventBpmnElement,
expectIntermediateCatchEventBpmnElement,
expectIntermediateThrowEventBpmnElement,
expectParallelGatewayBpmnElement,
expectPoolBpmnElement,
expectSequenceFlowBpmnElement,
Expand Down Expand Up @@ -62,6 +64,30 @@ describe('Bpmn Elements registry - retrieve BPMN elements', () => {
expectSequenceFlowBpmnElement(bpmnElements[1], { id: 'Flow_2', source: 'Activity_1', target: 'EndEvent_1' });
});

it('Pass existing link intermediate event ids', () => {
bpmnVisualization.load(readFileSync('../fixtures/bpmn/registry/1-pool-2-lanes-link-intermediate-events.bpmn'));
const bpmnElements = bpmnVisualization.bpmnElementsRegistry.getElementsByIds(['Event_1wihmdr', 'Event_1q818hp']);
expect(bpmnElements).toHaveLength(2);

expectIntermediateCatchEventBpmnElement(bpmnElements[0], {
id: 'Event_1wihmdr',
name: 'link catch intermediate',
parentId: 'lane_02',
eventDefinitionKind: ShapeBpmnEventDefinitionKind.LINK,
outgoing: ['Flow_18jrbeb'],
linkEventSourceIds: ['Event_1q818hp'],
});

expectIntermediateThrowEventBpmnElement(bpmnElements[1], {
id: 'Event_1q818hp',
name: 'link throw intermediate',
parentId: 'lane_01',
eventDefinitionKind: ShapeBpmnEventDefinitionKind.LINK,
incoming: ['sequenceFlow_lane_1_elt_1'],
linkEventTargetId: 'Event_1wihmdr',
});
});

it('Pass a single non existing id', () => {
bpmnVisualization.load(readFileSync('../fixtures/bpmn/simple-start-task-end.bpmn'));
const bpmnElements = bpmnVisualization.bpmnElementsRegistry.getElementsByIds('unknown');
Expand Down
23 changes: 14 additions & 9 deletions test/integration/helpers/model-expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ declare global {
toBeBusinessRuleTask(modelElement: ExpectedShapeModelElement): R;
toBeStartEvent(modelElement: ExpectedStartEventModelElement): R;
toBeEndEvent(modelElement: ExpectedEventModelElement): R;
toBeIntermediateThrowEvent(modelElement: ExpectedEventModelElement): R;
toBeIntermediateCatchEvent(modelElement: ExpectedEventModelElement): R;
toBeIntermediateThrowEvent(modelElement: ExpectedIntermediateThrowEventModelElement): R;
toBeIntermediateCatchEvent(modelElement: ExpectedIntermediateCatchEventModelElement): R;
toBeBoundaryEvent(modelElement: ExpectedBoundaryEventModelElement): R;
toBeEventBasedGateway(modelElement: ExpectedEventBasedGatewayModelElement): R;
toBeExclusiveGateway(modelElement: ExpectedShapeModelElement): R;
Expand Down Expand Up @@ -200,6 +200,18 @@ export type ExpectedShapeModelElement = {
export type ExpectedEventModelElement = {
eventDefinitionKind: ShapeBpmnEventDefinitionKind;
} & ExpectedShapeModelElement;
export type ExpectedIntermediateCatchEventModelElement = {
sourceIds?: string[];
} & ExpectedEventModelElement;
export type ExpectedIntermediateThrowEventModelElement = {
targetId?: string;
} & ExpectedEventModelElement;
export type ExpectedBoundaryEventModelElement = {
isInterrupting?: boolean;
} & ExpectedEventModelElement;
export type ExpectedStartEventModelElement = {
isInterrupting?: boolean;
} & ExpectedEventModelElement;

export type ExpectedSubProcessModelElement = {
subProcessKind: ShapeBpmnSubProcessKind;
Expand All @@ -220,13 +232,6 @@ export type ExpectedSequenceFlowModelElement = {
sequenceFlowKind?: SequenceFlowKind;
} & ExpectedEdgeModelElement;

export type ExpectedBoundaryEventModelElement = {
isInterrupting?: boolean;
} & ExpectedEventModelElement;
export type ExpectedStartEventModelElement = {
isInterrupting?: boolean;
} & ExpectedEventModelElement;

export type ExpectedEventBasedGatewayModelElement = {
gatewayKind?: ShapeBpmnEventBasedGatewayKind;
} & ExpectedShapeModelElement;
Expand Down
31 changes: 29 additions & 2 deletions test/integration/helpers/semantic-with-svg-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,28 @@ limitations under the License.
*/

import type { BpmnElement, EdgeBpmnSemantic, ShapeBpmnSemantic } from '@lib/component/registry';
import type { ExpectedBaseBpmnElement, ExpectedFlowElement, ExpectedFlowNodeElement, ExpectedEventElement } from '@test/shared/model/bpmn-semantic-utils';
import type {
ExpectedBaseBpmnElement,
ExpectedFlowElement,
ExpectedFlowNodeElement,
ExpectedEventElement,
ExpectedIntermediateThrowEventElement,
ExpectedIntermediateCatchEventElement,
} from '@test/shared/model/bpmn-semantic-utils';

import { expectSvgEvent, expectSvgGateway, expectSvgPool, expectSvgSequenceFlow, expectSvgTask } from './html-utils';

import { expectEndEvent, expectParallelGateway, expectPool, expectSequenceFlow, expectServiceTask, expectStartEvent, expectTask } from '@test/shared/model/bpmn-semantic-utils';
import {
expectEndEvent,
expectIntermediateCatchEvent,
expectIntermediateThrowEvent,
expectParallelGateway,
expectPool,
expectSequenceFlow,
expectServiceTask,
expectStartEvent,
expectTask,
} from '@test/shared/model/bpmn-semantic-utils';

export function expectStartEventBpmnElement(bpmnElement: BpmnElement, expected: ExpectedEventElement): void {
expectStartEvent(bpmnElement.bpmnSemantic as ShapeBpmnSemantic, expected);
Expand All @@ -31,6 +48,16 @@ export function expectEndEventBpmnElement(bpmnElement: BpmnElement, expected: Ex
expectSvgEvent(bpmnElement.htmlElement);
}

export function expectIntermediateCatchEventBpmnElement(bpmnElement: BpmnElement, expected: ExpectedIntermediateCatchEventElement): void {
expectIntermediateCatchEvent(bpmnElement.bpmnSemantic as ShapeBpmnSemantic, expected);
expectSvgEvent(bpmnElement.htmlElement);
}

export function expectIntermediateThrowEventBpmnElement(bpmnElement: BpmnElement, expected: ExpectedIntermediateThrowEventElement): void {
expectIntermediateThrowEvent(bpmnElement.bpmnSemantic as ShapeBpmnSemantic, expected);
expectSvgEvent(bpmnElement.htmlElement);
}

export function expectSequenceFlowBpmnElement(bpmnElement: BpmnElement, expected: ExpectedFlowElement): void {
expectSequenceFlow(bpmnElement.bpmnSemantic as EdgeBpmnSemantic, expected);
expectSvgSequenceFlow(bpmnElement.htmlElement);
Expand Down
15 changes: 12 additions & 3 deletions test/integration/matchers/toBeShape/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import type {
ExpectedShapeModelElement,
ExpectedStartEventModelElement,
ExpectedSubProcessModelElement,
ExpectedIntermediateCatchEventModelElement,
ExpectedIntermediateThrowEventModelElement,
} from '../../helpers/model-expect';
import type { BpmnCellStyle, ExpectedCell } from '../matcher-utils';

Expand Down Expand Up @@ -97,7 +99,8 @@ function buildExpectedShapeStylePropertyRegexp(
expectedModel:
| ExpectedShapeModelElement
| ExpectedSubProcessModelElement
| ExpectedEventModelElement
| ExpectedIntermediateCatchEventModelElement
| ExpectedIntermediateThrowEventModelElement
| ExpectedStartEventModelElement
| ExpectedBoundaryEventModelElement
| ExpectedEventBasedGatewayModelElement
Expand Down Expand Up @@ -127,6 +130,12 @@ function buildExpectedShapeStylePropertyRegexp(
if ('gatewayKind' in expectedModel) {
expectedStyle = expectedStyle + `.*bpmn.gatewayKind=${expectedModel.gatewayKind}`;
}
if ('sourceIds' in expectedModel) {
expectedStyle = expectedStyle + `.*bpmn.linkEventSourceIds=${expectedModel.sourceIds}`;
}
if ('targetId' in expectedModel) {
expectedStyle = expectedStyle + `.*bpmn.linkEventTargetId=${expectedModel.targetId}`;
}
return expectedStyle + '.*';
}

Expand Down Expand Up @@ -221,11 +230,11 @@ export function toBeEndEvent(this: MatcherContext, received: string, expected: E
return buildEventMatcher('toBeEndEvent', this, received, { ...expected, kind: ShapeBpmnElementKind.EVENT_END });
}

export function toBeIntermediateThrowEvent(this: MatcherContext, received: string, expected: ExpectedEventModelElement): CustomMatcherResult {
export function toBeIntermediateThrowEvent(this: MatcherContext, received: string, expected: ExpectedIntermediateThrowEventModelElement): CustomMatcherResult {
return buildEventMatcher('toBeIntermediateThrowEvent', this, received, { ...expected, kind: ShapeBpmnElementKind.EVENT_INTERMEDIATE_THROW });
}

export function toBeIntermediateCatchEvent(this: MatcherContext, received: string, expected: ExpectedEventModelElement): CustomMatcherResult {
export function toBeIntermediateCatchEvent(this: MatcherContext, received: string, expected: ExpectedIntermediateCatchEventModelElement): CustomMatcherResult {
return buildEventMatcher('toBeIntermediateCatchEvent', this, received, { ...expected, kind: ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH });
}

Expand Down
Loading

0 comments on commit 3d52258

Please sign in to comment.