feat(modeling): allow to attach events with event definition

This feature is allowed only for events which have their boundary
counteparts, i.e. intermediate throw, message catch, timer catch,
signal catch and conditional catch events.
This commit is contained in:
Maciej Barelkowski 2019-05-22 10:42:07 +02:00 committed by merge-me[bot]
parent 1e9aceecd7
commit 33f9e7be6e
5 changed files with 281 additions and 54 deletions

View File

@ -2,13 +2,14 @@ import inherits from 'inherits';
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
import { is } from '../../../util/ModelUtil';
import { isAny } from '../util/ModelingUtil';
import { getBusinessObject } from '../../../util/ModelUtil';
/**
* BPMN specific attach event behavior
*/
export default function AttachEventBehavior(eventBus, bpmnFactory, bpmnReplace) {
export default function AttachEventBehavior(eventBus, bpmnReplace) {
CommandInterceptor.call(this, eventBus);
@ -21,9 +22,9 @@ export default function AttachEventBehavior(eventBus, bpmnFactory, bpmnReplace)
var shapes = context.shapes,
host = context.newHost,
shape,
newShape,
businessObject,
boundaryEvent;
eventDefinition,
boundaryEvent,
newShape;
if (shapes.length !== 1) {
return;
@ -31,20 +32,18 @@ export default function AttachEventBehavior(eventBus, bpmnFactory, bpmnReplace)
shape = shapes[0];
var attrs = {
cancelActivity: true
};
if (host && isAny(shape, [ 'bpmn:IntermediateThrowEvent', 'bpmn:IntermediateCatchEvent' ])) {
if (host && is(shape, 'bpmn:IntermediateThrowEvent')) {
attrs.attachedToRef = host.businessObject;
businessObject = bpmnFactory.create('bpmn:BoundaryEvent', attrs);
eventDefinition = getEventDefinition(shape);
boundaryEvent = {
type: 'bpmn:BoundaryEvent',
businessObject: businessObject
type: 'bpmn:BoundaryEvent'
};
if (eventDefinition) {
boundaryEvent.eventDefinitionType = eventDefinition.$type;
}
newShape = bpmnReplace.replaceElement(shape, boundaryEvent);
context.shapes = [ newShape ];
@ -54,8 +53,16 @@ export default function AttachEventBehavior(eventBus, bpmnFactory, bpmnReplace)
AttachEventBehavior.$inject = [
'eventBus',
'bpmnFactory',
'bpmnReplace'
];
inherits(AttachEventBehavior, CommandInterceptor);
// helper /////
function getEventDefinition(element) {
var bo = getBusinessObject(element);
return bo && bo.eventDefinitions && bo.eventDefinitions[0];
}

View File

@ -564,7 +564,18 @@ function isLane(element) {
* this must be reflected in the rules.
*/
function isBoundaryCandidate(element) {
return isBoundaryEvent(element) || is(element, 'bpmn:IntermediateThrowEvent');
if (isBoundaryEvent(element)) {
return true;
}
if (is(element, 'bpmn:IntermediateThrowEvent') && hasNoEventDefinition(element)) {
return true;
}
return (
is(element, 'bpmn:IntermediateCatchEvent') &&
hasCommonBoundaryIntermediateEventDefinition(element)
);
}
function hasNoEventDefinition(element) {

View File

@ -11,49 +11,100 @@ describe('features/modeling/behavior - attach events', function() {
var testModules = [ coreModule, modelingModule ];
var processDiagramXML = require('../../../../fixtures/bpmn/boundary-events.bpmn');
var processDiagramXML = require('test/spec/features/rules/BpmnRules.attaching.bpmn');
beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules }));
it('should execute on attach', inject(function(elementRegistry, modeling) {
describe('basics', function() {
// given
var eventId = 'IntermediateThrowEvent_1',
intermediateThrowEvent = elementRegistry.get(eventId),
subProcess = elementRegistry.get('SubProcess_1'),
boundaryEvent;
it('should execute on attach', inject(function(elementRegistry, modeling) {
var elements = [ intermediateThrowEvent ];
// given
var eventId = 'IntermediateThrowEvent',
intermediateThrowEvent = elementRegistry.get(eventId),
subProcess = elementRegistry.get('SubProcess_1'),
boundaryEvent;
// when
modeling.moveElements(elements, { x: 60, y: -131 }, subProcess, { attach: true });
var elements = [ intermediateThrowEvent ];
// then
boundaryEvent = elementRegistry.get(eventId);
// when
modeling.moveElements(elements, { x: 0, y: -90 }, subProcess, { attach: true });
expect(intermediateThrowEvent.parent).to.not.exist;
expect(boundaryEvent).to.exist;
expect(boundaryEvent.type).to.equal('bpmn:BoundaryEvent');
expect(boundaryEvent.businessObject.attachedToRef).to.equal(subProcess.businessObject);
}));
// then
boundaryEvent = elementRegistry.get(eventId);
expect(intermediateThrowEvent.parent).to.not.exist;
expect(boundaryEvent).to.exist;
expect(boundaryEvent.type).to.equal('bpmn:BoundaryEvent');
expect(boundaryEvent.businessObject.attachedToRef).to.equal(subProcess.businessObject);
}));
it('should NOT execute on drop', inject(function(elementRegistry, modeling) {
it('should NOT execute on drop', inject(function(elementRegistry, modeling) {
// given
var eventId = 'IntermediateThrowEvent_1',
intermediateThrowEvent = elementRegistry.get(eventId),
subProcess = elementRegistry.get('SubProcess_1');
// given
var eventId = 'IntermediateThrowEvent',
intermediateThrowEvent = elementRegistry.get(eventId),
subProcess = elementRegistry.get('SubProcess_1');
var elements = [ intermediateThrowEvent ];
var elements = [ intermediateThrowEvent ];
// when
modeling.moveElements(elements, { x: 60, y: -191 }, subProcess);
// when
modeling.moveElements(elements, { x: 0, y: -150 }, subProcess);
// then
expect(intermediateThrowEvent.parent).to.eql(subProcess);
expect(intermediateThrowEvent.type).to.equal('bpmn:IntermediateThrowEvent');
}));
// then
expect(intermediateThrowEvent.parent).to.eql(subProcess);
expect(intermediateThrowEvent.type).to.equal('bpmn:IntermediateThrowEvent');
}));
});
describe('event definition', function() {
it('should copy event definitions', inject(function(elementRegistry, modeling) {
// given
var attachableEvents = [
'IntermediateThrowEvent',
'MessageCatchEvent',
'TimerCatchEvent',
'SignalCatchEvent',
'ConditionalCatchEvent'
];
attachableEvents.forEach(function(eventId) {
var event = elementRegistry.get(eventId),
subProcess = elementRegistry.get('SubProcess_1'),
eventDefinitions = event.businessObject.eventDefinitions,
boundaryEvent, bo;
var elements = [ event ];
// when
modeling.moveElements(elements, { x: 0, y: -90 }, subProcess, { attach: true });
// then
boundaryEvent = elementRegistry.get(eventId);
bo = boundaryEvent.businessObject;
expect(boundaryEvent.type).to.equal('bpmn:BoundaryEvent');
expect(bo.eventDefinitions).to.jsonEqual(eventDefinitions, skipId);
});
}));
});
});
// helper //////
function skipId(key, value) {
if (key === 'id') {
return;
}
return value;
}

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<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:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.1.0">
<bpmn:process id="Process_1" isExecutable="false">
<bpmn:subProcess id="SubProcess_1" name="SubProcess" />
<bpmn:intermediateCatchEvent id="MessageCatchEvent" name="2">
<bpmn:messageEventDefinition />
</bpmn:intermediateCatchEvent>
<bpmn:intermediateThrowEvent id="MessageThrowEvent" name="3">
<bpmn:messageEventDefinition />
</bpmn:intermediateThrowEvent>
<bpmn:intermediateThrowEvent id="IntermediateThrowEvent" name="1" />
<bpmn:intermediateCatchEvent id="TimerCatchEvent" name="4">
<bpmn:timerEventDefinition />
</bpmn:intermediateCatchEvent>
<bpmn:intermediateThrowEvent id="EscalationThrowEvent" name="5">
<bpmn:escalationEventDefinition />
</bpmn:intermediateThrowEvent>
<bpmn:intermediateCatchEvent id="ConditionalCatchEvent" name="6">
<bpmn:conditionalEventDefinition>
<bpmn:condition xsi:type="bpmn:tFormalExpression" />
</bpmn:conditionalEventDefinition>
</bpmn:intermediateCatchEvent>
<bpmn:intermediateCatchEvent id="LinkCatchEvent" name="7">
<bpmn:linkEventDefinition />
</bpmn:intermediateCatchEvent>
<bpmn:intermediateThrowEvent id="LinkThrowEvent" name="8">
<bpmn:linkEventDefinition />
</bpmn:intermediateThrowEvent>
<bpmn:intermediateThrowEvent id="CompensateThrowEvent" name="9">
<bpmn:compensateEventDefinition />
</bpmn:intermediateThrowEvent>
<bpmn:intermediateCatchEvent id="SignalCatchEvent" name="10">
<bpmn:signalEventDefinition />
</bpmn:intermediateCatchEvent>
<bpmn:intermediateThrowEvent id="SignalThrowEvent" name="11">
<bpmn:signalEventDefinition />
</bpmn:intermediateThrowEvent>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="SubProcess_1_di" bpmnElement="SubProcess_1" isExpanded="true">
<dc:Bounds x="156" y="30" width="660" height="200" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateCatchEvent_0ckelak_di" bpmnElement="MessageCatchEvent">
<dc:Bounds x="234" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="249" y="340" width="7" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateThrowEvent_0h76veu_di" bpmnElement="MessageThrowEvent">
<dc:Bounds x="281" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="296" y="340" width="7" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateThrowEvent_di" bpmnElement="IntermediateThrowEvent">
<dc:Bounds x="188" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="203" y="340" width="7" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateCatchEvent_1bkb8p2_di" bpmnElement="TimerCatchEvent">
<dc:Bounds x="343" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="358" y="340" width="7" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateThrowEvent_0fxof2e_di" bpmnElement="EscalationThrowEvent">
<dc:Bounds x="402" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="417" y="340" width="7" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateCatchEvent_0r6qqek_di" bpmnElement="ConditionalCatchEvent">
<dc:Bounds x="454" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="469" y="340" width="7" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateCatchEvent_179y1zf_di" bpmnElement="LinkCatchEvent">
<dc:Bounds x="505" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="520" y="340" width="7" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateThrowEvent_169wfo1_di" bpmnElement="LinkThrowEvent">
<dc:Bounds x="556" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="571" y="340" width="7" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateThrowEvent_1o8dtmg_di" bpmnElement="CompensateThrowEvent">
<dc:Bounds x="605" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="620" y="340" width="7" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateCatchEvent_0du6szv_di" bpmnElement="SignalCatchEvent">
<dc:Bounds x="654" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="666" y="340" width="13" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateThrowEvent_0sqptpn_di" bpmnElement="SignalThrowEvent">
<dc:Bounds x="701" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="713" y="340" width="12" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -1260,23 +1260,69 @@ describe('features/modeling/rules - BpmnRules', function() {
}
));
});
it('attach/move IntermediateThrowEvent -> SubProcess', inject(
function(elementRegistry) {
// given
var intermediateThrowEvent = elementRegistry.get('IntermediateThrowEvent_1');
describe('event attaching', function() {
var elements = [ intermediateThrowEvent ];
var testXML = require('./BpmnRules.attaching.bpmn');
// then
expectCanMove(elements, 'SubProcess_1', {
beforeEach(bootstrapModeler(testXML, { modules: testModules }));
it('should allow to attach attachable events to SubProcess', inject(function(elementRegistry) {
// given
var attachableEvents = [
'IntermediateThrowEvent',
'MessageCatchEvent',
'TimerCatchEvent',
'SignalCatchEvent',
'ConditionalCatchEvent'
];
var events = attachableEvents.map(function(eventId) {
return elementRegistry.get(eventId);
});
// then
events.forEach(function(event) {
expectCanMove([ event ], 'SubProcess_1', {
attach: 'attach',
move: true
});
}
));
});
}));
it('should not allow to attach non-attachable events to SubProcess',
inject(function(elementRegistry) {
// given
var nonAttachableEvents = [
'MessageThrowEvent',
'EscalationThrowEvent',
'LinkCatchEvent',
'LinkThrowEvent',
'CompensateThrowEvent',
'SignalThrowEvent'
];
var events = nonAttachableEvents.map(function(eventId) {
return elementRegistry.get(eventId);
});
// then
events.forEach(function(event) {
expectCanMove([ event ], 'SubProcess_1', {
attach: false,
move: true
});
});
})
);
});