Skip to content

Commit

Permalink
feat(cloud/changeElementTemplateHandler): handle `zeebe:linkedResourc…
Browse files Browse the repository at this point in the history
…e` bindings
  • Loading branch information
marstamm committed Jan 17, 2025
1 parent 9300a8b commit 34c13f5
Show file tree
Hide file tree
Showing 7 changed files with 792 additions and 8 deletions.
172 changes: 168 additions & 4 deletions src/cloud-element-templates/cmd/ChangeElementTemplateHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {

import {
find,
isUndefined,
without
} from 'min-dash';

Expand All @@ -33,7 +32,8 @@ import {
MESSAGE_PROPERTY_TYPE,
MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE,
TASK_DEFINITION_TYPES,
ZEEBE_CALLED_ELEMENT
ZEEBE_CALLED_ELEMENT,
ZEEBE_LINKED_RESOURCE_PROPERTY
} from '../util/bindingTypes';

import {
Expand Down Expand Up @@ -107,6 +107,8 @@ export default class ChangeElementTemplateHandler {
this._updateMessage(element, oldTemplate, newTemplate);

this._updateCalledElement(element, oldTemplate, newTemplate);

this._updateLinkedResources(element, oldTemplate, newTemplate);
}
}

Expand Down Expand Up @@ -1032,6 +1034,138 @@ export default class ChangeElementTemplateHandler {

return replacedElement;
}


_updateLinkedResources(element, oldTemplate, newTemplate) {
const bpmnFactory = this._bpmnFactory,
commandStack = this._commandStack;

const newLinkedResources = newTemplate.properties.filter((newProperty) => {
const newBinding = newProperty.binding,
newBindingType = newBinding.type;

return newBindingType === 'zeebe:linkedResource';
});

const extensionElements = this._getOrCreateExtensionElements(element);

let linkedResources = findExtension(extensionElements, 'zeebe:LinkedResources');

// (1) remove linkedResourcess if no new specified
if (!newLinkedResources.length) {
if (!linkedResources) {
return;
}

commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: without(extensionElements.get('values'), linkedResources)
}
});
return;
}

if (!linkedResources) {
linkedResources = bpmnFactory.create('zeebe:LinkedResources');

linkedResources.$parent = extensionElements;

commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: extensionElements,
properties: {
values: [ ...extensionElements.get('values'), linkedResources ]
}
});
}

const unusedLinkedResources = linkedResources.get('values')?.slice() || [];
const unusedResourceProperties = oldTemplate?.properties.slice() || [];

newLinkedResources.forEach((newLinkedResource) => {
const oldProperty = findOldProperty(oldTemplate, newLinkedResource),
oldLinkedResource = findBusinessObject(extensionElements, newLinkedResource),
newPropertyValue = getDefaultValue(newLinkedResource),
newBinding = newLinkedResource.binding;

if (oldProperty) {
remove(unusedResourceProperties, oldProperty);
}

// (2) update old LinkesResources
if (oldLinkedResource) {
if (
shouldUpdate(newPropertyValue, newLinkedResource)
|| shouldKeepValue(oldLinkedResource, oldProperty, newLinkedResource)
) {
remove(unusedLinkedResources, oldLinkedResource);
}

if (!shouldKeepValue(oldLinkedResource, oldProperty, newLinkedResource)) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldLinkedResource,
properties: {
[newBinding.property]: newPropertyValue
}
});
}
}

// (3) add new linkedResources
else if (shouldUpdate(newPropertyValue, newLinkedResource)) {
const newProperties = {
linkName: newBinding.linkName,
[newBinding.property]: newPropertyValue
};

const newLinkedResource = createElement('zeebe:LinkedResource', newProperties, extensionElements, bpmnFactory);

commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: linkedResources,
properties: {
values: [ ...linkedResources.get('values'), newLinkedResource ]
}
});
}
});


// (4) remove unused linkedResources
if (unusedLinkedResources.length) {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: linkedResources,
properties: {
values: without(linkedResources.get('values'), linkedResource => unusedLinkedResources.includes(linkedResource))
}
});
}

// (5) remove unused resource properties
unusedResourceProperties.forEach((unusedResourceProperty) => {
const oldLinkedResource = findBusinessObject(extensionElements, unusedResourceProperty);

const oldBinding = unusedResourceProperty.binding;

// No property was reused and element was removed in previous step
if (!oldLinkedResource) {
return;
}

commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: oldLinkedResource,
properties: {
[oldBinding.property]: undefined
}
});
});
}

}

ChangeElementTemplateHandler.$inject = [
Expand Down Expand Up @@ -1106,6 +1240,18 @@ function findBusinessObject(element, property) {
return value.get('name') === binding.name;
});
}

if (bindingType === ZEEBE_LINKED_RESOURCE_PROPERTY) {
const linkedResources = findExtension(businessObject, 'zeebe:LinkedResources');

if (!linkedResources) {
return;
}

return linkedResources.get('values').find((value) => {
return value.get('linkName') === binding.linkName;
});
}
}

/**
Expand Down Expand Up @@ -1224,6 +1370,19 @@ export function findOldProperty(oldTemplate, newProperty) {
return oldBinding.name === newBinding.name;
});
}

if (newBindingType === ZEEBE_LINKED_RESOURCE_PROPERTY) {
return oldProperties.find(oldProperty => {
const oldBinding = oldProperty.binding,
oldBindingType = oldBinding.type;

if (oldBindingType !== ZEEBE_LINKED_RESOURCE_PROPERTY) {
return;
}

return oldBinding.linkName === newBinding.linkName && oldBinding.property === newBinding.property;
});
}
}

/**
Expand Down Expand Up @@ -1289,7 +1448,8 @@ function getPropertyValue(element, property) {

const binding = property.binding,
bindingName = binding.name,
bindingType = binding.type;
bindingType = binding.type,
bindingProperty = binding.property;


if (bindingType === 'property') {
Expand Down Expand Up @@ -1323,12 +1483,16 @@ function getPropertyValue(element, property) {
if (bindingType === MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE) {
return businessObject.get(bindingName);
}

if (bindingType === ZEEBE_LINKED_RESOURCE_PROPERTY) {
return businessObject.get(bindingProperty);
}
}

function remove(array, item) {
const index = array.indexOf(item);

if (isUndefined(index)) {
if (index < 0) {
return array;
}

Expand Down
4 changes: 3 additions & 1 deletion src/cloud-element-templates/util/bindingTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const ZEEBE_TASK_HEADER_TYPE = 'zeebe:taskHeader';
export const MESSAGE_PROPERTY_TYPE = 'bpmn:Message#property';
export const MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE = 'bpmn:Message#zeebe:subscription#property';
export const ZEEBE_CALLED_ELEMENT = 'zeebe:calledElement';
export const ZEEBE_LINKED_RESOURCE_PROPERTY = 'zeebe:linkedResource';

export const EXTENSION_BINDING_TYPES = [
MESSAGE_ZEEBE_SUBSCRIPTION_PROPERTY_TYPE,
Expand All @@ -19,7 +20,8 @@ export const EXTENSION_BINDING_TYPES = [
ZEEBE_TASK_DEFINITION_TYPE_TYPE,
ZEEBE_TASK_DEFINITION,
ZEEBE_TASK_HEADER_TYPE,
ZEEBE_CALLED_ELEMENT
ZEEBE_CALLED_ELEMENT,
ZEEBE_LINKED_RESOURCE_PROPERTY
];

export const TASK_DEFINITION_TYPES = [
Expand Down
63 changes: 60 additions & 3 deletions src/cloud-element-templates/util/propertyUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
ZEEBE_OUTPUT_TYPE,
ZEEBE_PROPERTY_TYPE,
ZEEBE_TASK_HEADER_TYPE,
ZEEBE_CALLED_ELEMENT
ZEEBE_CALLED_ELEMENT,
ZEEBE_LINKED_RESOURCE_PROPERTY
} from './bindingTypes';

import {
Expand Down Expand Up @@ -81,7 +82,8 @@ function getRawPropertyValue(element, property, scope) {
const {
name,
property: bindingProperty,
type
type,
linkName
} = binding;

// property
Expand Down Expand Up @@ -210,6 +212,18 @@ function getRawPropertyValue(element, property, scope) {
return calledElement ? calledElement.get(bindingProperty) : defaultValue;
}

if (type === ZEEBE_LINKED_RESOURCE_PROPERTY) {
const linkedResources = findExtension(businessObject, 'zeebe:LinkedResources');

if (!linkedResources) {
return defaultValue;
}

const linkedResource = linkedResources.get('values').find((value) => value.get('linkName') === linkName);

return linkedResource ? linkedResource.get(bindingProperty) : defaultValue;
}

// should never throw as templates are validated beforehand
throw unknownBindingError(element, property);
}
Expand Down Expand Up @@ -242,7 +256,9 @@ export function setPropertyValue(bpmnFactory, commandStack, element, property, v

const {
name,
type
type,
property: bindingProperty,
linkName
} = binding;

let extensionElements;
Expand Down Expand Up @@ -593,6 +609,47 @@ export function setPropertyValue(bpmnFactory, commandStack, element, property, v
}
}

if (type === ZEEBE_LINKED_RESOURCE_PROPERTY) {
let linkedResources = findExtension(businessObject, 'zeebe:LinkedResources');

if (!linkedResources) {
linkedResources = createElement('zeebe:LinkedResources', null, businessObject, bpmnFactory);

commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: extensionElements,
properties: { values: [ ...extensionElements.get('values'), linkedResources ] }
}
});
}

let linkedResource = linkedResources.get('values').find((value) => value.get('linkName') === linkName);

if (!linkedResource) {
linkedResource = createElement('zeebe:LinkedResource', { linkName }, businessObject, bpmnFactory);

commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: linkedResources,
properties: { values: [ ...linkedResources.get('values'), linkedResource ] }
}
});
}

commands.push({
cmd: 'element.updateModdleProperties',
context: {
...context,
moddleElement: linkedResource,
properties: { [ bindingProperty ]: value }
}
});
}

if (commands.length) {
const commandsToExecute = commands.filter((command) => command !== NO_OP);

Expand Down
Loading

0 comments on commit 34c13f5

Please sign in to comment.