Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: default user tasks with zeebe:UserTask extension element #86

Merged
merged 5 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions lib/camunda-cloud/CreateZeebeUserTaskBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { createElement } from '../util/ElementUtil';
import { getZeebeUserTaskElement } from './util/ZeebeUserTaskUtil';
import { getBusinessObject, is } from 'bpmn-js/lib/util/ModelUtil';
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';

const HIGH_PRIORITY = 5000;

/**
* Zeebe BPMN specific behavior for creating user tasks.
*/
export default class CreateZeebeUserTaskBehavior extends CommandInterceptor {
constructor(bpmnFactory, eventBus, modeling) {
super(eventBus);

/**
* Add zeebe:userTask extension element when creating bpmn:UserTask.
*/
this.postExecuted(
[ 'shape.create', 'shape.replace' ],
HIGH_PRIORITY,
function(context) {
const shape = context.shape || context.newShape;
const isCopy = context.hints && context.hints.createElementsBehavior === false;

if (!is(shape, 'bpmn:UserTask') || isCopy) {
return;
}

const businessObject = getBusinessObject(shape);

// Use getZeebeUserTaskElement to check if zeebe:userTask already exists
let userTaskElement = getZeebeUserTaskElement(businessObject);

if (!userTaskElement) {
let extensionElements = businessObject.get('extensionElements');

if (!extensionElements) {
extensionElements = createElement(
'bpmn:ExtensionElements',
{
values: [],
},
businessObject,
bpmnFactory
);

modeling.updateProperties(shape, { extensionElements });
}

userTaskElement = createElement(
'zeebe:UserTask',
{},
extensionElements,
bpmnFactory
);

modeling.updateModdleProperties(shape, extensionElements, {
values: [ ...(extensionElements.values || []), userTaskElement ],
});
}
},
true
);
}
}

CreateZeebeUserTaskBehavior.$inject = [ 'bpmnFactory', 'eventBus', 'modeling' ];
3 changes: 3 additions & 0 deletions lib/camunda-cloud/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import CleanUpSubscriptionBehavior from './CleanUpSubscriptionBehavior';
import CleanUpTimerExpressionBehavior from './CleanUpTimerExpressionBehavior';
import CopyPasteBehavior from './CopyPasteBehavior';
import CreateZeebeCallActivityBehavior from './CreateZeebeCallActivityBehavior';
import CreateZeebeUserTaskBehavior from './CreateZeebeUserTaskBehavior';
import DeleteParticipantBehaviour from '../shared/DeleteParticipantBehaviour';
import FormsBehavior from './FormsBehavior';
import RemoveAssignmentDefinitionBehavior from './RemoveAssignmentDefinitionBehavior';
Expand All @@ -22,6 +23,7 @@ export default {
'cleanUpTimerExpressionBehavior',
'copyPasteBehavior',
'createZeebeCallActivityBehavior',
'createZeebeUserTaskBehavior',
'deleteParticipantBehaviour',
'formsBehavior',
'removeAssignmentDefinitionBehavior',
Expand All @@ -36,6 +38,7 @@ export default {
cleanUpTimerExpressionBehavior: [ 'type', CleanUpTimerExpressionBehavior ],
copyPasteBehavior: [ 'type', CopyPasteBehavior ],
createZeebeCallActivityBehavior: [ 'type', CreateZeebeCallActivityBehavior ],
createZeebeUserTaskBehavior: [ 'type', CreateZeebeUserTaskBehavior ],
deleteParticipantBehaviour: [ 'type', DeleteParticipantBehaviour ],
formsBehavior: [ 'type', FormsBehavior ],
removeAssignmentDefinitionBehavior: [ 'type', RemoveAssignmentDefinitionBehavior ],
Expand Down
42 changes: 42 additions & 0 deletions lib/camunda-cloud/util/ZeebeUserTaskUtil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { getExtensionElementsList } from '../../util/ExtensionElementsUtil';

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

/**
* Get all zeebe:userTask elements of an element.
*
* @param {djs.model.Base|ModdleElement} element
*
* @returns {Array<ModdleElement>}
*/
export function getZeebeUserTaskElements(element) {
const businessObject = getBusinessObject(element);
return getExtensionElementsList(businessObject, 'zeebe:UserTask');
}

/**
* Get the first zeebe:userTask element of an element.
*
* @param {djs.model.Base|ModdleElement} element
*
* @returns {ModdleElement|null}
*/
export function getZeebeUserTaskElement(element) {
const userTaskElements = getZeebeUserTaskElements(element);
return userTaskElements[0] || null;
}

/**
* Check whether a zeebe:userTask extension element is set on an element.
*
* @param {djs.model.Base|ModdleElement} element
*
* @returns {boolean}
*/
export function hasZeebeUserTaskExtension(element) {
if (!is(element, 'bpmn:UserTask')) {
return false;

Check warning on line 38 in lib/camunda-cloud/util/ZeebeUserTaskUtil.js

View check run for this annotation

Codecov / codecov/patch

lib/camunda-cloud/util/ZeebeUserTaskUtil.js#L37-L38

Added lines #L37 - L38 were not covered by tests
}

return !!getZeebeUserTaskElement(element);

Check warning on line 41 in lib/camunda-cloud/util/ZeebeUserTaskUtil.js

View check run for this annotation

Codecov / codecov/patch

lib/camunda-cloud/util/ZeebeUserTaskUtil.js#L41

Added line #L41 was not covered by tests
}
18 changes: 0 additions & 18 deletions test/camunda-cloud/CleanUpTaskListenersBehaviorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,24 +155,6 @@ describe('camunda-cloud/features/modeling - CleanUpTaskListenersBehavior', funct
}));


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

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

// when
bpmnReplace.replaceElement(el, {
type: 'bpmn:UserTask'
});

// then
el = elementRegistry.get('NonZeebeUserTask');
const extensionElements = getExtensionElements(el);

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


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

// given
Expand Down
169 changes: 169 additions & 0 deletions test/camunda-cloud/CreateZeebeUserTaskBehaviorSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { bootstrapCamundaCloudModeler, inject } from 'test/TestHelper';

import { find } from 'min-dash';

import {
getZeebeUserTaskElement,
getZeebeUserTaskElements,
} from '../../lib/camunda-cloud/util/ZeebeUserTaskUtil';

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

import emptyProcessDiagramXML from './process-empty.bpmn';
import userTasksXML from './process-user-tasks.bpmn';

describe('camunda-cloud/features/modeling - CreateZeebeUserTaskBehavior', function() {
describe('when creating new shapes', function() {
beforeEach(bootstrapCamundaCloudModeler(emptyProcessDiagramXML));

it('should execute when creating bpmn:UserTask', inject(function(
canvas,
modeling
) {

// given
const rootElement = canvas.getRootElement();

// when
const newShape = modeling.createShape(
{ type: 'bpmn:UserTask' },
{ x: 100, y: 100 },
rootElement
);

// then
const businessObject = getBusinessObject(newShape),
extensionElements = businessObject.get('extensionElements'),
zeebeUserTaskExtension = getZeebeUserTaskElement(newShape);

expect(zeebeUserTaskExtension).to.exist;
expect(extensionElements).to.exist;
expect(zeebeUserTaskExtension.$parent).to.equal(extensionElements);
}));

it('should not execute when creating bpmn:Task', inject(function(
canvas,
modeling
) {

// given
const rootElement = canvas.getRootElement();

// when
const newShape = modeling.createShape(
{ type: 'bpmn:Task' },
{ x: 100, y: 100 },
rootElement
);

// then
const zeebeUserTaskExtension = getZeebeUserTaskElement(newShape);

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

describe('when copying bpmn:UserTask', function() {
beforeEach(bootstrapCamundaCloudModeler(userTasksXML));

it('should re-use existing extensionElement', inject(function(
canvas,
copyPaste,
elementRegistry
) {

// given
const rootElement = canvas.getRootElement();
const userTask = elementRegistry.get('withZeebeUserTask');

// when
copyPaste.copy(userTask);

const elements = copyPaste.paste({
element: rootElement,
point: {
x: 1000,
y: 1000,
},
});

// then
const pastedUserTask = find(elements, (element) =>
is(element, 'bpmn:UserTask')
);

const businessObject = getBusinessObject(pastedUserTask),
extensionElements = businessObject.get('extensionElements'),
zeebeUserTaskExtensions = getZeebeUserTaskElements(pastedUserTask);

expect(zeebeUserTaskExtensions).to.exist;
expect(extensionElements).to.exist;
expect(zeebeUserTaskExtensions.length).to.equal(1);
expect(zeebeUserTaskExtensions[0].$parent).to.equal(extensionElements);
}));

it('should not add zeebe:UserTask if it was not already present', inject(function(
canvas,
copyPaste,
elementRegistry
) {

// given
const rootElement = canvas.getRootElement();
const userTask = elementRegistry.get('withEmptyExternalReference');

// when
copyPaste.copy(userTask);

const elements = copyPaste.paste({
element: rootElement,
point: {
x: 1000,
y: 1000,
},
});

// then
const pastedUserTask = find(elements, (element) =>
is(element, 'bpmn:UserTask')
);

const businessObject = getBusinessObject(pastedUserTask),
extensionElements = businessObject.get('extensionElements'),
zeebeUserTaskExtensions = getZeebeUserTaskElements(pastedUserTask);

expect(zeebeUserTaskExtensions).to.exist;
barmac marked this conversation as resolved.
Show resolved Hide resolved
expect(extensionElements).to.exist;
expect(zeebeUserTaskExtensions.length).to.equal(0);
}));
});

describe('when replacing bpmn:Task', function() {
beforeEach(bootstrapCamundaCloudModeler(userTasksXML));

it('should execute when replacing to bpmn:UserTask', inject(function(
elementRegistry,
bpmnReplace,
canvas,
modeling
) {

// given
const rootElement = canvas.getRootElement();

// when
const task = modeling.createShape(
{ type: 'bpmn:Task', id: 'simpleTask' },
{ x: 100, y: 100 },
rootElement
);
bpmnReplace.replaceElement(task, { type: 'bpmn:UserTask' });

// then
const updatedTask = elementRegistry.get(task.id),
zeebeUserTaskExtension = getZeebeUserTaskElement(updatedTask);

expect(zeebeUserTaskExtension).to.exist;
}));
barmac marked this conversation as resolved.
Show resolved Hide resolved
});
});
8 changes: 4 additions & 4 deletions test/camunda-cloud/FormsBehaviorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ describe('camunda-cloud/features/modeling - FormsBehavior', function() {
});

// when
modeling.createShape(element, { x: 100, y: 100 }, rootElement);
modeling.createShape(element, { x: 100, y: 100 }, rootElement, { createElementsBehavior: false });
barmac marked this conversation as resolved.
Show resolved Hide resolved

// then
expect(getUserTaskForm(element)).not.to.exist;
Expand Down Expand Up @@ -230,7 +230,7 @@ describe('camunda-cloud/features/modeling - FormsBehavior', function() {
});

// when
modeling.createShape(element, { x: 100, y: 100 }, rootElement);
modeling.createShape(element, { x: 100, y: 100 }, rootElement, { createElementsBehavior: false });

// then
expect(getUserTaskForm(element)).not.to.exist;
Expand Down Expand Up @@ -269,7 +269,7 @@ describe('camunda-cloud/features/modeling - FormsBehavior', function() {
});

// when
modeling.createShape(element, { x: 100, y: 100 }, rootElement);
modeling.createShape(element, { x: 100, y: 100 }, rootElement, { createElementsBehavior: false });

// then
expect(getUserTaskForm(element)).to.exist;
Expand Down Expand Up @@ -310,7 +310,7 @@ describe('camunda-cloud/features/modeling - FormsBehavior', function() {
});

// when
modeling.createShape(element, { x: 100, y: 100 }, rootElement);
modeling.createShape(element, { x: 100, y: 100 }, rootElement, { createElementsBehavior: false });
}));


Expand Down
9 changes: 9 additions & 0 deletions test/camunda-cloud/process-user-tasks.bpmn
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
<zeebe:formDefinition externalReference="" />
</bpmn:extensionElements>
</bpmn:userTask>
<bpmn:userTask id="withZeebeUserTask" name="With zeebe user task">
<bpmn:extensionElements>
<zeebe:userTask />
</bpmn:extensionElements>
</bpmn:userTask>
<bpmn:userTask id="UserTask_13" name="UserTask_13">
<bpmn:extensionElements>
<zeebe:formDefinition formKey="" />
Expand Down Expand Up @@ -156,6 +161,10 @@
<dc:Bounds x="250" y="780" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BPMNShape_0xs9191" bpmnElement="withZeebeUserTask">
<dc:Bounds x="250" y="870" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BPMNShape_1ts16ji" bpmnElement="withEmptyExternalReference">
<dc:Bounds x="390" y="690" width="100" height="80" />
<bpmndi:BPMNLabel />
Expand Down
Loading