diff --git a/app/app.js b/app/app.js index 783a5c6..edfa6fa 100644 --- a/app/app.js +++ b/app/app.js @@ -179,6 +179,15 @@ bpmnModeler.on('spiff.dmn_files.requested', (event) => { }); }); +bpmnModeler.on('spiff.data_stores.requested', (event) => { + event.eventBus.fire('spiff.data_stores.returned', { + options: [ + { type: 'typeahead', name: 'countries' }, + { type: 'kkv', name: 'foods' } + ], + }); +}); + // As call activites might refernce processes across the system // it should be possible to search for a paticular call activity. bpmnModeler.on('spiff.callactivity.search', (event) => { diff --git a/app/spiffworkflow/DataStoreReference/index.js b/app/spiffworkflow/DataStoreReference/index.js new file mode 100644 index 0000000..ffd9aa6 --- /dev/null +++ b/app/spiffworkflow/DataStoreReference/index.js @@ -0,0 +1,10 @@ +import RulesModule from 'diagram-js/lib/features/rules'; +import CustomDataStorePropertiesProvider from './propertiesPanel/CustomDataStorePropertiesProvider'; + +export default { + __depends__: [ + RulesModule + ], + __init__: [ 'dataSourcePropertiesProvider' ], + dataSourcePropertiesProvider: [ 'type', CustomDataStorePropertiesProvider ] +}; diff --git a/app/spiffworkflow/DataStoreReference/propertiesPanel/CustomDataStorePropertiesProvider.js b/app/spiffworkflow/DataStoreReference/propertiesPanel/CustomDataStorePropertiesProvider.js new file mode 100644 index 0000000..4c1d4d5 --- /dev/null +++ b/app/spiffworkflow/DataStoreReference/propertiesPanel/CustomDataStorePropertiesProvider.js @@ -0,0 +1,63 @@ +import { is } from 'bpmn-js/lib/util/ModelUtil'; +import { DataStoreSelect, OPTION_TYPE } from './DataStoreSelect'; + +const LOW_PRIORITY = 500; + +export default function CustomDataStorePropertiesProvider( + modeling, + propertiesPanel, + translate, + moddle, + commandStack, + bpmnFactory, + elementRegistry +) { + this.getGroups = function (element) { + return function (groups) { + if (is(element, 'bpmn:DataStoreReference')) { + groups.push( + createCustomDataStoreGroup(modeling, element, translate, moddle, commandStack, bpmnFactory) + ); + } + return groups; + }; + }; + propertiesPanel.registerProvider(LOW_PRIORITY, this); +} + +CustomDataStorePropertiesProvider.$inject = [ + 'modeling', + 'propertiesPanel', + 'translate', + 'moddle', + 'commandStack', + 'bpmnFactory', + 'elementRegistry' +]; + +function createCustomDataStoreGroup(modeling, element, translate, moddle, commandStack, bpmnFactory) { + + let group = { + label: translate('Custom Data Store Properties'), + id: 'custom-datastore-properties', + entries: [] + }; + + // other custom properties as needed + group.entries.push({ + id: 'selectDataStore', + element, + component: DataStoreSelect, + optionType: OPTION_TYPE.data_stores, + moddle, + commandStack, + translate, + name: 'dataStoreRef', + label: translate('Select DataSource'), + description: translate('Select a datasource from the list'), + modeling, + bpmnFactory + }) + + return group; +} diff --git a/app/spiffworkflow/DataStoreReference/propertiesPanel/DataStoreSelect.js b/app/spiffworkflow/DataStoreReference/propertiesPanel/DataStoreSelect.js new file mode 100644 index 0000000..477f0c6 --- /dev/null +++ b/app/spiffworkflow/DataStoreReference/propertiesPanel/DataStoreSelect.js @@ -0,0 +1,86 @@ +import { useService } from 'bpmn-js-properties-panel'; +import { SelectEntry } from '@bpmn-io/properties-panel'; + +export const OPTION_TYPE = { + data_stores: 'data_stores', +}; + +export const spiffExtensionOptions = {}; + +export function DataStoreSelect(props) { + const { element } = props; + const { commandStack } = props; + const { modeling, bpmnFactory } = props; + const { id, label, description, optionType } = props; + + const debounce = useService('debounceInput'); + const eventBus = useService('eventBus'); + + const getValue = () => { + const value = (element.businessObject.dataStoreRef) ? element.businessObject.dataStoreRef.id : ''; + return value; + }; + + const setValue = (value) => { + if(!value || value == ''){ + modeling.updateProperties(element, { + dataStoreRef: null + }); + return; + } + const dataStore = bpmnFactory.create('bpmn:DataStore', { + id: value, + name: 'DataStore_' + value + }); + modeling.updateProperties(element, { + dataStoreRef: dataStore + }); + }; + + if ( + !(optionType in spiffExtensionOptions) || + spiffExtensionOptions[optionType] === null + ) { + spiffExtensionOptions[optionType] = null; + requestOptions(eventBus, element, commandStack, optionType); + } + + const getOptions = () => { + const optionList = []; + optionList.push({ + label: '', + value: '', + }); + if ( + optionType in spiffExtensionOptions && + spiffExtensionOptions[optionType] !== null + ) { + spiffExtensionOptions[optionType].forEach((opt) => { + optionList.push({ + label: opt.name, + value: opt.name, + }); + }); + } + return optionList; + }; + + return SelectEntry({ + id, + element, + label, + description, + getValue, + setValue, + getOptions, + debounce, + }); +} + +function requestOptions(eventBus, element, commandStack, optionType) { + eventBus.on(`spiff.${optionType}.returned`, (event) => { + spiffExtensionOptions[optionType] = event.options; + }); + eventBus.fire(`spiff.${optionType}.requested`, { eventBus }); +} + diff --git a/app/spiffworkflow/index.js b/app/spiffworkflow/index.js index 5ab4feb..b5a856d 100644 --- a/app/spiffworkflow/index.js +++ b/app/spiffworkflow/index.js @@ -6,6 +6,7 @@ import DataObjectInterceptor from './DataObject/DataObjectInterceptor'; import DataObjectRules from './DataObject/DataObjectRules'; import DataObjectRenderer from './DataObject/DataObjectRenderer'; import DataObjectPropertiesProvider from './DataObject/propertiesPanel/DataObjectPropertiesProvider'; +import CustomDataStorePropertiesProvider from './DataStoreReference/propertiesPanel/CustomDataStorePropertiesProvider'; import ConditionsPropertiesProvider from './conditions/propertiesPanel/ConditionsPropertiesProvider'; import ExtensionsPropertiesProvider from './extensions/propertiesPanel/ExtensionsPropertiesProvider'; import MessagesPropertiesProvider from './messages/propertiesPanel/MessagesPropertiesProvider'; @@ -22,6 +23,7 @@ export default { 'dataObjectInterceptor', 'dataObjectRules', 'dataObjectPropertiesProvider', + 'dataSourcePropertiesProvider', 'conditionsPropertiesProvider', 'extensionsPropertiesProvider', 'messagesPropertiesProvider', @@ -40,6 +42,7 @@ export default { dataObjectRules: ['type', DataObjectRules], dataObjectRenderer: ['type', DataObjectRenderer], dataObjectPropertiesProvider: ['type', DataObjectPropertiesProvider], + dataSourcePropertiesProvider: ['type', CustomDataStorePropertiesProvider], conditionsPropertiesProvider: ['type', ConditionsPropertiesProvider], extensionsPropertiesProvider: ['type', ExtensionsPropertiesProvider], signalPropertiesProvider: ['type', SignalPropertiesProvider], diff --git a/test/spec/DataStoreReferenceSpec.js b/test/spec/DataStoreReferenceSpec.js new file mode 100644 index 0000000..dae6ef5 --- /dev/null +++ b/test/spec/DataStoreReferenceSpec.js @@ -0,0 +1,111 @@ +import TestContainer from 'mocha-test-container-support'; +import { + BpmnPropertiesPanelModule, + BpmnPropertiesProviderModule, +} from 'bpmn-js-properties-panel'; +import { getBusinessObject } from 'bpmn-js/lib/util/ModelUtil'; +import { + bootstrapPropertiesPanel, + changeInput, + expectSelected, + findGroupEntry, + findEntry, + findSelect, + getPropertiesPanel +} from './helpers'; + +import { getBpmnJS } from 'bpmn-js/test/helper'; + +import spiffModdleExtension from '../../app/spiffworkflow/moddle/spiffworkflow.json'; +import DataStoreReference from '../../app/spiffworkflow/DataStoreReference'; + +const return_datastores = (event) => { + event.eventBus.fire('spiff.data_stores.returned', { + options: [ + { type: 'typeahead', name: 'countries' }, + { type: 'kkv', name: 'foods' } + ], + }); +} + + +describe('Data Source Reference Test cases', function () { + const xml = require('./bpmn/data_store.bpmn').default; + let container; + + beforeEach(function () { + container = TestContainer.get(this); + }); + + beforeEach( + bootstrapPropertiesPanel(xml, { + container, + debounceInput: false, + additionalModules: [ + DataStoreReference, + BpmnPropertiesPanelModule, + BpmnPropertiesProviderModule, + ], + moddleExtensions: { + spiffworkflow: spiffModdleExtension, + }, + }) + ); + + it('should display the custom data store properties group - DataStoreReference element', async function () { + // We Select a DataStoreReference element + const shapeElement = await expectSelected('DataStoreReference_0eqeh4p'); + expect(shapeElement, "I can't find DataStoreReference element").to.exist; + + // Lets Check if the custom properties group is displayed + const customGroup = findGroupEntry('custom-datastore-properties', container); + expect(customGroup).to.exist; + const entry = findEntry('selectDataStore', container); + expect(entry).to.exist; + }); + + it('should list data sources from aa api response and append it to select - DataStoreReference element', async function () { + const modeler = getBpmnJS(); + modeler.get('eventBus').once('spiff.data_stores.requested', return_datastores); + + // We Select a DataStoreReference element + const shapeElement = await expectSelected('DataStoreReference_0eqeh4p'); + expect(shapeElement, "I can't find DataStoreReference element").to.exist; + + // Interact with the DataStoreSelect component + const selectGroup = findGroupEntry('custom-datastore-properties', container) + expect(selectGroup).to.exist; + + const entry = findEntry('selectDataStore', getPropertiesPanel()); + expect(entry).to.exist; + + // Verification if the dataStoreRef attribute is updated + let selector = findSelect(entry); + expect(selector.length).to.equal(3); + expect(selector[1].value === 'countries'); + expect(selector[2].value === 'foods'); + }); + + it('should update dataStoreRef after a select event - DataStoreReference element', async function () { + const modeler = getBpmnJS(); + modeler.get('eventBus').once('spiff.data_stores.requested', return_datastores); + + // We Select a DataStoreReference element + const shapeElement = await expectSelected('DataStoreReference_0eqeh4p'); + expect(shapeElement, "I can't find DataStoreReference element").to.exist; + + // Interact with the DataStoreSelect component + const selectGroup = findGroupEntry('custom-datastore-properties', container) + expect(selectGroup).to.exist; + + const entry = findEntry('selectDataStore', getPropertiesPanel()); + expect(entry).to.exist; + + // Verification if the dataStoreRef attribute is updated + let selector = findSelect(entry); + changeInput(selector, 'foods'); + const nwbusinessObject = getBusinessObject(shapeElement); + expect(nwbusinessObject.get('dataStoreRef').id).to.equal('foods'); + }); + +}); diff --git a/test/spec/bpmn/data_store.bpmn b/test/spec/bpmn/data_store.bpmn new file mode 100644 index 0000000..9adc30b --- /dev/null +++ b/test/spec/bpmn/data_store.bpmn @@ -0,0 +1,62 @@ + + + + + Flow_0vt1twq + + + Flow_1udyjxo + + + + + + + + + + + + + + + + + Flow_0vt1twq + Flow_1udyjxo + + DataStoreReference_0eqeh4p + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +