Skip to content

Commit

Permalink
fix: filter duplicates in BpmnElementsRegistry APIs (#2922)
Browse files Browse the repository at this point in the history
Filter duplicates generated when passing duplicated ids or kinds to get elements from the model.

Closes #2921
  • Loading branch information
tbouffard authored Oct 12, 2023
1 parent 195ed9e commit 2b219eb
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 12 deletions.
8 changes: 5 additions & 3 deletions src/component/registry/bpmn-elements-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class BpmnElementsRegistry implements CssClassesRegistry, ElementsRegistr
}

getModelElementsByIds(bpmnElementIds: string | string[]): BpmnSemantic[] {
return ensureIsArray<string>(bpmnElementIds)
return filterDuplicates<string>(bpmnElementIds)
.map(id => this.bpmnModelRegistry.getBpmnSemantic(id))
.filter(Boolean);
}
Expand All @@ -90,13 +90,13 @@ export class BpmnElementsRegistry implements CssClassesRegistry, ElementsRegistr
}

getModelElementsByKinds(bpmnKinds: BpmnElementKind | BpmnElementKind[]): BpmnSemantic[] {
return ensureIsArray<BpmnElementKind>(bpmnKinds)
return filterDuplicates<BpmnElementKind>(bpmnKinds)
.flatMap(kind => this.htmlElementRegistry.getBpmnHtmlElements(kind))
.map(htmlElement => this.getRelatedBpmnSemantic(htmlElement));
}

getElementsByKinds(bpmnKinds: BpmnElementKind | BpmnElementKind[]): BpmnElement[] {
return ensureIsArray<BpmnElementKind>(bpmnKinds)
return filterDuplicates<BpmnElementKind>(bpmnKinds)
.flatMap(kind => this.htmlElementRegistry.getBpmnHtmlElements(kind))
.map(htmlElement => ({ htmlElement, bpmnSemantic: this.getRelatedBpmnSemantic(htmlElement) }));
}
Expand Down Expand Up @@ -157,3 +157,5 @@ class HtmlElementRegistry {
return [...this.container.querySelectorAll<HTMLElement>(selectors)];
}
}

const filterDuplicates = <T>(ids: T | T[]): T[] => [...new Set(ensureIsArray<T>(ids))];
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<bpmn:flowNodeRef>Event_1q818hp</bpmn:flowNodeRef>
</bpmn:lane>
<bpmn:lane id="lane_02" name="Lane 2">
<bpmn:flowNodeRef>Gateway_1hq21li</bpmn:flowNodeRef>
<bpmn:flowNodeRef>gateway_02_parallel</bpmn:flowNodeRef>
<bpmn:flowNodeRef>userTask_2_2</bpmn:flowNodeRef>
<bpmn:flowNodeRef>serviceTask_2_1</bpmn:flowNodeRef>
<bpmn:flowNodeRef>endEvent_message_1</bpmn:flowNodeRef>
Expand Down Expand Up @@ -44,7 +44,7 @@
<bpmn:incoming>Flow_0ule9dn</bpmn:incoming>
<bpmn:outgoing>Flow_1ceafgv</bpmn:outgoing>
</bpmn:userTask>
<bpmn:parallelGateway id="Gateway_1hq21li" name="gateway 2">
<bpmn:parallelGateway id="gateway_02_parallel" name="gateway 2">
<bpmn:incoming>Flow_0sqwsrw</bpmn:incoming>
<bpmn:incoming>Flow_0g017tm</bpmn:incoming>
<bpmn:outgoing>Flow_09zytr1</bpmn:outgoing>
Expand All @@ -66,9 +66,9 @@
<bpmn:sequenceFlow id="Flow_1ceafgv" sourceRef="Activity_1s8cug0" targetRef="serviceTask_1_2" />
<bpmn:sequenceFlow id="sequenceFlow_lane_1_elt_6" sourceRef="serviceTask_1_2" targetRef="endEvent_terminate_1" />
<bpmn:sequenceFlow id="Flow_1hvyo7b" sourceRef="serviceTask_2_1" targetRef="userTask_2_2" />
<bpmn:sequenceFlow id="Flow_09zytr1" sourceRef="Gateway_1hq21li" targetRef="endEvent_message_1" />
<bpmn:sequenceFlow id="Flow_09zytr1" sourceRef="gateway_02_parallel" targetRef="endEvent_message_1" />
<bpmn:sequenceFlow id="Flow_1noi0ay" sourceRef="task_1" targetRef="gateway_01" />
<bpmn:sequenceFlow id="Flow_0sqwsrw" sourceRef="userTask_2_2" targetRef="Gateway_1hq21li" />
<bpmn:sequenceFlow id="Flow_0sqwsrw" sourceRef="userTask_2_2" targetRef="gateway_02_parallel" />
<bpmn:task id="task_1" name="Task 1">
<bpmn:incoming>sequenceFlow_lane_1_elt_2</bpmn:incoming>
<bpmn:outgoing>Flow_1noi0ay</bpmn:outgoing>
Expand Down Expand Up @@ -98,7 +98,7 @@
<bpmn:outgoing>sequenceFlow_lane_3_elt_3</bpmn:outgoing>
<bpmn:messageEventDefinition id="MessageEventDefinition_077j2qu" />
</bpmn:intermediateCatchEvent>
<bpmn:sequenceFlow id="Flow_0g017tm" sourceRef="Event_1q818hp" targetRef="Gateway_1hq21li" />
<bpmn:sequenceFlow id="Flow_0g017tm" sourceRef="Event_1q818hp" targetRef="gateway_02_parallel" />
<bpmn:intermediateThrowEvent id="Event_1q818hp" name="message throw intermediate 1">
<bpmn:incoming>sequenceFlow_lane_3_elt_3</bpmn:incoming>
<bpmn:outgoing>Flow_0g017tm</bpmn:outgoing>
Expand Down Expand Up @@ -203,7 +203,7 @@
<bpmndi:BPMNShape id="Activity_0tufvfp_di" bpmnElement="Activity_1s8cug0">
<dc:Bounds x="790" y="70" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0m423t9_di" bpmnElement="Gateway_1hq21li">
<bpmndi:BPMNShape id="gateway_02_parallel_di" bpmnElement="gateway_02_parallel">
<dc:Bounds x="955" y="245" width="50" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="954" y="223" width="53" height="14" />
Expand Down
31 changes: 31 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,7 @@ import {
} from './helpers/bpmn-visualization-initialization';
import {
expectEndEventBpmnElement,
expectParallelGatewayBpmnElement,
expectPoolBpmnElement,
expectSequenceFlowBpmnElement,
expectServiceTaskBpmnElement,
Expand Down Expand Up @@ -72,6 +73,12 @@ describe('Bpmn Elements registry - retrieve BPMN elements', () => {
const bpmnElements = bpmnVisualization.bpmnElementsRegistry.getElementsByIds(['StartEvent_1', 'unknown', 'Flow_1', 'another_unknown']);
expect(bpmnElements).toHaveLength(2);
});

it('Pass duplicated ids', () => {
bpmnVisualization.load(readFileSync('../fixtures/bpmn/simple-start-task-end.bpmn'));
const bpmnElements = bpmnVisualization.bpmnElementsRegistry.getElementsByIds(['StartEvent_1', 'Flow_1', 'StartEvent_1', 'Flow_1', 'Flow_1']);
expect(bpmnElements).toHaveLength(2);
});
});

describe('Get by kinds', () => {
Expand Down Expand Up @@ -140,6 +147,30 @@ describe('Bpmn Elements registry - retrieve BPMN elements', () => {
parentId: 'lane_02',
});
});

it('Pass duplicated kinds', () => {
bpmnVisualization.load(readFileSync('../fixtures/bpmn/registry/1-pool-3-lanes-message-start-end-intermediate-events.bpmn'));
const bpmnElements = bpmnVisualization.bpmnElementsRegistry.getElementsByKinds([
ShapeBpmnElementKind.TASK,
ShapeBpmnElementKind.GATEWAY_PARALLEL,
ShapeBpmnElementKind.GATEWAY_PARALLEL,
ShapeBpmnElementKind.TASK,
]);
expect(bpmnElements).toHaveLength(2);

expectTaskBpmnElement(bpmnElements[0], {
id: 'task_1',
incoming: ['sequenceFlow_lane_1_elt_2'],
name: 'Task 1',
outgoing: ['Flow_1noi0ay'],
parentId: 'lane_01',
});
expectParallelGatewayBpmnElement(bpmnElements[1], {
id: 'gateway_02_parallel',
name: 'gateway 2',
parentId: 'lane_02',
});
});
});
});
});
9 changes: 7 additions & 2 deletions test/integration/helpers/semantic-with-svg-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ 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 { expectSvgEvent, expectSvgPool, expectSvgSequenceFlow, expectSvgTask } from './html-utils';
import { expectSvgEvent, expectSvgGateway, expectSvgPool, expectSvgSequenceFlow, expectSvgTask } from './html-utils';

import { expectEndEvent, expectPool, expectSequenceFlow, expectServiceTask, expectStartEvent, expectTask } from '@test/shared/model/bpmn-semantic-utils';
import { expectEndEvent, 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 @@ -46,6 +46,11 @@ export function expectServiceTaskBpmnElement(bpmnElement: BpmnElement, expected:
expectSvgTask(bpmnElement.htmlElement);
}

export function expectParallelGatewayBpmnElement(bpmnElement: BpmnElement, expected: ExpectedFlowNodeElement): void {
expectParallelGateway(bpmnElement.bpmnSemantic as ShapeBpmnSemantic, expected);
expectSvgGateway(bpmnElement.htmlElement);
}

export function expectPoolBpmnElement(bpmnElement: BpmnElement, expected: ExpectedBaseBpmnElement): void {
expectPool(bpmnElement.bpmnSemantic, expected);
expectSvgPool(bpmnElement.htmlElement);
Expand Down
56 changes: 55 additions & 1 deletion test/integration/model.elements.api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ import {
expectBoundaryEvent,
expectCallActivity,
expectEndEvent,
expectIntermediateCatchEvent,
expectParallelGateway,
expectSequenceFlow,
expectStartEvent,
expectSubprocess,
expectTask,
expectUserTask,
} from '@test/shared/model/bpmn-semantic-utils';

Expand Down Expand Up @@ -150,6 +152,31 @@ describe('Registry API - retrieve Model Bpmn elements', () => {
parentId: 'send_task_id',
});
});

test('Pass duplicated ids', () => {
const modelElements = bpmnElementsRegistry.getModelElementsByIds([
'start_event_none_id',
'conditional_sequence_flow_from_gateway_id',
'conditional_sequence_flow_from_gateway_id',
'start_event_none_id',
'conditional_sequence_flow_from_gateway_id',
]);

expect(modelElements).toHaveLength(2);

expectStartEvent(modelElements[0] as ShapeBpmnSemantic, {
eventDefinitionKind: ShapeBpmnEventDefinitionKind.NONE,
id: 'start_event_none_id',
name: 'None Start Event',
parentId: 'participant_1_id',
});
expectSequenceFlow(modelElements[1] as EdgeBpmnSemantic, {
id: 'conditional_sequence_flow_from_gateway_id',
name: '',
source: 'gateway_with_flows_id',
target: 'task_with_flows_id',
});
});
});

describe('Get by ids - diagram including elements without pool', () => {
Expand Down Expand Up @@ -232,7 +259,7 @@ describe('Registry API - retrieve Model Bpmn elements', () => {
eventDefinitionKind: ShapeBpmnEventDefinitionKind.MESSAGE,
incoming: ['Flow_09zytr1'],
});
expectParallelGateway(modelElements[2] as ShapeBpmnSemantic, { id: 'Gateway_1hq21li', name: 'gateway 2', parentId: 'lane_02' });
expectParallelGateway(modelElements[2] as ShapeBpmnSemantic, { id: 'gateway_02_parallel', name: 'gateway 2', parentId: 'lane_02' });
expectSequenceFlow(modelElements[3] as EdgeBpmnSemantic, { id: 'Flow_1noi0ay', source: 'task_1', target: 'gateway_01' });
// all remaining are sequence flows
expectAllElementsWithKind(modelElements.slice(3), FlowKind.SEQUENCE_FLOW);
Expand All @@ -241,6 +268,33 @@ describe('Registry API - retrieve Model Bpmn elements', () => {
test.each([null, undefined])('Pass nullish parameter: %s', (nullishResetParameter: BpmnElementKind) => {
expect(bpmnElementsRegistry.getModelElementsByKinds(nullishResetParameter)).toHaveLength(0);
});

test('Pass duplicated kinds', () => {
const modelElements = bpmnElementsRegistry.getModelElementsByKinds([
ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH,
ShapeBpmnElementKind.TASK,
ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH,
ShapeBpmnElementKind.TASK,
]);

expect(modelElements).toHaveLength(2);

expectIntermediateCatchEvent(modelElements[0] as ShapeBpmnSemantic, {
eventDefinitionKind: ShapeBpmnEventDefinitionKind.MESSAGE,
id: 'Event_1wihmdr',
incoming: ['Flow_08z7uoy'],
name: 'message catch intermediate 1',
outgoing: ['sequenceFlow_lane_3_elt_3'],
parentId: 'lane_03',
});
expectTask(modelElements[1] as ShapeBpmnSemantic, {
id: 'task_1',
incoming: ['sequenceFlow_lane_1_elt_2'],
name: 'Task 1',
outgoing: ['Flow_1noi0ay'],
parentId: 'lane_01',
});
});
});
});

Expand Down
5 changes: 5 additions & 0 deletions test/shared/model/bpmn-semantic-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ export function expectBoundaryEvent(bpmnSemantic: ShapeBpmnSemantic, expected: E
expectEvent(bpmnSemantic, expected);
}

export function expectIntermediateCatchEvent(bpmnSemantic: ShapeBpmnSemantic, expected: ExpectedEventElement): void {
expect(bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.EVENT_INTERMEDIATE_CATCH);
expectEvent(bpmnSemantic, expected);
}

export function expectParallelGateway(bpmnSemantic: BpmnSemantic, expected: ExpectedFlowNodeElement): void {
expectShape(bpmnSemantic, expected);
expect(bpmnSemantic.kind).toEqual(ShapeBpmnElementKind.GATEWAY_PARALLEL);
Expand Down

0 comments on commit 2b219eb

Please sign in to comment.