From 33f9e7be6ed4e89572d752d093713feefb8852ba Mon Sep 17 00:00:00 2001 From: Maciej Barelkowski Date: Wed, 22 May 2019 10:42:07 +0200 Subject: [PATCH] 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. --- .../modeling/behavior/AttachEventBehavior.js | 37 +++--- lib/features/rules/BpmnRules.js | 13 +- .../behavior/AttachEventBehaviorSpec.js | 109 ++++++++++++----- .../features/rules/BpmnRules.attaching.bpmn | 112 ++++++++++++++++++ test/spec/features/rules/BpmnRulesSpec.js | 64 ++++++++-- 5 files changed, 281 insertions(+), 54 deletions(-) create mode 100644 test/spec/features/rules/BpmnRules.attaching.bpmn diff --git a/lib/features/modeling/behavior/AttachEventBehavior.js b/lib/features/modeling/behavior/AttachEventBehavior.js index edf13e00..ef908b38 100644 --- a/lib/features/modeling/behavior/AttachEventBehavior.js +++ b/lib/features/modeling/behavior/AttachEventBehavior.js @@ -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]; +} diff --git a/lib/features/rules/BpmnRules.js b/lib/features/rules/BpmnRules.js index ab013a34..4f7f8f10 100644 --- a/lib/features/rules/BpmnRules.js +++ b/lib/features/rules/BpmnRules.js @@ -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) { diff --git a/test/spec/features/modeling/behavior/AttachEventBehaviorSpec.js b/test/spec/features/modeling/behavior/AttachEventBehaviorSpec.js index 1e10b0d9..39fb2249 100644 --- a/test/spec/features/modeling/behavior/AttachEventBehaviorSpec.js +++ b/test/spec/features/modeling/behavior/AttachEventBehaviorSpec.js @@ -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; +} diff --git a/test/spec/features/rules/BpmnRules.attaching.bpmn b/test/spec/features/rules/BpmnRules.attaching.bpmn new file mode 100644 index 00000000..7dc38650 --- /dev/null +++ b/test/spec/features/rules/BpmnRules.attaching.bpmn @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/rules/BpmnRulesSpec.js b/test/spec/features/rules/BpmnRulesSpec.js index f61d8d82..8ce66918 100644 --- a/test/spec/features/rules/BpmnRulesSpec.js +++ b/test/spec/features/rules/BpmnRulesSpec.js @@ -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 + }); + }); + }) + ); });