Skip to content

Commit

Permalink
feat: support zeebe:TaskListeners
Browse files Browse the repository at this point in the history
  • Loading branch information
misiekhardcore committed Nov 11, 2024
1 parent 2c64833 commit 66618e9
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 0 deletions.
76 changes: 76 additions & 0 deletions lib/camunda-cloud/CleanUpTaskListenersBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { is } from 'bpmn-js/lib/util/ModelUtil';
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
import { without } from 'min-dash';

import { getExtensionElementsList } from '../util/ExtensionElementsUtil';

const ALLOWED_EVENT_TYPES = [ 'complete', 'assignment' ];

export default class CleanUpTaskListenersBehavior extends CommandInterceptor {
constructor(eventBus, modeling) {
super(eventBus);

// remove task listeners of disallowed type
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);

if (newListeners.length !== listeners.length) {
modeling.updateModdleProperties(element, taskListenersContainer, { listeners: newListeners });
}
});

// remove empty task listener container
this.postExecuted('element.updateModdleProperties', function(event) {
const {
element,
moddleElement
} = event.context;

if (!is(moddleElement, 'zeebe:TaskListeners')) {
return;
}

const listeners = moddleElement.get('listeners');
if (listeners.length) {
return;
}

const extensionElements = moddleElement.$parent;
modeling.updateModdleProperties(element, extensionElements, { values: without(extensionElements.get('values'), moddleElement) });
});
}
}

CleanUpTaskListenersBehavior.$inject = [
'eventBus',
'modeling'
];

// helpers //////////
function withoutDisallowedListeners(element, listeners) {
return listeners.filter(listener => {
if (
!is(element, 'bpmn:UserTask') ||
!ALLOWED_EVENT_TYPES.includes(listener.eventType) ||
!hasZeebeTaskExtensionElement(element)
) {
return false;
}
return true;
});
}

function getTaskListenersContainer(element) {
return getExtensionElementsList(element, 'zeebe:TaskListeners')[0];
}

function hasZeebeTaskExtensionElement(element) {
return getExtensionElementsList(element, 'zeebe:UserTask').length > 0;
}
3 changes: 3 additions & 0 deletions lib/camunda-cloud/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import CleanUpBusinessRuleTaskBehavior from './CleanUpBusinessRuleTaskBehavior';
import CleanUpEndEventBehavior from './CleanUpEndEventBehavior';
import CleanUpExecutionListenersBehavior from './CleanUpExecutionListenersBehavior';
import CleanUpTaskListenersBehavior from './CleanUpTaskListenersBehavior';
import CleanUpSubscriptionBehavior from './CleanUpSubscriptionBehavior';
import CleanUpTimerExpressionBehavior from './CleanUpTimerExpressionBehavior';
import CopyPasteBehavior from './CopyPasteBehavior';
Expand All @@ -16,6 +17,7 @@ export default {
'cleanUpBusinessRuleTaskBehavior',
'cleanUpEndEventBehavior',
'cleanUpExecutionListenersBehavior',
'cleanUpTaskListenersBehavior',
'cleanUpSubscriptionBehavior',
'cleanUpTimerExpressionBehavior',
'copyPasteBehavior',
Expand All @@ -29,6 +31,7 @@ export default {
cleanUpBusinessRuleTaskBehavior: [ 'type', CleanUpBusinessRuleTaskBehavior ],
cleanUpEndEventBehavior: [ 'type', CleanUpEndEventBehavior ],
cleanUpExecutionListenersBehavior: [ 'type', CleanUpExecutionListenersBehavior ],
cleanUpTaskListenersBehavior: [ 'type', CleanUpTaskListenersBehavior ],
cleanUpSubscriptionBehavior: [ 'type', CleanUpSubscriptionBehavior ],
cleanUpTimerExpressionBehavior: [ 'type', CleanUpTimerExpressionBehavior ],
copyPasteBehavior: [ 'type', CopyPasteBehavior ],
Expand Down
191 changes: 191 additions & 0 deletions test/camunda-cloud/CleanUpTaskListenersBehaviorSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import {
bootstrapCamundaCloudModeler,
inject
} from 'test/TestHelper';

import { getExtensionElementsList } from 'lib/util/ExtensionElementsUtil';

import { getBusinessObject } from 'bpmn-js/lib/util/ModelUtil';

import diagramXML from './task-listeners.bpmn';


describe('camunda-cloud/features/modeling - CleanUpTaskListenersBehavior', function() {

beforeEach(bootstrapCamundaCloudModeler(diagramXML));

describe('remove execution listeners if disallowed in element type', function() {

const testCases = [
{
title: 'User Task -> Start Event',
element: 'UserTask',
target: {
type: 'bpmn:StartEvent'
}
},
{
title: 'User Task -> Gateway',
element: 'UserTask',
target: {
type: 'bpmn:Gateway'
}
}
];

for (const { title, element, target } of testCases) {

describe(title, function() {

it('should execute', inject(function(bpmnReplace, elementRegistry) {

// given
let el = elementRegistry.get(element);

// when
bpmnReplace.replaceElement(el, target);

// then
el = elementRegistry.get(element);

const executionListenersContainer = getTaskListenersContainer(el);

expect(executionListenersContainer).not.to.exist;
}));


it('should undo', inject(function(bpmnReplace, commandStack, elementRegistry) {

// given
let el = elementRegistry.get(element);

// when
bpmnReplace.replaceElement(el, target);

commandStack.undo();

// then
el = elementRegistry.get(element);
const extensionElements = getBusinessObject(el).get('extensionElements');

expect(extensionElements.get('values')).to.have.lengthOf(2);
}));


it('should redo', inject(function(bpmnReplace, commandStack, elementRegistry) {

// given
let el = elementRegistry.get(element);

// when
bpmnReplace.replaceElement(el, target);

commandStack.undo();
commandStack.redo();

// then
el = elementRegistry.get(element);
const executionListenersContainer = getTaskListenersContainer(el);

expect(executionListenersContainer).not.to.exist;
}));
});
}
});

describe('should remove execution listeners of disallowed type', function() {

it('should execute', inject(function(bpmnReplace, elementRegistry) {

// given
let el = elementRegistry.get('UserTaskWrongType');
let targetEl = elementRegistry.get('UserTask');

// when
bpmnReplace.replaceElement(el, targetEl);

// then
el = elementRegistry.get('UserTaskWrongType');
const container = getExtensionElementsList(getBusinessObject(el), 'zeebe:TaskListeners')[0];

expect(container.get('listeners')).to.have.lengthOf(1);
}));


it('should undo', inject(function(bpmnReplace, commandStack, elementRegistry) {

// given
let el = elementRegistry.get('UserTaskWrongType');
let targetEl = elementRegistry.get('UserTask');

// when
bpmnReplace.replaceElement(el, targetEl);

commandStack.undo();

// then
el = elementRegistry.get('UserTaskWrongType');
const container = getExtensionElementsList(getBusinessObject(el), 'zeebe:TaskListeners')[0];

expect(container.get('listeners')).to.have.lengthOf(2);
}));


it('should redo', inject(function(bpmnReplace, commandStack, elementRegistry) {

// given
let el = elementRegistry.get('UserTaskWrongType');
let targetEl = elementRegistry.get('UserTask');

// when
bpmnReplace.replaceElement(el, targetEl);

commandStack.undo();
commandStack.redo();

// then
el = elementRegistry.get('UserTaskWrongType');
const container = getExtensionElementsList(getBusinessObject(el), 'zeebe:TaskListeners')[0];

expect(container.get('listeners')).to.have.lengthOf(1);
}));


it('should remove zeebe:TaskListeners for non zeebe user task', inject(function(bpmnReplace, elementRegistry) {

// given
let el = elementRegistry.get('NonZeebeUserTask');
const targetEl = elementRegistry.get('UserTask');

// when
bpmnReplace.replaceElement(el, targetEl);

// then
el = elementRegistry.get('NonZeebeUserTask');
const extensionElements = getBusinessObject(el).get('extensionElements');
console.log(extensionElements.get('values'));

expect(extensionElements.get('values')).to.have.lengthOf(0);
}));


it('should remove zeebe:TaskListeners', inject(function(elementRegistry, modeling) {

// given
const el = elementRegistry.get('UserTask');
const listenersContainer = getTaskListenersContainer(el);

// when
modeling.updateModdleProperties(el, listenersContainer, { listeners: [] });

// then
const extensionElements = getBusinessObject(el).get('extensionElements');

expect(extensionElements.get('values')).to.have.lengthOf(1);
}));
});
});

function getTaskListenersContainer(element) {
return getExtensionElementsList(getBusinessObject(element), 'zeebe:TaskListeners')[0];
}
42 changes: 42 additions & 0 deletions test/camunda-cloud/task-listeners.bpmn
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?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" xmlns:zeebe="http://camunda.org/schema/zeebe/1.0" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0qh9wc7" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.23.0" modeler:userTaskPlatform="Camunda Cloud" modeler:userTaskPlatformVersion="8.0.0">
<bpmn:process id="TaskListenersTest" name="Task Listeners Test" isExecutable="true">
<bpmn:userTask id="UserTask">
<bpmn:extensionElements>
<zeebe:taskListeners>
<zeebe:taskListener eventType="assignment" retries="2" type="assign_listener" />
</zeebe:taskListeners>
<zeebe:userTask />
</bpmn:extensionElements>
</bpmn:userTask>
<bpmn:userTask id="UserTaskWrongType">
<bpmn:extensionElements>
<zeebe:taskListeners>
<zeebe:taskListener eventType="wrong" retries="2" type="wrong_listener" />
<zeebe:taskListener eventType="complete" retries="2" type="complete_listener" />
</zeebe:taskListeners>
<zeebe:userTask />
</bpmn:extensionElements>
</bpmn:userTask>
<bpmn:userTask id="NonZeebeUserTask">
<bpmn:extensionElements>
<zeebe:taskListeners>
<zeebe:taskListener eventType="complete" retries="2" type="complete_listener" />
</zeebe:taskListeners>
</bpmn:extensionElements>
</bpmn:userTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="TaskListenersTest">
<bpmndi:BPMNShape id="UserTask_di" bpmnElement="UserTask">
<dc:Bounds x="160" y="400" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="UserTaskWrongType_di" bpmnElement="UserTaskWrongType">
<dc:Bounds x="160" y="400" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="NonZeebeUserTask_di" bpmnElement="NonZeebeUserTask">
<dc:Bounds x="160" y="400" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

0 comments on commit 66618e9

Please sign in to comment.