Merge pull request #18 from sartography/feature/service_result

Allow setting the variable that will contain the result of a service call
This commit is contained in:
Dan Funk 2022-10-11 10:25:40 -04:00 committed by GitHub
commit e7be00a6e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 232 additions and 30 deletions

View File

@ -6,7 +6,7 @@ import { SpiffExtensionTextInput } from './SpiffExtensionTextInput';
import { SpiffExtensionInstructionsForEndUser } from './SpiffExtensionInstructionsForEndUser';
import {
ServiceTaskParameterArray,
ServiceTaskOperatorSelect,
ServiceTaskOperatorSelect, ServiceTaskResultTextInput,
} from './SpiffExtensionServiceProperties';
const LOW_PRIORITY = 500;
@ -230,6 +230,13 @@ function createServiceGroup(element, translate, moddle, commandStack) {
component: ServiceTaskOperatorSelect,
translate,
},
{
element,
moddle,
commandStack,
component: ServiceTaskResultTextInput,
translate,
},
{
id: 'serviceTaskParameters',
label: translate('Parameters'),

View File

@ -1,7 +1,7 @@
import {useService } from 'bpmn-js-properties-panel';
import { useService } from 'bpmn-js-properties-panel';
import { TextFieldEntry } from '@bpmn-io/properties-panel';
const SPIFF_PROP = "spiffworkflow:calledDecisionId"
const SPIFF_PROP = 'spiffworkflow:calledDecisionId';
/**
* A generic properties' editor for text input.
@ -19,33 +19,34 @@ const SPIFF_PROP = "spiffworkflow:calledDecisionId"
* @returns {string|null|*}
*/
export function SpiffExtensionCalledDecision(props) {
const element = props.element;
const commandStack = props.commandStack, moddle = props.moddle;
const label = props.label, description = props.description;
const { element } = props;
const { commandStack } = props;
const { moddle } = props;
const { label } = props;
const { description } = props;
const debounce = useService('debounceInput');
const getPropertyObject = () => {
const bizObj = element.businessObject;
if (!bizObj.extensionElements) {
return null;
} else {
return bizObj.extensionElements.get("values").filter(function (e) {
return e.$instanceOf(SPIFF_PROP)
})[0];
}
}
return bizObj.extensionElements.get('values').filter(function (e) {
return e.$instanceOf(SPIFF_PROP);
})[0];
};
const getValue = () => {
const property = getPropertyObject()
const property = getPropertyObject();
if (property) {
return property.calledDecisionId;
}
return ""
}
return '';
};
const setValue = value => {
let property = getPropertyObject()
let businessObject = element.businessObject;
const setValue = (value) => {
let property = getPropertyObject();
const { businessObject } = element;
let extensions = businessObject.extensionElements;
if (!property) {
@ -61,19 +62,20 @@ export function SpiffExtensionCalledDecision(props) {
element,
moddleElement: businessObject,
properties: {
"extensionElements": extensions
}
extensionElements: extensions,
},
});
};
return <TextFieldEntry
id='extension_called_decision'
element={element}
description={description}
label={label}
getValue={getValue}
setValue={setValue}
debounce={debounce}
/>;
return (
<TextFieldEntry
id="extension_called_decision"
element={element}
description={description}
label={label}
getValue={getValue}
setValue={setValue}
debounce={debounce}
/>
);
}

View File

@ -25,7 +25,7 @@ const SERVICE_TASK_PARAMETER_ELEMENT_NAME = `${SPIFFWORKFLOW_XML_NAMESPACE}:para
*
<bpmn:serviceTask id="service_task_one" name="Service Task One">
<bpmn:extensionElements>
<spiffworkflow:serviceTaskOperator id="SlackWebhookOperator">
<spiffworkflow:serviceTaskOperator id="SlackWebhookOperator" resultVariable="result">
<spiffworkflow:parameters>
<spiffworkflow:parameter name="webhook_token" type="string" value="token" />
<spiffworkflow:parameter name="message" type="string" value="ServiceTask testing" />
@ -247,3 +247,43 @@ function ServiceTaskParameterTextField(props) {
debounce,
});
}
export function ServiceTaskResultTextInput(props) {
const { element, translate, commandStack } = props;
const debounce = useService('debounceInput');
const serviceTaskOperatorModdleElement =
getServiceTaskOperatorModdleElement(element);
const setValue = (value) => {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: serviceTaskOperatorModdleElement,
properties: {
resultVariable: value,
},
});
};
const getValue = () => {
if (serviceTaskOperatorModdleElement) {
return serviceTaskOperatorModdleElement.resultVariable;
}
return '';
};
if (serviceTaskOperatorModdleElement) {
return TextFieldEntry({
element,
label: translate('Response Variable'),
description: translate(
'response will be saved to this variable. Leave empty to discard the response.'
),
id: `result-textField`,
getValue,
setValue,
debounce,
});
}
return null;
}

View File

@ -110,6 +110,11 @@
"isAttr": true,
"type": "String"
},
{
"name": "resultVariable",
"isAttr": true,
"type": "String"
},
{
"name": "parameterList",
"type": "parameters"

View File

@ -0,0 +1,109 @@
import {
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
} from 'bpmn-js-properties-panel';
import { getBusinessObject } from 'bpmn-js/lib/util/ModelUtil';
import TestContainer from 'mocha-test-container-support';
import { getBpmnJS } from 'bpmn-js/test/helper';
import spiffModdleExtension from '../../app/spiffworkflow/moddle/spiffworkflow.json';
import {
bootstrapPropertiesPanel,
changeInput,
expectSelected,
findEntry,
findGroupEntry,
findInput, findSelect,
} from './helpers';
import extensions from '../../app/spiffworkflow/extensions';
describe('Properties Panel for Service Tasks', function () {
const diagramXml = require('./bpmn/service.bpmn').default;
let container;
beforeEach(function () {
container = TestContainer.get(this);
});
function preparePropertiesPanelWithXml(xml) {
return bootstrapPropertiesPanel(xml, {
container,
debounceInput: false,
additionalModules: [
extensions,
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
],
moddleExtensions: {
spiffworkflow: spiffModdleExtension,
},
});
}
function addServicesToModeler(bpmnModeler) {
/**
* This will inject available services into the modeler which should be
* available as a dropdown list when selecting which service you want to call.
*
*/
bpmnModeler.on('spiff.service_tasks.requested', (event) => {
event.eventBus.fire('spiff.service_tasks.returned', {
serviceTaskOperators: [
{
id: 'ExampleService',
parameters: [
{
id: 'name',
type: 'string',
},
],
},
{
id: 'ExampleService2',
parameters: [
{
id: 'number',
type: 'integer',
},
],
},
],
});
});
}
it('should display a panel for selecting service', async function () {
await preparePropertiesPanelWithXml(diagramXml)();
const modeler = getBpmnJS();
addServicesToModeler(modeler);
// IF - you select a service task
const serviceTask = await expectSelected('my_service_task');
expect(serviceTask).to.exist;
// THEN - a property panel exists with a section for editing web forms
const group = findGroupEntry('service_task_properties', container);
expect(group).to.exist;
});
it('should display a list of services to select from.', async function () {
await preparePropertiesPanelWithXml(diagramXml)();
const modeler = getBpmnJS();
addServicesToModeler(modeler);
// IF - you select a service task
const serviceTask = await expectSelected('my_service_task');
const group = findGroupEntry('service_task_properties', container);
const entry = findEntry('selectOperatorId', group)
// THEN - a select list appears and is populated by a list of known services
const selectList = findSelect(entry);
expect(selectList).to.exist;
expect(selectList.options.length).to.equal(2)
expect(selectList.options[0].label).to.equal('ExampleService')
expect(selectList.options[1].label).to.equal('ExampleService2')
});
});

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_116xv2e" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.0.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.17.0">
<bpmn:process id="Process_0z3z6vd" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_09rugkh</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_09rugkh" sourceRef="StartEvent_1" targetRef="my_service_task" />
<bpmn:endEvent id="Event_1tb182h">
<bpmn:incoming>Flow_0kql76n</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0kql76n" sourceRef="my_service_task" targetRef="Event_1tb182h" />
<bpmn:serviceTask id="my_service_task" name="My Service Task">
<bpmn:incoming>Flow_09rugkh</bpmn:incoming>
<bpmn:outgoing>Flow_0kql76n</bpmn:outgoing>
</bpmn:serviceTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0z3z6vd">
<bpmndi:BPMNEdge id="Flow_09rugkh_di" bpmnElement="Flow_09rugkh">
<di:waypoint x="215" y="117" />
<di:waypoint x="270" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0kql76n_di" bpmnElement="Flow_0kql76n">
<di:waypoint x="370" y="117" />
<di:waypoint x="432" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1tb182h_di" bpmnElement="Event_1tb182h">
<dc:Bounds x="432" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_04smezy_di" bpmnElement="my_service_task">
<dc:Bounds x="270" y="77" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>