diff --git a/app/spiffworkflow/DataObject/DataObjectHelpers.js b/app/spiffworkflow/DataObject/DataObjectHelpers.js index abff7ef73..fc03799c9 100644 --- a/app/spiffworkflow/DataObject/DataObjectHelpers.js +++ b/app/spiffworkflow/DataObject/DataObjectHelpers.js @@ -5,10 +5,19 @@ */ -export function findDataObjects(process) { +export function findDataObjects(parent) { let dataObjects = []; - if (!process || !process.flowElements) { - return dataObjects; + let process; + if (!parent) { + return []; + } + if (parent.processRef) { + process = parent.processRef; + } else { + process = parent; + } + if (!process.flowElements) { + return []; } for (const element of process.flowElements) { if (element.$type === 'bpmn:DataObject') { diff --git a/app/spiffworkflow/DataObject/DataObjectInterceptor.js b/app/spiffworkflow/DataObject/DataObjectInterceptor.js index bdbfb2da1..2fb3498d9 100644 --- a/app/spiffworkflow/DataObject/DataObjectInterceptor.js +++ b/app/spiffworkflow/DataObject/DataObjectInterceptor.js @@ -1,8 +1,10 @@ import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; -import { is } from 'bpmn-js/lib/util/ModelUtil'; -import { findDataObjects } from './DataObjectHelpers'; +import {getDi, is} from 'bpmn-js/lib/util/ModelUtil'; +import {findDataObjects, findDataReferenceShapes} from './DataObjectHelpers'; var HIGH_PRIORITY = 1500; - +import { + remove as collectionRemove, +} from 'diagram-js/lib/util/Collections'; /** * This Command Interceptor functions like the BpmnUpdator in BPMN.js - It hooks into events * from Diagram.js and updates the underlying BPMN model accordingly. @@ -53,6 +55,45 @@ export default class DataObjectInterceptor extends CommandInterceptor { shape.businessObject.dataObjectRef = dataObject; } }); + + /** + * Don't remove the associated DataObject, unless all references to that data object + * Difficult to do given placement of this logic in the BPMN Updater, so we have + * to manually handle the removal. + */ + this.executed([ 'shape.delete' ], HIGH_PRIORITY, function(event) { + const { context } = event; + const { shape, oldParent } = context; + if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') { + const references = findDataReferenceShapes( + oldParent, + shape.businessObject.dataObjectRef.id + ); + if (references.length === 0) { + return; // Use the default bahavior and delete the data object. + } + // Remove the business Object + let containment = ''; + const { businessObject } = shape; + if (is(businessObject, 'bpmn:DataOutputAssociation')) { + containment = 'dataOutputAssociations'; + } + if (is(businessObject, 'bpmn:DataInputAssociation')) { + containment = 'dataInputAssociations'; + } + const children = businessObject.$parent.get(containment); + collectionRemove(children, businessObject); + + // Remove the visible element. + const di = getDi(shape); + const planeElements = di.$parent.get('planeElement'); + collectionRemove(planeElements, di); + di.$parent = null; + + // Stop the propogation. + event.stopPropagation(); + } + }); } } diff --git a/app/spiffworkflow/extensions/propertiesPanel/ExtensionsPropertiesProvider.js b/app/spiffworkflow/extensions/propertiesPanel/ExtensionsPropertiesProvider.js index 121e72f4d..2afa97eb7 100644 --- a/app/spiffworkflow/extensions/propertiesPanel/ExtensionsPropertiesProvider.js +++ b/app/spiffworkflow/extensions/propertiesPanel/ExtensionsPropertiesProvider.js @@ -245,6 +245,7 @@ function createServiceGroup(element, translate, moddle, commandStack) { element, moddle, translate, + commandStack }), }, ], diff --git a/app/spiffworkflow/extensions/propertiesPanel/SpiffExtensionServiceProperties.js b/app/spiffworkflow/extensions/propertiesPanel/SpiffExtensionServiceProperties.js index 3d60240a5..33f56d70d 100644 --- a/app/spiffworkflow/extensions/propertiesPanel/SpiffExtensionServiceProperties.js +++ b/app/spiffworkflow/extensions/propertiesPanel/SpiffExtensionServiceProperties.js @@ -111,9 +111,11 @@ export function ServiceTaskOperatorSelect(props) { console.error(`Could not find service task operator with id: ${value}`); return; } - + if (!(element.businessObject.id in previouslyUsedServiceTaskParameterValuesHash)) { + previouslyUsedServiceTaskParameterValuesHash[element.businessObject.id] = {} + } const previouslyUsedServiceTaskParameterValues = - previouslyUsedServiceTaskParameterValuesHash[value]; + previouslyUsedServiceTaskParameterValuesHash[element.businessObject.id][value]; const { businessObject } = element; let extensions = businessObject.extensionElements; @@ -143,9 +145,12 @@ export function ServiceTaskOperatorSelect(props) { newParameterModdleElement.type = stoParameter.type; newParameterList.parameters.push(newParameterModdleElement); }); - previouslyUsedServiceTaskParameterValuesHash[value] = newParameterList; + + previouslyUsedServiceTaskParameterValuesHash[element.businessObject.id][ + value + ] = newParameterList; if (oldServiceTaskOperatorModdleElement) { - previouslyUsedServiceTaskParameterValuesHash[ + previouslyUsedServiceTaskParameterValuesHash[element.businessObject.id][ oldServiceTaskOperatorModdleElement.id ] = oldServiceTaskOperatorModdleElement.parameterList; } @@ -228,13 +233,21 @@ function serviceTaskParameterEntries(props) { } function ServiceTaskParameterTextField(props) { - const { idPrefix, element, serviceTaskParameterModdleElement } = props; + const { idPrefix, element, serviceTaskParameterModdleElement, commandStack } = props; const debounce = useService('debounceInput'); + const setValue = (value) => { - serviceTaskParameterModdleElement.value = value; + commandStack.execute('element.updateModdleProperties', { + element, + moddleElement: serviceTaskParameterModdleElement, + properties: { + value: value, + }, + }); }; + const getValue = () => { return serviceTaskParameterModdleElement.value; }; diff --git a/test/spec/DataObjectInterceptorSpec.js b/test/spec/DataObjectInterceptorSpec.js index 0742a97b7..85edf9374 100644 --- a/test/spec/DataObjectInterceptorSpec.js +++ b/test/spec/DataObjectInterceptorSpec.js @@ -50,6 +50,41 @@ describe('DataObject Interceptor', function() { })); + it('Deleting a data object reference does not delete the data object, unless it is the last reference', inject(function(canvas, modeling) { + + // IF - two dataObjectReferences are created + let rootShape = canvas.getRootElement(); + const dataObjectRefShape1 = modeling.createShape({ type: 'bpmn:DataObjectReference' }, + { x: 220, y: 220 }, rootShape); + const dataObjectRefShape2 = modeling.createShape({ type: 'bpmn:DataObjectReference' }, + { x: 320, y: 220 }, rootShape); + + // AND one is deleted + modeling.removeShape(dataObjectRefShape1) + + // THEN - there is still a data object + const dataObjects = findDataObjects(rootShape.businessObject); + expect(dataObjects.length).to.equal(1); + })); + + it('Deleting all the data references will also delete the data object', inject(function(canvas, modeling) { + + // IF - two dataObjectReferences are created + let rootShape = canvas.getRootElement(); + const dataObjectRefShape1 = modeling.createShape({ type: 'bpmn:DataObjectReference' }, + { x: 220, y: 220 }, rootShape); + const dataObjectRefShape2 = modeling.createShape({ type: 'bpmn:DataObjectReference' }, + { x: 320, y: 220 }, rootShape); + + // AND both are deleted + modeling.removeShape(dataObjectRefShape1); + modeling.removeShape(dataObjectRefShape2); + + // THEN - there is no data object + const dataObjects = findDataObjects(rootShape.businessObject); + expect(dataObjects.length).to.equal(0); + })); + it('Creating a new Reference will update the name to match the DataObject', inject(function(canvas, modeling) { // IF - a Data Reference Exists