feat(bpmn-rules): replace start events only allowed in event sub process

this includes the following event definitions:

* error
* escalation
* compensate

Closes #583
This commit is contained in:
Philipp Fromme 2016-11-09 11:45:12 +01:00 committed by pedesen
parent 42a40494a7
commit d5fa27ead2
4 changed files with 195 additions and 10 deletions

View File

@ -13,7 +13,10 @@ var getParents = require('../modeling/util/ModelingUtil').getParents,
getBusinessObject = require('../../util/ModelUtil').getBusinessObject, getBusinessObject = require('../../util/ModelUtil').getBusinessObject,
isExpanded = require('../../util/DiUtil').isExpanded, isExpanded = require('../../util/DiUtil').isExpanded,
isEventSubProcess = require('../../util/DiUtil').isEventSubProcess, isEventSubProcess = require('../../util/DiUtil').isEventSubProcess,
isInterrupting = require('../../util/DiUtil').isInterrupting; isInterrupting = require('../../util/DiUtil').isInterrupting,
hasErrorEventDefinition = require('../../util/DiUtil').hasErrorEventDefinition,
hasEscalationEventDefinition = require('../../util/DiUtil').hasEscalationEventDefinition,
hasCompensateEventDefinition = require('../../util/DiUtil').hasCompensateEventDefinition;
var RuleProvider = require('diagram-js/lib/features/rules/RuleProvider'); var RuleProvider = require('diagram-js/lib/features/rules/RuleProvider');
@ -564,20 +567,32 @@ function canReplace(elements, target, position) {
forEach(elements, function(element) { forEach(elements, function(element) {
// replace a non-interrupting start event by a blank interrupting start event
// when the target is not an event sub process
if (!isEventSubProcess(target)) { if (!isEventSubProcess(target)) {
if (is(element, 'bpmn:StartEvent') && if (is(element, 'bpmn:StartEvent') &&
!isInterrupting(element) &&
element.type !== 'label' && element.type !== 'label' &&
canDrop(element, target)) { canDrop(element, target)) {
// replace a non-interrupting start event by a blank interrupting start event
// when the target is not an event sub process
if (!isInterrupting(element)) {
canExecute.replacements.push({ canExecute.replacements.push({
oldElementId: element.id, oldElementId: element.id,
newElementType: 'bpmn:StartEvent' newElementType: 'bpmn:StartEvent'
}); });
} }
// replace an error/escalation/compansate start event by a blank interrupting start event
// when the target is not an event sub process
if (hasErrorEventDefinition(element) ||
hasEscalationEventDefinition(element) ||
hasCompensateEventDefinition(element)) {
canExecute.replacements.push({
oldElementId: element.id,
newElementType: 'bpmn:StartEvent'
});
}
}
} }
if (!is(target, 'bpmn:Transaction')) { if (!is(target, 'bpmn:Transaction')) {

View File

@ -3,6 +3,8 @@
var is = require('./ModelUtil').is, var is = require('./ModelUtil').is,
getBusinessObject = require('./ModelUtil').getBusinessObject; getBusinessObject = require('./ModelUtil').getBusinessObject;
var forEach = require('lodash/collection/forEach');
module.exports.isExpanded = function(element) { module.exports.isExpanded = function(element) {
if (is(element, 'bpmn:CallActivity')) { if (is(element, 'bpmn:CallActivity')) {
@ -27,3 +29,32 @@ module.exports.isInterrupting = function(element) {
module.exports.isEventSubProcess = function(element) { module.exports.isEventSubProcess = function(element) {
return element && !!getBusinessObject(element).triggeredByEvent; return element && !!getBusinessObject(element).triggeredByEvent;
}; };
function hasEventDefinition(element, eventType) {
var bo = getBusinessObject(element),
hasEventDefinition = false;
if (bo.eventDefinitions) {
forEach(bo.eventDefinitions, function(event) {
if (is(event, eventType)) {
hasEventDefinition = true;
}
});
}
return hasEventDefinition;
}
module.exports.hasEventDefinition = hasEventDefinition;
module.exports.hasErrorEventDefinition = function(element) {
return hasEventDefinition(element, 'bpmn:ErrorEventDefinition');
};
module.exports.hasEscalationEventDefinition = function(element) {
return hasEventDefinition(element, 'bpmn:EscalationEventDefinition');
};
module.exports.hasCompensateEventDefinition = function(element) {
return hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
};

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?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:di="http://www.omg.org/spec/DD/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"> <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/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="1.2.2">
<bpmn:process id="Process_1" isExecutable="false"> <bpmn:process id="Process_1" isExecutable="false">
<bpmn:subProcess id="SubProcess_1" name="SubProcess_1"> <bpmn:subProcess id="SubProcess_1" name="SubProcess_1">
<bpmn:incoming>SequenceFlow_1</bpmn:incoming> <bpmn:incoming>SequenceFlow_1</bpmn:incoming>
@ -31,6 +31,16 @@
</bpmn:task> </bpmn:task>
<bpmn:sequenceFlow id="SequenceFlow_1" sourceRef="Task_3" targetRef="SubProcess_1" /> <bpmn:sequenceFlow id="SequenceFlow_1" sourceRef="Task_3" targetRef="SubProcess_1" />
<bpmn:sequenceFlow id="SequenceFlow_2" sourceRef="SubProcess_1" targetRef="Task_4" /> <bpmn:sequenceFlow id="SequenceFlow_2" sourceRef="SubProcess_1" targetRef="Task_4" />
<bpmn:subProcess id="SubProcess_3" name="SubProcess_3" triggeredByEvent="true">
<bpmn:startEvent id="StartEvent_3" name="StartEvent_3">
<bpmn:outgoing>SequenceFlow_094t9fa</bpmn:outgoing>
<bpmn:errorEventDefinition />
</bpmn:startEvent>
<bpmn:task id="Task_5">
<bpmn:incoming>SequenceFlow_094t9fa</bpmn:incoming>
</bpmn:task>
<bpmn:sequenceFlow id="SequenceFlow_094t9fa" sourceRef="StartEvent_3" targetRef="Task_5" />
</bpmn:subProcess>
</bpmn:process> </bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1"> <bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1"> <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
@ -92,6 +102,25 @@
<dc:Bounds x="400" y="142" width="90" height="20" /> <dc:Bounds x="400" y="142" width="90" height="20" />
</bpmndi:BPMNLabel> </bpmndi:BPMNLabel>
</bpmndi:BPMNEdge> </bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="SubProcess_09cm4qb_di" bpmnElement="SubProcess_3" isExpanded="true">
<dc:Bounds x="816" y="59" width="350" height="200" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="StartEvent_1v1eve6_di" bpmnElement="StartEvent_3">
<dc:Bounds x="858" y="134" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="831" y="170" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_1j96pnq_di" bpmnElement="Task_5">
<dc:Bounds x="972" y="112" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_094t9fa_di" bpmnElement="SequenceFlow_094t9fa">
<di:waypoint xsi:type="dc:Point" x="894" y="152" />
<di:waypoint xsi:type="dc:Point" x="972" y="152" />
<bpmndi:BPMNLabel>
<dc:Bounds x="888" y="127" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane> </bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram> </bpmndi:BPMNDiagram>
</bpmn:definitions> </bpmn:definitions>

View File

@ -12,7 +12,8 @@ var modelingModule = require('../../../../lib/features/modeling'),
var is = require('../../../../lib/util/ModelUtil').is, var is = require('../../../../lib/util/ModelUtil').is,
isExpanded = require('../../../../lib/util/DiUtil').isExpanded, isExpanded = require('../../../../lib/util/DiUtil').isExpanded,
isInterrupting = require('../../../../lib/util/DiUtil').isInterrupting, isInterrupting = require('../../../../lib/util/DiUtil').isInterrupting,
isEventSubProcess = require('../../../../lib/util/DiUtil').isEventSubProcess; isEventSubProcess = require('../../../../lib/util/DiUtil').isEventSubProcess,
hasErrorEventDefinition = require('../../../../lib/util/DiUtil').hasErrorEventDefinition;
describe('features/replace - bpmn replace', function() { describe('features/replace - bpmn replace', function() {
@ -1030,6 +1031,115 @@ describe('features/replace - bpmn replace', function() {
}) })
); );
it('should replace error start event after moving it outside event sub process',
inject(function(elementRegistry, bpmnReplace, modeling) {
// given
var startEvent = elementRegistry.get('StartEvent_3'),
root = elementRegistry.get('Process_1');
// when
modeling.moveElements([startEvent], { x: 0, y: 200 }, root);
var startEventAfter = elementRegistry.filter(function(element) {
return is(element, 'bpmn:StartEvent') && element.parent === root;
})[0];
// then
expect(hasErrorEventDefinition(startEventAfter)).to.be.false;
expect(startEventAfter.parent).to.equal(root);
})
);
it('should replace error start event after moving it to a regular sub process',
inject(function(bpmnReplace, elementRegistry, modeling) {
// given
var startEvent = elementRegistry.get('StartEvent_3'),
subProcess = elementRegistry.get('SubProcess_1');
// when
modeling.moveElements([startEvent], { x: 260, y: 60 }, subProcess);
var startEventAfter = elementRegistry.filter(function(element) {
return is(element, 'bpmn:StartEvent') && element.parent === subProcess;
})[0];
// then
expect(hasErrorEventDefinition(startEventAfter)).to.be.false;
expect(startEventAfter.parent).to.equal(subProcess);
})
);
it('should not replace error start event after moving it to another event sub process',
inject(function(bpmnReplace, elementRegistry, modeling) {
// given
var startEvent = elementRegistry.get('StartEvent_3'),
subProcess = elementRegistry.get('SubProcess_1');
var eventSubProcess = bpmnReplace.replaceElement(subProcess, {
type: 'bpmn:SubProcess',
triggeredByEvent: true,
isExpanded: true
});
// when
modeling.moveElements([startEvent], { x: 260, y: 60 }, eventSubProcess);
var startEventAfter = elementRegistry.filter(function(element) {
return is(element, 'bpmn:StartEvent') && element.parent === eventSubProcess && element.type !== 'label';
})[1];
// then
expect(hasErrorEventDefinition(startEventAfter)).to.be.true;
expect(startEventAfter.parent).to.equal(eventSubProcess);
})
);
it('should replace error start event when replacing parent event sub process',
inject(function(elementRegistry, bpmnReplace) {
// given
var eventSubProcess = elementRegistry.get('SubProcess_3');
// when
var subProcess = bpmnReplace.replaceElement(eventSubProcess, { type: 'bpmn:SubProcess' });
// then
var replacedStartEvent = elementRegistry.filter(function(element) {
return (element.parent === subProcess && element.type !== 'label');
})[0];
expect(hasErrorEventDefinition(replacedStartEvent)).to.be.false;
expect(replacedStartEvent.parent).to.equal(subProcess);
})
);
it('should not replace error start event when moving parent event sub process',
inject(function(elementRegistry, bpmnReplace, modeling) {
// given
var eventSubProcess = elementRegistry.get('SubProcess_3'),
startEvent = elementRegistry.get('StartEvent_3');
// when
modeling.moveElements([eventSubProcess], { x: 20, y: 30 });
// start event after moving parent
var startEventAfter = elementRegistry.filter(function(element) {
return (element.parent === eventSubProcess && element.type !== 'label');
})[0];
// then
expect(startEventAfter).to.equal(startEvent);
expect(startEventAfter.parent).to.eql(eventSubProcess);
})
);
}); });