diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b03856..8250b3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to [camunda-bpmn-js-behaviors](https://github.com/camunda/ca ___Note:__ Yet to be released changes appear here._ +* `FIX`: clean up `zeebe:TaskListeners` on user task properties update ([#90](https://github.com/camunda/camunda-bpmn-js-behaviors/pull/90)) + ## 1.7.0 * `FEAT`: support `zeebe:TaskListener` ([#88](https://github.com/camunda/camunda-bpmn-js-behaviors/pull/88)) diff --git a/lib/camunda-cloud/CleanUpTaskListenersBehavior.js b/lib/camunda-cloud/CleanUpTaskListenersBehavior.js index 401236a..dbfc98f 100644 --- a/lib/camunda-cloud/CleanUpTaskListenersBehavior.js +++ b/lib/camunda-cloud/CleanUpTaskListenersBehavior.js @@ -10,20 +10,22 @@ export default class CleanUpTaskListenersBehavior extends CommandInterceptor { constructor(eventBus, modeling) { super(eventBus); - // remove task listeners of disallowed type + // remove task listeners of disallowed type on shape replace this.postExecuted('shape.replace', function(event) { const element = event.context.newShape; - const taskListenersContainer = getTaskListenersContainer(element); - if (!taskListenersContainer) { - return; - } - const listeners = taskListenersContainer.get('listeners'); - const newListeners = withoutDisallowedListeners(element, listeners); + updateListeners(element, modeling); + }); - if (newListeners.length !== listeners.length) { - modeling.updateModdleProperties(element, taskListenersContainer, { listeners: newListeners }); + // remove task listeners of disallowed type on user task properties update + this.postExecuted('element.updateModdleProperties', function(event) { + const element = event.context.element; + + if (!is(element, 'bpmn:UserTask')) { + return; } + + updateListeners(element, modeling); }); // remove empty task listener container @@ -54,6 +56,20 @@ CleanUpTaskListenersBehavior.$inject = [ ]; // helpers ////////// +function updateListeners(element, modeling) { + const taskListenersContainer = getTaskListenersContainer(element); + if (!taskListenersContainer) { + return; + } + + const listeners = taskListenersContainer.get('listeners'); + const newListeners = withoutDisallowedListeners(element, listeners); + + if (newListeners.length !== listeners.length) { + modeling.updateModdleProperties(element, taskListenersContainer, { listeners: newListeners }); + } +} + function withoutDisallowedListeners(element, listeners) { return listeners.filter(listener => { if ( diff --git a/test/camunda-cloud/CleanUpTaskListenersBehaviorSpec.js b/test/camunda-cloud/CleanUpTaskListenersBehaviorSpec.js index 2516877..d51963f 100644 --- a/test/camunda-cloud/CleanUpTaskListenersBehaviorSpec.js +++ b/test/camunda-cloud/CleanUpTaskListenersBehaviorSpec.js @@ -1,11 +1,12 @@ import { bootstrapCamundaCloudModeler, + getBpmnJS, inject } from 'test/TestHelper'; import { getExtensionElementsList } from 'lib/util/ExtensionElementsUtil'; -import { getBusinessObject } from 'bpmn-js/lib/util/ModelUtil'; +import { getBusinessObject, is } from 'bpmn-js/lib/util/ModelUtil'; import diagramXML from './task-listeners.bpmn'; @@ -66,7 +67,7 @@ describe('camunda-cloud/features/modeling - CleanUpTaskListenersBehavior', funct // then el = elementRegistry.get(element); - const extensionElements = getBusinessObject(el).get('extensionElements'); + const extensionElements = getElementExtensions(el); expect(extensionElements.get('values')).to.have.lengthOf(2); })); @@ -106,7 +107,7 @@ describe('camunda-cloud/features/modeling - CleanUpTaskListenersBehavior', funct // then el = elementRegistry.get('UserTaskWrongType'); - const container = getExtensionElementsList(getBusinessObject(el), 'zeebe:TaskListeners')[0]; + const container = getFirstExtensionElement(el, 'zeebe:TaskListeners'); expect(container.get('listeners')).to.have.lengthOf(1); })); @@ -125,7 +126,7 @@ describe('camunda-cloud/features/modeling - CleanUpTaskListenersBehavior', funct // then el = elementRegistry.get('UserTaskWrongType'); - const container = getExtensionElementsList(getBusinessObject(el), 'zeebe:TaskListeners')[0]; + const container = getFirstExtensionElement(el, 'zeebe:TaskListeners'); expect(container.get('listeners')).to.have.lengthOf(2); })); @@ -145,7 +146,7 @@ describe('camunda-cloud/features/modeling - CleanUpTaskListenersBehavior', funct // then el = elementRegistry.get('UserTaskWrongType'); - const container = getExtensionElementsList(getBusinessObject(el), 'zeebe:TaskListeners')[0]; + const container = getFirstExtensionElement(el, 'zeebe:TaskListeners'); expect(container.get('listeners')).to.have.lengthOf(1); })); @@ -162,13 +163,13 @@ describe('camunda-cloud/features/modeling - CleanUpTaskListenersBehavior', funct // then el = elementRegistry.get('NonZeebeUserTask'); - const extensionElements = getBusinessObject(el).get('extensionElements'); + const extensionElements = getElementExtensions(el); expect(extensionElements.get('values')).to.have.lengthOf(0); })); - it('should remove zeebe:TaskListeners', inject(function(elementRegistry, modeling) { + it('should remove zeebe:TaskListeners container when empty', inject(function(elementRegistry, modeling) { // given const el = elementRegistry.get('UserTask'); @@ -178,13 +179,77 @@ describe('camunda-cloud/features/modeling - CleanUpTaskListenersBehavior', funct modeling.updateModdleProperties(el, listenersContainer, { listeners: [] }); // then - const extensionElements = getBusinessObject(el).get('extensionElements'); + const extensionElements = getElementExtensions(el); expect(extensionElements.get('values')).to.have.lengthOf(1); })); + + + it('should remove zeebe:TaskListeners when user task is not zeebe user task', inject(function(elementRegistry, modeling) { + + // given + const el = elementRegistry.get('UserTask'); + + // when + const extensionElements = getElementExtensions(el); + modeling.updateModdleProperties(el, extensionElements, { values: removeZeebeUserTask(extensionElements) }); + + // then + const listenersContainer = getTaskListenersContainer(el); + expect(listenersContainer).not.to.exist; + })); + + + it('should NOT remove zeebe:TaskListeners when an unrelated property is added', inject(function(elementRegistry, modeling) { + + // given + const el = elementRegistry.get('UserTask'); + + // when + addFormDefinition(el); + + // then + const extensionElements = getElementExtensions(el); + + const taskListeners = getFirstExtensionElement(el, 'zeebe:TaskListeners'); + const userTask = getFirstExtensionElement(el, 'zeebe:UserTask'); + const formDefinition = getFirstExtensionElement(el, 'zeebe:FormDefinition'); + + expect(extensionElements.get('values')).to.have.lengthOf(3); + expect(taskListeners).to.exist; + expect(userTask).to.exist; + expect(formDefinition).to.exist; + })); }); }); + +// helpers + +function getElementExtensions(element) { + return getBusinessObject(element).get('extensionElements'); +} + +function getFirstExtensionElement(element, type) { + return getExtensionElementsList(getBusinessObject(element), type)[0]; +} + function getTaskListenersContainer(element) { - return getExtensionElementsList(getBusinessObject(element), 'zeebe:TaskListeners')[0]; + return getFirstExtensionElement(element, 'zeebe:TaskListeners'); +} + +function removeZeebeUserTask(extensionElements) { + return extensionElements.get('values').filter(extensionElement => !is(extensionElement, 'zeebe:UserTask')); } + +function addFormDefinition(element) { + getBpmnJS().invoke(function(bpmnFactory, modeling) { + const extensionElements = getElementExtensions(element); + const values = extensionElements.get('values'), + formDefinition = bpmnFactory.create('zeebe:FormDefinition'); + + modeling.updateModdleProperties(element, extensionElements, { + values: values.concat(formDefinition) + }); + }); +} \ No newline at end of file