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 all 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
81 changes: 81 additions & 0 deletions lib/camunda-cloud/CreateZeebeUserTaskBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { getBusinessObject, is } from 'bpmn-js/lib/util/ModelUtil';
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';

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

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 explicitlyDisabled = context.hints && context.hints.createElementsBehavior === false;

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

let userTaskElement = getZeebeUserTask(shape);
if (userTaskElement) {
return;
}

const businessObject = getBusinessObject(shape);
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' ];

/**
* Get zeebe:userTask extension.
*
* @param {djs.model.Base|ModdleElement} element
*
* @returns {ModdleElement|null}
*/
function getZeebeUserTask(element) {
const businessObject = getBusinessObject(element);
const userTaskElements = getExtensionElementsList(businessObject, 'zeebe:UserTask');

return userTaskElements[0] || null;
}
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
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
239 changes: 239 additions & 0 deletions test/camunda-cloud/CreateZeebeUserTaskBehaviorSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import { bootstrapCamundaCloudModeler, inject } from 'test/TestHelper';

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

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


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

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

describe('when a shape is created', 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),
zeebeUserTaskExtensions = getExtensionElementsList(businessObject, 'zeebe:UserTask');

expect(zeebeUserTaskExtensions).to.exist;
expect(zeebeUserTaskExtensions).to.have.lengthOf(1);
}));


it('should NOT execute when zeebe:UserTask already present', inject(function(
canvas,
bpmnFactory,
modeling
) {

// given
const rootElement = canvas.getRootElement(),
bo = bpmnFactory.create('bpmn:UserTask', {
extensionElements: bpmnFactory.create('bpmn:ExtensionElements', {
values: [ bpmnFactory.create('zeebe:UserTask') ],
}),
});

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

// then
const businessObject = getBusinessObject(newShape),
zeebeUserTaskExtensions = getExtensionElementsList(businessObject, 'zeebe:UserTask');

expect(zeebeUserTaskExtensions).to.exist;
expect(zeebeUserTaskExtensions).to.have.lengthOf(1);
}));


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 = getZeebeUserTask(newShape);

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


describe('when a shape is pasted', function() {

beforeEach(bootstrapCamundaCloudModeler(userTasksXML));


it('should NOT add zeebe:UserTask', inject(function(
canvas,
copyPaste,
elementRegistry
) {

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

// 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 zeebeUserTask = getZeebeUserTask(pastedUserTask);

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


it('should keep existing zeebe:UserTask', 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 zeebeUserTasks = getExtensionElementsList(pastedUserTask, 'zeebe:UserTask');

expect(zeebeUserTasks).to.exist;
expect(zeebeUserTasks).to.have.lengthOf(1);
}));
});


describe('when a shape is replaced', function() {

beforeEach(bootstrapCamundaCloudModeler(userTasksXML));


it('should add zeebe:UserTask when target is 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 = getZeebeUserTask(updatedTask);

expect(zeebeUserTaskExtension).to.exist;
}));
barmac marked this conversation as resolved.
Show resolved Hide resolved


it('should NOT add zeebe:UserTask when target is bpmn:ServiceTask', 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:ServiceTask' });

// then
const updatedTask = elementRegistry.get(task.id),
zeebeUserTask = getZeebeUserTask(updatedTask);

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


// helpers //////////

/**
* Get the first zeebe:userTask element of an element.
*
* @param {djs.model.Base|ModdleElement} element
*
* @returns {ModdleElement|null}
*/
function getZeebeUserTask(element) {
const businessObject = getBusinessObject(element);
const userTaskElements = getExtensionElementsList(businessObject, 'zeebe:UserTask');

return userTaskElements[0] || null;
}
Loading
Loading