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 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 * BPMN specific attach event behavior
*/ */
export default function AttachEventBehavior(eventBus, bpmnFactory, bpmnReplace) { export default function AttachEventBehavior(eventBus, bpmnReplace) {
CommandInterceptor.call(this, eventBus); CommandInterceptor.call(this, eventBus);
@ -21,9 +22,9 @@ export default function AttachEventBehavior(eventBus, bpmnFactory, bpmnReplace)
var shapes = context.shapes, var shapes = context.shapes,
host = context.newHost, host = context.newHost,
shape, shape,
newShape, eventDefinition,
businessObject, boundaryEvent,
boundaryEvent; newShape;
if (shapes.length !== 1) { if (shapes.length !== 1) {
return; return;
@ -31,20 +32,18 @@ export default function AttachEventBehavior(eventBus, bpmnFactory, bpmnReplace)
shape = shapes[0]; shape = shapes[0];
var attrs = { if (host && isAny(shape, [ 'bpmn:IntermediateThrowEvent', 'bpmn:IntermediateCatchEvent' ])) {
cancelActivity: true
};
if (host && is(shape, 'bpmn:IntermediateThrowEvent')) { eventDefinition = getEventDefinition(shape);
attrs.attachedToRef = host.businessObject;
businessObject = bpmnFactory.create('bpmn:BoundaryEvent', attrs);
boundaryEvent = { boundaryEvent = {
type: 'bpmn:BoundaryEvent', type: 'bpmn:BoundaryEvent'
businessObject: businessObject
}; };
if (eventDefinition) {
boundaryEvent.eventDefinitionType = eventDefinition.$type;
}
newShape = bpmnReplace.replaceElement(shape, boundaryEvent); newShape = bpmnReplace.replaceElement(shape, boundaryEvent);
context.shapes = [ newShape ]; context.shapes = [ newShape ];
@ -54,8 +53,16 @@ export default function AttachEventBehavior(eventBus, bpmnFactory, bpmnReplace)
AttachEventBehavior.$inject = [ AttachEventBehavior.$inject = [
'eventBus', 'eventBus',
'bpmnFactory',
'bpmnReplace' 'bpmnReplace'
]; ];
inherits(AttachEventBehavior, CommandInterceptor); 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. * this must be reflected in the rules.
*/ */
function isBoundaryCandidate(element) { 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) { function hasNoEventDefinition(element) {

View File

@ -11,49 +11,100 @@ describe('features/modeling/behavior - attach events', function() {
var testModules = [ coreModule, modelingModule ]; 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 })); beforeEach(bootstrapModeler(processDiagramXML, { modules: testModules }));
it('should execute on attach', inject(function(elementRegistry, modeling) { describe('basics', function() {
// given it('should execute on attach', inject(function(elementRegistry, modeling) {
var eventId = 'IntermediateThrowEvent_1',
intermediateThrowEvent = elementRegistry.get(eventId),
subProcess = elementRegistry.get('SubProcess_1'),
boundaryEvent;
var elements = [ intermediateThrowEvent ]; // given
var eventId = 'IntermediateThrowEvent',
intermediateThrowEvent = elementRegistry.get(eventId),
subProcess = elementRegistry.get('SubProcess_1'),
boundaryEvent;
// when var elements = [ intermediateThrowEvent ];
modeling.moveElements(elements, { x: 60, y: -131 }, subProcess, { attach: true });
// then // when
boundaryEvent = elementRegistry.get(eventId); modeling.moveElements(elements, { x: 0, y: -90 }, subProcess, { attach: true });
expect(intermediateThrowEvent.parent).to.not.exist; // then
expect(boundaryEvent).to.exist; boundaryEvent = elementRegistry.get(eventId);
expect(boundaryEvent.type).to.equal('bpmn:BoundaryEvent');
expect(boundaryEvent.businessObject.attachedToRef).to.equal(subProcess.businessObject); 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 // given
var eventId = 'IntermediateThrowEvent_1', var eventId = 'IntermediateThrowEvent',
intermediateThrowEvent = elementRegistry.get(eventId), intermediateThrowEvent = elementRegistry.get(eventId),
subProcess = elementRegistry.get('SubProcess_1'); subProcess = elementRegistry.get('SubProcess_1');
var elements = [ intermediateThrowEvent ]; var elements = [ intermediateThrowEvent ];
// when // when
modeling.moveElements(elements, { x: 60, y: -191 }, subProcess); modeling.moveElements(elements, { x: 0, y: -150 }, subProcess);
// then // then
expect(intermediateThrowEvent.parent).to.eql(subProcess); expect(intermediateThrowEvent.parent).to.eql(subProcess);
expect(intermediateThrowEvent.type).to.equal('bpmn:IntermediateThrowEvent'); 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 describe('event attaching', function() {
var intermediateThrowEvent = elementRegistry.get('IntermediateThrowEvent_1');
var elements = [ intermediateThrowEvent ]; var testXML = require('./BpmnRules.attaching.bpmn');
// then beforeEach(bootstrapModeler(testXML, { modules: testModules }));
expectCanMove(elements, 'SubProcess_1', {
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', attach: 'attach',
move: true 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
});
});
})
);
}); });