Skip to content

Commit dbba03d

Browse files
barmacnikku
authored andcommitted
feat: support bpmn:AdHocSubProcess as job worker
Related to camunda/tmp-camunda-modeler-adhoc-subprocess#1
1 parent d7c3ceb commit dbba03d

9 files changed

+586
-47
lines changed

src/provider/zeebe/ZeebePropertiesProvider.js

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { findIndex } from 'min-dash';
55
import {
66
ActiveElementsProps,
77
AdHocCompletionProps,
8+
AdHocSubProcessImplementationProps,
89
AssignmentDefinitionProps,
910
BusinessRuleImplementationProps,
1011
CalledDecisionProps,
@@ -36,7 +37,11 @@ import {
3637

3738
import { ExtensionPropertiesProps } from '../shared/ExtensionPropertiesProps';
3839

39-
import { isMessageEndEvent, isMessageThrowEvent } from './utils/ZeebeServiceTaskUtil';
40+
import {
41+
isMessageEndEvent,
42+
isMessageThrowEvent,
43+
isZeebeServiceTask
44+
} from './utils/ZeebeServiceTaskUtil';
4045

4146
/**
4247
* @typedef { import('@bpmn-io/properties-panel').EntryDefinition } Entry
@@ -49,10 +54,11 @@ const ZEEBE_GROUPS = [
4954
CalledDecisionGroup,
5055
ScriptImplementationGroup,
5156
ScriptGroup,
57+
OutputCollectionGroup,
58+
AdHocSubProcessImplementationGroup,
5259
UserTaskImplementationGroup,
5360
TaskDefinitionGroup,
5461
AssignmentDefinitionGroup,
55-
OutputCollectionGroup,
5662
ActiveElementsGroup,
5763
FormGroup,
5864
ConditionGroup,
@@ -90,10 +96,10 @@ export default class ZeebePropertiesProvider {
9096
updateTimerGroup(groups, element, this._injector);
9197
updateMultiInstanceGroup(groups, element);
9298
updateAdHocCompletionGroup(groups, element);
93-
updateOutputCollectionGroup(groups);
9499

95-
// (3) remove message group when not applicable
100+
// (3) remove groups when not applicable
96101
groups = removeMessageGroup(groups, element);
102+
groups = removeCompletionGroup(groups, element);
97103

98104
return groups;
99105
};
@@ -110,6 +116,20 @@ export default class ZeebePropertiesProvider {
110116
ZeebePropertiesProvider.$inject = [ 'propertiesPanel', 'injector' ];
111117

112118

119+
function AdHocSubProcessImplementationGroup(element, injector) {
120+
const translate = injector.get('translate');
121+
const group = {
122+
id: 'adHocSubProcessImplementation',
123+
label: translate('Implementation'),
124+
entries: [
125+
...AdHocSubProcessImplementationProps({ element })
126+
],
127+
component: Group
128+
};
129+
130+
return group.entries.length ? group : null;
131+
}
132+
113133
function CalledDecisionGroup(element, injector) {
114134
const translate = injector.get('translate');
115135
const group = {
@@ -498,24 +518,10 @@ function updateAdHocCompletionGroup(groups, element) {
498518
AdHocCompletionProps({ element })
499519
);
500520

501-
// reorder groups to move active elements before adHoc completion
502-
const activeElementsGroup = findGroup(groups, 'activeElements');
503-
if (activeElementsGroup) {
504-
reorderGroupsIfNecessary(groups, activeElementsGroup, adHocCompletionGroup);
505-
}
506-
}
507-
508-
// move output collection group above active elements
509-
function updateOutputCollectionGroup(groups) {
510-
const outputCollectionGroup = findGroup(groups, 'outputCollection');
511-
if (!outputCollectionGroup) {
512-
return;
513-
}
514-
515-
// reorder groups to move output collection before active elements
521+
// reorder groups to move adHoc completion after active elements
516522
const activeElementsGroup = findGroup(groups, 'activeElements');
517523
if (activeElementsGroup) {
518-
reorderGroupsIfNecessary(groups, outputCollectionGroup, activeElementsGroup);
524+
moveGroupAfter(groups, adHocCompletionGroup, activeElementsGroup);
519525
}
520526
}
521527

@@ -530,30 +536,20 @@ function removeMessageGroup(groups, element) {
530536
return groups;
531537
}
532538

539+
// remove completion group if AdHoc SubProcess is implemented as job worker
540+
function removeCompletionGroup(groups, element) {
541+
if (isZeebeServiceTask(element)) {
542+
return groups.filter(group => group.id !== 'adHocCompletion');
543+
}
533544

534-
// helper /////////////////////
535-
536-
function findGroup(groups, id) {
537-
return groups.find(g => g.id === id);
545+
return groups;
538546
}
539547

540-
/**
541-
* Put groups into the defined order if necessary.
542-
*
543-
* @param {Group[]} groups
544-
* @param {Group} firstGroup
545-
* @param {Group} secondGroup
546-
*/
547-
function reorderGroupsIfNecessary(groups, firstGroup, secondGroup) {
548-
const firstGroupIndex = groups.indexOf(firstGroup);
549-
const secondGroupIndex = groups.indexOf(secondGroup);
550548

551-
if (firstGroupIndex === -1 || secondGroupIndex === -1 || firstGroupIndex <= secondGroupIndex) {
552-
return;
553-
}
549+
// helper /////////////////////
554550

555-
groups[firstGroupIndex] = secondGroup;
556-
groups[secondGroupIndex] = firstGroup;
551+
function findGroup(groups, id) {
552+
return groups.find(g => g.id === id);
557553
}
558554

559555
/**
@@ -615,4 +611,15 @@ function indexEntriesById(entries) {
615611
index[entry.id] = idx;
616612
return index;
617613
}, {});
618-
}
614+
}
615+
616+
/**
617+
* Move a group after a reference group. Assumes that both groups are present.
618+
*/
619+
function moveGroupAfter(groups, groupToMove, referenceGroup) {
620+
const groupIndex = groups.indexOf(groupToMove);
621+
groups.splice(groupIndex, 1);
622+
623+
const referenceIndex = groups.indexOf(referenceGroup);
624+
groups.splice(referenceIndex + 1, 0, groupToMove);
625+
}

src/provider/zeebe/properties/ActiveElementsProps.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55

66
import { isFeelEntryEdited } from '@bpmn-io/properties-panel';
77

8+
import { isZeebeServiceTask } from '../utils/ZeebeServiceTaskUtil';
9+
810
import { useService } from '../../../hooks';
911

1012
import { BpmnFeelEntry } from '../../../entries/BpmnFeelEntry';
@@ -21,7 +23,7 @@ export function ActiveElementsProps(props) {
2123
element
2224
} = props;
2325

24-
if (!is(element, 'bpmn:AdHocSubProcess')) {
26+
if (!is(element, 'bpmn:AdHocSubProcess') || isZeebeServiceTask(element)) {
2527
return [];
2628
}
2729

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import {
2+
getBusinessObject
3+
} from 'bpmn-js/lib/util/ModelUtil';
4+
5+
import {
6+
is
7+
} from 'bpmn-js/lib/util/ModelUtil';
8+
9+
import { SelectEntry } from '@bpmn-io/properties-panel';
10+
11+
import {
12+
addExtensionElements,
13+
getExtensionElementsList,
14+
removeExtensionElements
15+
} from '../../../utils/ExtensionElementsUtil';
16+
17+
import {
18+
createElement
19+
} from '../../../utils/ElementUtil';
20+
21+
import { useService } from '../../../hooks';
22+
23+
export const BPMN_IMPLEMENTATION_OPTION = 'bpmn',
24+
JOB_WORKER_IMPLEMENTATION_OPTION = 'jobWorker';
25+
26+
27+
export function AdHocSubProcessImplementationProps(props) {
28+
const {
29+
element
30+
} = props;
31+
32+
if (!is(element, 'bpmn:AdHocSubProcess')) {
33+
return [];
34+
}
35+
36+
return [
37+
{
38+
id: 'adHocImplementation',
39+
component: AdHocImplementation,
40+
isEdited: () => isAdHocImplementationEdited(element)
41+
}
42+
];
43+
}
44+
45+
function AdHocImplementation(props) {
46+
const {
47+
element,
48+
id
49+
} = props;
50+
51+
const commandStack = useService('commandStack');
52+
const bpmnFactory = useService('bpmnFactory');
53+
const translate = useService('translate');
54+
55+
const getValue = () => {
56+
if (getTaskDefinition(element)) {
57+
return JOB_WORKER_IMPLEMENTATION_OPTION;
58+
}
59+
60+
return BPMN_IMPLEMENTATION_OPTION;
61+
};
62+
63+
/**
64+
* Change to and from job worker is done via task definition
65+
*/
66+
const setValue = (value) => {
67+
if (value === JOB_WORKER_IMPLEMENTATION_OPTION) {
68+
createTaskDefinition(element, bpmnFactory, commandStack);
69+
} else if (value === BPMN_IMPLEMENTATION_OPTION) {
70+
removeTaskDefinition(element, commandStack);
71+
}
72+
};
73+
74+
const getOptions = () => {
75+
76+
const options = [
77+
{ value: BPMN_IMPLEMENTATION_OPTION, label: translate('BPMN') },
78+
{ value: JOB_WORKER_IMPLEMENTATION_OPTION, label: translate('Job worker') }
79+
];
80+
81+
return options;
82+
};
83+
84+
return SelectEntry({
85+
element,
86+
id,
87+
label: translate('Implementation type'),
88+
getValue,
89+
setValue,
90+
getOptions
91+
});
92+
}
93+
94+
95+
// helper ///////////////////////
96+
function createTaskDefinition(element, bpmnFactory, commandStack) {
97+
const businessObject = getBusinessObject(element);
98+
99+
const taskDefinition = createElement(
100+
'zeebe:TaskDefinition',
101+
{},
102+
null,
103+
bpmnFactory
104+
);
105+
106+
addExtensionElements(element, businessObject, taskDefinition, bpmnFactory, commandStack);
107+
}
108+
109+
function removeTaskDefinition(element, commandStack) {
110+
const taskDefinition = getTaskDefinition(element);
111+
112+
if (taskDefinition) {
113+
removeExtensionElements(element, getBusinessObject(element), taskDefinition, commandStack);
114+
}
115+
}
116+
117+
function isAdHocImplementationEdited(element) {
118+
return !!getTaskDefinition(element);
119+
}
120+
121+
function getTaskDefinition(element) {
122+
const businessObject = getBusinessObject(element);
123+
return getExtensionElementsList(businessObject, 'zeebe:TaskDefinition')[0];
124+
}

src/provider/zeebe/properties/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export { ActiveElementsProps } from './ActiveElementsProps';
22
export { AdHocCompletionProps } from './AdHocCompletionProps';
3+
export { AdHocSubProcessImplementationProps } from './AdHocSubProcessImplementationProps';
34
export { AssignmentDefinitionProps } from './AssignmentDefinitionProps';
45
export { BusinessRuleImplementationProps } from './BusinessRuleImplementationProps';
56
export { CalledDecisionProps } from './CalledDecisionProps';

src/provider/zeebe/utils/ZeebeServiceTaskUtil.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import {
1515
getExtensionElementsList
1616
} from '../../../utils/ExtensionElementsUtil';
1717

18+
const OPTIONAL_JOB_WORKER_ELEMENTS = [
19+
'bpmn:AdHocSubProcess',
20+
'bpmn:BusinessRuleTask',
21+
'bpmn:ScriptTask'
22+
];
1823

1924
export function isZeebeServiceTask(element) {
2025
if (!is(element, 'zeebe:ZeebeServiceTask')) return false;
@@ -23,9 +28,8 @@ export function isZeebeServiceTask(element) {
2328
return !!getMessageEventDefinition(element);
2429
}
2530

26-
// BusinessRuleTask and ScriptTask are ServiceTasks only if they have a TaskDefinition
27-
// (ie. if the implementation is set to ==JobWorker)
28-
if (isAny(element, [ 'bpmn:BusinessRuleTask', 'bpmn:ScriptTask' ]) && !getTaskDefinition(element)) {
31+
// Elements which may optionally be implemented as job workers
32+
if (isAny(element, OPTIONAL_JOB_WORKER_ELEMENTS) && !getTaskDefinition(element)) {
2933
return false;
3034
}
3135

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<bpmn:definitions 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:zeebe="http://camunda.org/schema/zeebe/1.0" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.25.0">
3+
<bpmn:process id="Process_1" isExecutable="true">
4+
<bpmn:adHocSubProcess id="AdHocSubProcess_1">
5+
<bpmn:task id="Task_1" name="Task 1" />
6+
<bpmn:task id="Task_2" name="Task 2" />
7+
</bpmn:adHocSubProcess>
8+
<bpmn:adHocSubProcess id="AdHocSubProcess_2">
9+
<bpmn:extensionElements>
10+
<zeebe:taskDefinition type="adHocSubProcess" />
11+
</bpmn:extensionElements>
12+
<bpmn:task id="Task_3" name="Task 3" />
13+
</bpmn:adHocSubProcess>
14+
<bpmn:subProcess id="SubProcess_1">
15+
<bpmn:task id="Task_4" name="Task 4" />
16+
</bpmn:subProcess>
17+
</bpmn:process>
18+
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
19+
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
20+
<bpmndi:BPMNShape id="AdHocSubProcess_1_di" bpmnElement="AdHocSubProcess_1">
21+
<dc:Bounds x="100" y="100" width="350" height="200" />
22+
</bpmndi:BPMNShape>
23+
<bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1">
24+
<dc:Bounds x="130" y="150" width="100" height="80" />
25+
</bpmndi:BPMNShape>
26+
<bpmndi:BPMNShape id="Task_2_di" bpmnElement="Task_2">
27+
<dc:Bounds x="280" y="150" width="100" height="80" />
28+
</bpmndi:BPMNShape>
29+
<bpmndi:BPMNShape id="AdHocSubProcess_2_di" bpmnElement="AdHocSubProcess_2">
30+
<dc:Bounds x="100" y="350" width="350" height="200" />
31+
</bpmndi:BPMNShape>
32+
<bpmndi:BPMNShape id="Task_3_di" bpmnElement="Task_3">
33+
<dc:Bounds x="200" y="400" width="100" height="80" />
34+
</bpmndi:BPMNShape>
35+
<bpmndi:BPMNShape id="SubProcess_1_di" bpmnElement="SubProcess_1">
36+
<dc:Bounds x="500" y="100" width="350" height="200" />
37+
</bpmndi:BPMNShape>
38+
<bpmndi:BPMNShape id="Task_4_di" bpmnElement="Task_4">
39+
<dc:Bounds x="630" y="150" width="100" height="80" />
40+
</bpmndi:BPMNShape>
41+
</bpmndi:BPMNPlane>
42+
</bpmndi:BPMNDiagram>
43+
</bpmn:definitions>

0 commit comments

Comments
 (0)