Squashed 'bpmn-js-spiffworkflow/' changes from 9dcca6c80..6007a770a

6007a770a fix typo
aa524120b Merge pull request #47 from sartography/dependabot/github_actions/crazy-max/ghaction-github-labeler-5.0.0
75556c020 Bump crazy-max/ghaction-github-labeler from 4.2.0 to 5.0.0
9a5c333de Merge pull request #46 from sartography/feature/better_form_nav
c6c955284 Merge pull request #43 from sartography/bugfix/update-data-input-output-specs
094c573e0 fixing a test.
5fcb47125 Assume established naming conventions over requiring a form schema and ui schema each time as separate inputs. Making this one dropdown for the user form instead of two.
53f45dbaa Merge pull request #45 from sartography/dependabot/github_actions/actions/checkout-4
67765a994 Bump actions/checkout from 3 to 4
dbcd66942 Merge pull request #44 from sartography/dependabot/github_actions/crazy-max/ghaction-github-labeler-4.2.0
b8be0e335 Bump crazy-max/ghaction-github-labeler from 4.1.0 to 4.2.0
7743372d2 update iospec to include refs in input/output sets
6e17bda37 use same version of bpmn-js as arena
2c7fca88f missed a type in spiffworkflow.json
d62f021e3 Merge pull request #42 from sartography/bugfix/lowercase-overridden-tags
c625980c5 update extension names
e92ae2f5a make sure tags with extensions are written to xml correctly
eb37b6d29 Merge pull request #41 from sartography/feature/guest_form_submission
1b390c46c added panel extensions for guest access to human tasks w/ burnettk
f01bece04 Merge pull request #40 from sartography/feature/multiinstance-improvements
b8179146b allow attaching pre/post scripts to MI task or instance tasks

git-subtree-dir: bpmn-js-spiffworkflow
git-subtree-split: 6007a770a5938c993173001a013116cedfc80359
This commit is contained in:
burnettk 2023-10-14 12:30:16 -04:00
parent 7db152f010
commit 392c9a1b44
24 changed files with 4186 additions and 9625 deletions

View File

@ -11,9 +11,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out the repository - name: Check out the repository
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Run Labeler - name: Run Labeler
uses: crazy-max/ghaction-github-labeler@v4.1.0 uses: crazy-max/ghaction-github-labeler@v5.0.0
with: with:
skip-delete: true skip-delete: true

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 #Checkout Repo - uses: actions/checkout@v4 #Checkout Repo
- uses: actions/setup-node@v3 #Setup Node - uses: actions/setup-node@v3 #Setup Node
with: with:
node-version: 18 node-version: 18

View File

@ -18,7 +18,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 #Checkout Repo - uses: actions/checkout@v4 #Checkout Repo
- uses: actions/setup-node@v3 #Setup Node - uses: actions/setup-node@v3 #Setup Node
- uses: nanasess/setup-chromedriver@v2 #Setup ChromeDriver - uses: nanasess/setup-chromedriver@v2 #Setup ChromeDriver
with: with:

View File

@ -160,8 +160,8 @@ bpmnModeler.on('spiff.dmn.edit', (newEvent) => {
* Also handy to get a list of available files that can be used in a given * Also handy to get a list of available files that can be used in a given
* context, say json files for a form, or a DMN file for a BusinessRuleTask * context, say json files for a form, or a DMN file for a BusinessRuleTask
*/ */
bpmnModeler.on('spiff.json_files.requested', (event) => { bpmnModeler.on('spiff.json_schema_files.requested', (event) => {
event.eventBus.fire('spiff.json_files.returned', { event.eventBus.fire('spiff.json_schema_files.returned', {
options: [ options: [
{ label: 'pizza_form.json', value: 'pizza_form.json' }, { label: 'pizza_form.json', value: 'pizza_form.json' },
{ label: 'credit_card_form.json', value: 'credit_card_form.json' }, { label: 'credit_card_form.json', value: 'credit_card_form.json' },

View File

@ -30,7 +30,7 @@ export default class IoInterceptor extends CommandInterceptor {
let process = context.parent.businessObject; let process = context.parent.businessObject;
let ioSpec = assureIOSpecificationExists(process, bpmnFactory); let ioSpec = assureIOSpecificationExists(process, bpmnFactory);
let di = context.shape.di; let di = context.shape.di;
let generator = new IdGenerator(type_name), ioSpecification = process.get('ioSpecification'); let generator = new IdGenerator(type_name);
let dataIO = bpmnFactory.create(type, { id: generator.next() }); let dataIO = bpmnFactory.create(type, { id: generator.next() });
context.shape.businessObject = dataIO; context.shape.businessObject = dataIO;
dataIO.$parent = ioSpec; dataIO.$parent = ioSpec;
@ -40,9 +40,11 @@ export default class IoInterceptor extends CommandInterceptor {
bpmnUpdater.updateBounds(context.shape); bpmnUpdater.updateBounds(context.shape);
if (type == 'bpmn:DataInput') { if (type == 'bpmn:DataInput') {
collectionAdd(ioSpecification.get('dataInputs'), dataIO); collectionAdd(ioSpec.inputSets[0].get('dataInputRefs'), dataIO);
collectionAdd(ioSpec.get('dataInputs'), dataIO);
} else { } else {
collectionAdd(ioSpecification.get('dataOutputs'), dataIO); collectionAdd(ioSpec.outputSets[0].get('dataOutputRefs'), dataIO);
collectionAdd(ioSpec.get('dataOutputs'), dataIO);
} }
} }
}); });
@ -54,8 +56,10 @@ export default class IoInterceptor extends CommandInterceptor {
let process = context.shape.parent.businessObject; let process = context.shape.parent.businessObject;
let ioSpec = assureIOSpecificationExists(process, bpmnFactory); let ioSpec = assureIOSpecificationExists(process, bpmnFactory);
if (type == 'bpmn:DataInput') { if (type == 'bpmn:DataInput') {
collectionRemove(ioSpec.inputSets[0].get('dataInputRefs'), context.shape.businessObject);
collectionRemove(ioSpec.get('dataInputs'), context.shape.businessObject); collectionRemove(ioSpec.get('dataInputs'), context.shape.businessObject);
} else { } else {
collectionRemove(ioSpec.outputSets[0].get('dataOutputRefs'), context.shape.businessObject);
collectionRemove(ioSpec.get('dataOutputs'), context.shape.businessObject); collectionRemove(ioSpec.get('dataOutputs'), context.shape.businessObject);
} }
if (context.shape.di.$parent) { if (context.shape.di.$parent) {

View File

@ -148,7 +148,7 @@ function getTextFieldForExtension(eventDetails, label, description, catching) {
const debounce = useService('debounceInput'); const debounce = useService('debounceInput');
const translate = useService('translate'); const translate = useService('translate');
const root = getRoot(element.businessObject); const root = getRoot(element.businessObject);
const extensionName = (catching) ? 'spiffworkflow:variableName' : 'spiffworkflow:payloadExpression'; const extensionName = (catching) ? 'spiffworkflow:VariableName' : 'spiffworkflow:PayloadExpression';
const getEvent = () => { const getEvent = () => {
const eventDef = element.businessObject.eventDefinitions.find(v => v.$type == eventDefType); const eventDef = element.businessObject.eventDefinitions.find(v => v.$type == eventDefType);

View File

@ -1,5 +1,5 @@
const SPIFF_PARENT_PROP = 'spiffworkflow:properties'; const SPIFF_PARENT_PROP = 'spiffworkflow:Properties';
const SPIFF_PROP = 'spiffworkflow:property'; const SPIFF_PROP = 'spiffworkflow:Property';
const PREFIX = 'spiffworkflow:'; const PREFIX = 'spiffworkflow:';
/** /**

View File

@ -1,16 +1,22 @@
import { ListGroup } from '@bpmn-io/properties-panel'; import {
ListGroup,
CheckboxEntry,
isCheckboxEntryEdited,
} from '@bpmn-io/properties-panel';
import { is, isAny } from 'bpmn-js/lib/util/ModelUtil'; import { is, isAny } from 'bpmn-js/lib/util/ModelUtil';
import scriptGroup, { SCRIPT_TYPE } from './SpiffScriptGroup'; import scriptGroup, { SCRIPT_TYPE } from './SpiffScriptGroup';
import { import {
ServiceTaskParameterArray, ServiceTaskParameterArray,
ServiceTaskOperatorSelect, ServiceTaskResultTextInput, ServiceTaskOperatorSelect, ServiceTaskResultTextInput,
} from './SpiffExtensionServiceProperties'; } from './SpiffExtensionServiceProperties';
import {OPTION_TYPE, SpiffExtensionSelect} from './SpiffExtensionSelect'; import {OPTION_TYPE, spiffExtensionOptions, SpiffExtensionSelect} from './SpiffExtensionSelect';
import {SpiffExtensionLaunchButton} from './SpiffExtensionLaunchButton'; import {SpiffExtensionLaunchButton} from './SpiffExtensionLaunchButton';
import {SpiffExtensionTextArea} from './SpiffExtensionTextArea'; import {SpiffExtensionTextArea} from './SpiffExtensionTextArea';
import {SpiffExtensionTextInput} from './SpiffExtensionTextInput'; import {SpiffExtensionTextInput} from './SpiffExtensionTextInput';
import {SpiffExtensionCheckboxEntry} from './SpiffExtensionCheckboxEntry';
import {hasEventDefinition} from 'bpmn-js/lib/util/DiUtil'; import {hasEventDefinition} from 'bpmn-js/lib/util/DiUtil';
import { PropertyDescription } from 'bpmn-js-properties-panel/'; import { PropertyDescription } from 'bpmn-js-properties-panel/';
import {setExtensionValue} from "../extensionHelpers";
const LOW_PRIORITY = 500; const LOW_PRIORITY = 500;
@ -57,6 +63,16 @@ export default function ExtensionsPropertiesProvider(
createUserInstructionsGroup(element, translate, moddle, commandStack) createUserInstructionsGroup(element, translate, moddle, commandStack)
); );
} }
if (
isAny(element, [
'bpmn:ManualTask',
'bpmn:UserTask',
])
) {
groups.push(
createAllowGuestGroup(element, translate, moddle, commandStack)
);
}
if ( if (
is(element, 'bpmn:BoundaryEvent') && is(element, 'bpmn:BoundaryEvent') &&
hasEventDefinition(element, 'bpmn:SignalEventDefinition') && hasEventDefinition(element, 'bpmn:SignalEventDefinition') &&
@ -117,32 +133,69 @@ function createScriptGroup(element, translate, moddle, commandStack) {
* @returns The components to add to the properties panel. * @returns The components to add to the properties panel.
*/ */
function preScriptPostScriptGroup(element, translate, moddle, commandStack) { function preScriptPostScriptGroup(element, translate, moddle, commandStack) {
const entries = [
...scriptGroup({
element,
moddle,
commandStack,
translate,
scriptType: SCRIPT_TYPE.pre,
label: 'Pre-Script',
description: 'code to execute prior to this task.',
}),
...scriptGroup({
element,
moddle,
commandStack,
translate,
scriptType: SCRIPT_TYPE.post,
label: 'Post-Script',
description: 'code to execute after this task.',
}),
];
const loopCharacteristics = element.businessObject.loopCharacteristics;
if (typeof(loopCharacteristics) !== 'undefined') {
entries.push({
id: 'scriptValence',
component: ScriptValenceCheckbox,
isEdited: isCheckboxEntryEdited,
commandStack,
});
}
return { return {
id: 'spiff_pre_post_scripts', id: 'spiff_pre_post_scripts',
label: translate('Pre/Post Scripts'), label: translate('Pre/Post Scripts'),
entries: [ entries: entries,
...scriptGroup({
element,
moddle,
commandStack,
translate,
scriptType: SCRIPT_TYPE.pre,
label: 'Pre-Script',
description: 'code to execute prior to this task.',
}),
...scriptGroup({
element,
moddle,
commandStack,
translate,
scriptType: SCRIPT_TYPE.post,
label: 'Post-Script',
description: 'code to execute after this task.',
}),
],
}; };
} }
function ScriptValenceCheckbox(props) {
const { element, commandStack } = props;
const getValue = () => {
return element.businessObject.loopCharacteristics.scriptsOnInstances;
};
const setValue = (value) => {
const loopCharacteristics = element.businessObject.loopCharacteristics;
loopCharacteristics.scriptsOnInstances = value || undefined;
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: loopCharacteristics,
});
};
return CheckboxEntry({
element,
id: 'selectScriptValence',
label: 'Run scripts on instances',
description: 'By default, scripts will attach to the multiinstance task',
getValue,
setValue,
});
}
/** /**
* Create a group on the main panel with a select box (for choosing the Data Object to connect) * Create a group on the main panel with a select box (for choosing the Data Object to connect)
* @param element * @param element
@ -151,6 +204,17 @@ function preScriptPostScriptGroup(element, translate, moddle, commandStack) {
* @returns entries * @returns entries
*/ */
function createUserGroup(element, translate, moddle, commandStack) { function createUserGroup(element, translate, moddle, commandStack) {
const updateExtensionProperties = (element, name, value, moddle, commandStack) => {
const uiName = value.replace('schema\.json', 'uischema\.json')
setExtensionValue(element, 'formJsonSchemaFilename', value, moddle, commandStack);
setExtensionValue(element, 'formUiSchemaFilename', uiName, moddle, commandStack);
const matches = spiffExtensionOptions[OPTION_TYPE.json_schema_files].filter((opt) => opt.value === value);
if (matches.length === 0) {
spiffExtensionOptions[OPTION_TYPE.json_schema_files].push({label: value, value: value});
}
}
return { return {
id: 'user_task_properties', id: 'user_task_properties',
label: translate('Web Form (with Json Schemas)'), label: translate('Web Form (with Json Schemas)'),
@ -160,7 +224,7 @@ function createUserGroup(element, translate, moddle, commandStack) {
moddle, moddle,
commandStack, commandStack,
component: SpiffExtensionSelect, component: SpiffExtensionSelect,
optionType: OPTION_TYPE.json_files, optionType: OPTION_TYPE.json_schema_files,
name: 'formJsonSchemaFilename', name: 'formJsonSchemaFilename',
label: translate('JSON Schema Filename'), label: translate('JSON Schema Filename'),
description: translate('Form Description (RSJF)'), description: translate('Form Description (RSJF)'),
@ -168,29 +232,14 @@ function createUserGroup(element, translate, moddle, commandStack) {
{ {
component: SpiffExtensionLaunchButton, component: SpiffExtensionLaunchButton,
element, element,
moddle,
commandStack,
name: 'formJsonSchemaFilename', name: 'formJsonSchemaFilename',
label: translate('Launch Editor'), label: translate('Launch Editor'),
event: 'spiff.file.edit', event: 'spiff.file.edit',
description: translate('Edit the form description'), listenEvent: 'spiff.jsonSchema.update',
}, listenFunction: updateExtensionProperties,
{ description: translate('Edit the form schema')
element,
moddle,
commandStack,
component: SpiffExtensionSelect,
optionType: OPTION_TYPE.json_files,
label: translate('UI Schema Filename'),
event: 'spiff.file.edit',
description: translate('Rules for displaying the form. (RSJF Schema)'),
name: 'formUiSchemaFilename',
},
{
component: SpiffExtensionLaunchButton,
element,
name: 'formUiSchemaFilename',
label: translate('Launch Editor'),
event: 'spiff.file.edit',
description: translate('Edit the form schema'),
}, },
], ],
}; };
@ -216,14 +265,14 @@ function createBusinessRuleGroup(element, translate, moddle, commandStack) {
commandStack, commandStack,
component: SpiffExtensionSelect, component: SpiffExtensionSelect,
optionType: OPTION_TYPE.dmn_files, optionType: OPTION_TYPE.dmn_files,
name: 'spiffworkflow:calledDecisionId', name: 'spiffworkflow:CalledDecisionId',
label: translate('Select Decision Table'), label: translate('Select Decision Table'),
description: translate('Select a decision table from the list'), description: translate('Select a decision table from the list'),
}, },
{ {
element, element,
component: SpiffExtensionLaunchButton, component: SpiffExtensionLaunchButton,
name: 'spiffworkflow:calledDecisionId', name: 'spiffworkflow:CalledDecisionId',
label: translate('Launch Editor'), label: translate('Launch Editor'),
event: 'spiff.dmn.edit', event: 'spiff.dmn.edit',
description: translate('Modify the Decision Table'), description: translate('Modify the Decision Table'),
@ -254,7 +303,7 @@ function createUserInstructionsGroup (
moddle, moddle,
commandStack, commandStack,
component: SpiffExtensionTextArea, component: SpiffExtensionTextArea,
name: 'spiffworkflow:instructionsForEndUser', name: 'spiffworkflow:InstructionsForEndUser',
label: 'Instructions', label: 'Instructions',
description: 'Displayed above user forms or when this task is executing.', description: 'Displayed above user forms or when this task is executing.',
}, },
@ -263,7 +312,7 @@ function createUserInstructionsGroup (
moddle, moddle,
commandStack, commandStack,
component: SpiffExtensionLaunchButton, component: SpiffExtensionLaunchButton,
name: 'spiffworkflow:instructionsForEndUser', name: 'spiffworkflow:InstructionsForEndUser',
label: translate('Launch Editor'), label: translate('Launch Editor'),
event: 'spiff.markdown.edit', event: 'spiff.markdown.edit',
listenEvent: 'spiff.markdown.update', listenEvent: 'spiff.markdown.update',
@ -273,6 +322,55 @@ function createUserInstructionsGroup (
}; };
} }
/**
* Create a group on the main panel with a text box (for choosing the information to display to the user)
* @param element
* @param translate
* @param moddle
* @returns entries
*/
function createAllowGuestGroup (
element,
translate,
moddle,
commandStack
) {
return {
id: 'allow_guest_user',
label: translate('Guest options'),
entries: [
{
element,
moddle,
commandStack,
component: SpiffExtensionCheckboxEntry,
name: 'spiffworkflow:AllowGuest',
label: 'Guest can complete this task',
description: 'Allow a guest user to complete this task without logging in. They will not be allowed to do anything but submit this task. If another task directly follows it that allows guest access, they could also complete that task.',
},
{
element,
moddle,
commandStack,
component: SpiffExtensionTextArea,
name: 'spiffworkflow:GuestConfirmation',
label: 'Guest confirmation',
description: 'This is markdown that is displayed to the user after they complete the task. If this is filled out then the user will not be able to complete additional tasks without a new link to the next task.',
},
{
element,
moddle,
commandStack,
component: SpiffExtensionLaunchButton,
name: 'spiffworkflow:GuestConfirmation',
label: translate('Launch Editor'),
event: 'spiff.markdown.edit',
listenEvent: 'spiff.markdown.update',
}
],
};
}
/** /**
* Create a group on the main panel with a text box for specifying a * Create a group on the main panel with a text box for specifying a
* a Button Label that is associated with a signal event.) * a Button Label that is associated with a signal event.)
@ -299,7 +397,7 @@ function createSignalButtonGroup (
moddle, moddle,
commandStack, commandStack,
component: SpiffExtensionTextInput, component: SpiffExtensionTextInput,
name: 'spiffworkflow:signalButtonLabel', name: 'spiffworkflow:SignalButtonLabel',
label: 'Button Label', label: 'Button Label',
description: description description: description
}, },

View File

@ -16,7 +16,7 @@ const getScriptUnitTestsModdleElement = (shapeElement) => {
return bizObj.extensionElements return bizObj.extensionElements
.get('values') .get('values')
.filter(function getInstanceOfType(e) { .filter(function getInstanceOfType(e) {
return e.$instanceOf('spiffworkflow:unitTests'); return e.$instanceOf('spiffworkflow:UnitTests');
})[0]; })[0];
}; };
@ -73,19 +73,19 @@ export function ScriptUnitTestArray(props) {
if (!scriptUnitTestsModdleElement) { if (!scriptUnitTestsModdleElement) {
scriptUnitTestsModdleElement = scriptTaskModdleElement.$model.create( scriptUnitTestsModdleElement = scriptTaskModdleElement.$model.create(
'spiffworkflow:unitTests' 'spiffworkflow:UnitTests'
); );
scriptTaskModdleElement.extensionElements scriptTaskModdleElement.extensionElements
.get('values') .get('values')
.push(scriptUnitTestsModdleElement); .push(scriptUnitTestsModdleElement);
} }
const scriptUnitTestModdleElement = scriptTaskModdleElement.$model.create( const scriptUnitTestModdleElement = scriptTaskModdleElement.$model.create(
'spiffworkflow:unitTest' 'spiffworkflow:UnitTest'
); );
const scriptUnitTestInputModdleElement = const scriptUnitTestInputModdleElement =
scriptTaskModdleElement.$model.create('spiffworkflow:inputJson'); scriptTaskModdleElement.$model.create('spiffworkflow:InputJson');
const scriptUnitTestOutputModdleElement = const scriptUnitTestOutputModdleElement =
scriptTaskModdleElement.$model.create('spiffworkflow:expectedOutputJson'); scriptTaskModdleElement.$model.create('spiffworkflow:ExpectedOutputJson');
scriptUnitTestModdleElement.id = moddle.ids.nextPrefixed('ScriptUnitTest_'); scriptUnitTestModdleElement.id = moddle.ids.nextPrefixed('ScriptUnitTest_');
scriptUnitTestInputModdleElement.value = '{}'; scriptUnitTestInputModdleElement.value = '{}';
scriptUnitTestOutputModdleElement.value = '{}'; scriptUnitTestOutputModdleElement.value = '{}';

View File

@ -0,0 +1,36 @@
import {useService } from 'bpmn-js-properties-panel';
import {CheckboxEntry} from '@bpmn-io/properties-panel';
import {
getExtensionValue, setExtensionValue
} from '../extensionHelpers';
/**
* A generic properties' editor for text area.
*/
export function SpiffExtensionCheckboxEntry(props) {
const element = props.element;
const commandStack = props.commandStack, moddle = props.moddle;
const name = props.name, label = props.label, description = props.description;
const debounce = useService('debounceInput');
const getValue = () => {
return getExtensionValue(element, name)
}
const setValue = value => {
setExtensionValue(element, name, value, moddle, commandStack)
};
return <CheckboxEntry
id={'extension_' + name}
element={element}
description={description}
label={label}
getValue={getValue}
setValue={setValue}
debounce={debounce}
/>;
}

View File

@ -8,7 +8,7 @@ import {getExtensionValue, setExtensionValue} from '../extensionHelpers';
* update the value and send it back. * update the value and send it back.
*/ */
export function SpiffExtensionLaunchButton(props) { export function SpiffExtensionLaunchButton(props) {
const { element, name, event, listenEvent } = props; const { element, name, event, listenEvent, listenFunction } = props;
const eventBus = useService('eventBus'); const eventBus = useService('eventBus');
return HeaderButton({ return HeaderButton({
className: 'spiffworkflow-properties-panel-button', className: 'spiffworkflow-properties-panel-button',
@ -28,7 +28,11 @@ export function SpiffExtensionLaunchButton(props) {
const { commandStack, moddle } = props; const { commandStack, moddle } = props;
// Listen for a response, to update the script. // Listen for a response, to update the script.
eventBus.once(listenEvent, (response) => { eventBus.once(listenEvent, (response) => {
setExtensionValue(element, name, response.value, moddle, commandStack); if(listenFunction) {
listenFunction(element, name, response.value, moddle, commandStack);
} else {
setExtensionValue(element, name, response.value, moddle, commandStack);
}
}); });
} }

View File

@ -5,10 +5,10 @@ import {
setExtensionValue, setExtensionValue,
} from '../extensionHelpers'; } from '../extensionHelpers';
const spiffExtensionOptions = {}; export const spiffExtensionOptions = {};
export const OPTION_TYPE = { export const OPTION_TYPE = {
json_files: 'json_files', json_schema_files: 'json_schema_files',
dmn_files: 'dmn_files', dmn_files: 'dmn_files',
}; };
@ -52,6 +52,10 @@ export function SpiffExtensionSelect(props) {
} }
const getOptions = () => { const getOptions = () => {
const optionList = []; const optionList = [];
optionList.push({
label: '',
value: '',
});
if ( if (
optionType in spiffExtensionOptions && optionType in spiffExtensionOptions &&
spiffExtensionOptions[optionType] !== null spiffExtensionOptions[optionType] !== null
@ -81,7 +85,7 @@ export function SpiffExtensionSelect(props) {
function requestOptions(eventBus, element, commandStack, optionType) { function requestOptions(eventBus, element, commandStack, optionType) {
// Little backwards, but you want to assure you are ready to catch, before you throw // Little backwards, but you want to assure you are ready to catch, before you throw
// or you risk a race condition. // or you risk a race condition.
eventBus.once(`spiff.${optionType}.returned`, (event) => { eventBus.on(`spiff.${optionType}.returned`, (event) => {
spiffExtensionOptions[optionType] = event.options; spiffExtensionOptions[optionType] = event.options;
}); });
eventBus.fire(`spiff.${optionType}.requested`, { eventBus }); eventBus.fire(`spiff.${optionType}.requested`, { eventBus });

View File

@ -12,9 +12,11 @@ let serviceTaskOperators = [];
const previouslyUsedServiceTaskParameterValuesHash = {}; const previouslyUsedServiceTaskParameterValuesHash = {};
const LOW_PRIORITY = 500; const LOW_PRIORITY = 500;
const SERVICE_TASK_OPERATOR_ELEMENT_NAME = `${SPIFFWORKFLOW_XML_NAMESPACE}:serviceTaskOperator`; // I'm not going to change these variable names, but this is actually the name of the modeller
const SERVICE_TASK_PARAMETERS_ELEMENT_NAME = `${SPIFFWORKFLOW_XML_NAMESPACE}:parameters`; // type (as defined in moddle/spiffworkflow.json) NOT the element name (which is lowercase)
const SERVICE_TASK_PARAMETER_ELEMENT_NAME = `${SPIFFWORKFLOW_XML_NAMESPACE}:parameter`; const SERVICE_TASK_OPERATOR_ELEMENT_NAME = `${SPIFFWORKFLOW_XML_NAMESPACE}:ServiceTaskOperator`;
const SERVICE_TASK_PARAMETERS_ELEMENT_NAME = `${SPIFFWORKFLOW_XML_NAMESPACE}:Parameters`;
const SERVICE_TASK_PARAMETER_ELEMENT_NAME = `${SPIFFWORKFLOW_XML_NAMESPACE}:Parameter`;
/** /**
* A generic properties' editor for text input. * A generic properties' editor for text input.

View File

@ -9,8 +9,8 @@ import { ScriptUnitTestArray } from './ScriptUnitTestArray';
export const SCRIPT_TYPE = { export const SCRIPT_TYPE = {
bpmn: 'bpmn:script', bpmn: 'bpmn:script',
pre: 'spiffworkflow:preScript', pre: 'spiffworkflow:PreScript',
post: 'spiffworkflow:postScript', post: 'spiffworkflow:PostScript',
}; };
function PythonScript(props) { function PythonScript(props) {

View File

@ -18,7 +18,7 @@ export function MessagePayload(props) {
return messageElement.extensionElements return messageElement.extensionElements
.get('values') .get('values')
.filter(function getInstanceOfType(e) { .filter(function getInstanceOfType(e) {
return e.$instanceOf('spiffworkflow:messagePayload'); return e.$instanceOf('spiffworkflow:MessagePayload');
})[0]; })[0];
} }
} }
@ -37,7 +37,7 @@ export function MessagePayload(props) {
let messagePayloadObject = getMessagePayloadObject(); let messagePayloadObject = getMessagePayloadObject();
if (!messagePayloadObject) { if (!messagePayloadObject) {
messagePayloadObject = messageElement.$model.create( messagePayloadObject = messageElement.$model.create(
'spiffworkflow:messagePayload' 'spiffworkflow:MessagePayload'
); );
if (!messageElement.extensionElements) { if (!messageElement.extensionElements) {
messageElement.extensionElements = messageElement.$model.create( messageElement.extensionElements = messageElement.$model.create(

View File

@ -18,7 +18,7 @@ export function MessageVariable(props) {
return messageElement.extensionElements return messageElement.extensionElements
.get('values') .get('values')
.filter(function getInstanceOfType(e) { .filter(function getInstanceOfType(e) {
return e.$instanceOf('spiffworkflow:messageVariable'); return e.$instanceOf('spiffworkflow:MessageVariable');
})[0]; })[0];
} }
} }
@ -37,7 +37,7 @@ export function MessageVariable(props) {
let messageVariableObject = getMessageVariableObject(); let messageVariableObject = getMessageVariableObject();
if (!messageVariableObject) { if (!messageVariableObject) {
messageVariableObject = messageElement.$model.create( messageVariableObject = messageElement.$model.create(
'spiffworkflow:messageVariable' 'spiffworkflow:MessageVariable'
); );
if (!messageElement.extensionElements) { if (!messageElement.extensionElements) {
messageElement.extensionElements = messageElement.$model.create( messageElement.extensionElements = messageElement.$model.create(

View File

@ -3,9 +3,10 @@
"uri": "http://spiffworkflow.org/bpmn/schema/1.0/core", "uri": "http://spiffworkflow.org/bpmn/schema/1.0/core",
"prefix": "spiffworkflow", "prefix": "spiffworkflow",
"associations": [], "associations": [],
"xml": { "tagAlias": "lowerCase" },
"types": [ "types": [
{ {
"name": "preScript", "name": "PreScript",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -16,7 +17,7 @@
] ]
}, },
{ {
"name": "postScript", "name": "PostScript",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -27,7 +28,7 @@
] ]
}, },
{ {
"name": "messagePayload", "name": "MessagePayload",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -38,7 +39,7 @@
] ]
}, },
{ {
"name": "messageVariable", "name": "MessageVariable",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -49,7 +50,7 @@
] ]
}, },
{ {
"name": "calledDecisionId", "name": "CalledDecisionId",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -60,7 +61,7 @@
] ]
}, },
{ {
"name": "instructionsForEndUser", "name": "InstructionsForEndUser",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -71,7 +72,18 @@
] ]
}, },
{ {
"name": "signalButtonLabel", "name": "AllowGuest",
"superClass": [ "Element" ],
"properties": [
{
"name": "value",
"isBody": true,
"type": "Boolean"
}
]
},
{
"name": "GuestConfirmation",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -82,20 +94,31 @@
] ]
}, },
{ {
"name": "properties", "name": "SignalButtonLabel",
"superClass": [ "Element" ],
"properties": [
{
"name": "value",
"isBody": true,
"type": "String"
}
]
},
{
"name": "Properties",
"superClass": [ "superClass": [
"Element" "Element"
], ],
"properties": [ "properties": [
{ {
"name": "properties", "name": "properties",
"type": "property", "type": "Property",
"isMany": true "isMany": true
} }
] ]
}, },
{ {
"name": "property", "name": "Property",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -111,7 +134,7 @@
] ]
}, },
{ {
"name": "serviceTaskOperator", "name": "ServiceTaskOperator",
"superClass": [ "superClass": [
"Element" "Element"
], ],
@ -128,25 +151,25 @@
}, },
{ {
"name": "parameterList", "name": "parameterList",
"type": "parameters" "type": "Parameters"
} }
] ]
}, },
{ {
"name": "parameters", "name": "Parameters",
"superClass": [ "superClass": [
"Element" "Element"
], ],
"properties": [ "properties": [
{ {
"name": "parameters", "name": "parameters",
"type": "parameter", "type": "Parameter",
"isMany": true "isMany": true
} }
] ]
}, },
{ {
"name": "parameter", "name": "Parameter",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -167,20 +190,20 @@
] ]
}, },
{ {
"name": "unitTests", "name": "UnitTests",
"superClass": [ "superClass": [
"Element" "Element"
], ],
"properties": [ "properties": [
{ {
"name": "unitTests", "name": "unitTests",
"type": "unitTest", "type": "UnitTest",
"isMany": true "isMany": true
} }
] ]
}, },
{ {
"name": "unitTest", "name": "UnitTest",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -190,16 +213,16 @@
}, },
{ {
"name": "inputJson", "name": "inputJson",
"type": "inputJson" "type": "InputJson"
}, },
{ {
"name": "expectedOutputJson", "name": "expectedOutputJson",
"type": "expectedOutputJson" "type": "ExpectedOutputJson"
} }
] ]
}, },
{ {
"name": "inputJson", "name": "InputJson",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -210,7 +233,7 @@
] ]
}, },
{ {
"name": "expectedOutputJson", "name": "ExpectedOutputJson",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -221,7 +244,7 @@
] ]
}, },
{ {
"name": "payloadExpression", "name": "PayloadExpression",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -232,7 +255,7 @@
] ]
}, },
{ {
"name": "variableName", "name": "VariableName",
"superClass": [ "Element" ], "superClass": [ "Element" ],
"properties": [ "properties": [
{ {
@ -241,6 +264,20 @@
"type": "String" "type": "String"
} }
] ]
},
{
"name": "ScriptsOnInstances",
"extends": [
"bpmn:MultiInstanceLoopCharacteristics",
"bpmn:StandardLoopCharacteristics"
],
"properties": [
{
"name": "scriptsOnInstances",
"isAttr": true,
"type": "Boolean"
}
]
} }
] ]
} }

13344
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -73,7 +73,7 @@
"webpack-cli": "^4.9.2" "webpack-cli": "^4.9.2"
}, },
"peerDependencies": { "peerDependencies": {
"bpmn-js": "*", "bpmn-js": "^13.0.0",
"bpmn-js-properties-panel": "*", "bpmn-js-properties-panel": "*",
"diagram-js": "*" "diagram-js": "*"
}, },

View File

@ -49,7 +49,7 @@ describe('Business Rule Properties Panel', function () {
modeler.get('eventBus').once('spiff.dmn_files.requested', return_files); modeler.get('eventBus').once('spiff.dmn_files.requested', return_files);
expectSelected('business_rule_task'); expectSelected('business_rule_task');
// THEN - a properties panel exists with a section for editing that script // THEN - a properties panel exists with a section for editing that script
const entry = findEntry('extension_spiffworkflow:calledDecisionId', getPropertiesPanel()); const entry = findEntry('extension_spiffworkflow:CalledDecisionId', getPropertiesPanel());
expect(entry, 'No Entry').to.exist; expect(entry, 'No Entry').to.exist;
const selectList = findSelect(entry); const selectList = findSelect(entry);
expect(selectList, 'No Select').to.exist; expect(selectList, 'No Select').to.exist;
@ -60,7 +60,7 @@ describe('Business Rule Properties Panel', function () {
const modeler = getBpmnJS(); const modeler = getBpmnJS();
modeler.get('eventBus').once('spiff.dmn_files.requested', return_files); modeler.get('eventBus').once('spiff.dmn_files.requested', return_files);
const businessRuleTask = await expectSelected('business_rule_task'); const businessRuleTask = await expectSelected('business_rule_task');
const entry = findEntry('extension_calledDecisionId', getPropertiesPanel()); const entry = findEntry('extension_CalledDecisionId', getPropertiesPanel());
const selectList = findSelect(entry); const selectList = findSelect(entry);
changeInput(selectList, 'Decision_Pizza_Price'); changeInput(selectList, 'Decision_Pizza_Price');
@ -73,7 +73,7 @@ describe('Business Rule Properties Panel', function () {
it('should load up the xml and the value for the called decision should match the xml', async function () { it('should load up the xml and the value for the called decision should match the xml', async function () {
const businessRuleTask = await expectSelected('business_rule_task'); const businessRuleTask = await expectSelected('business_rule_task');
const entry = findEntry('extension_calledDecisionId', getPropertiesPanel()); const entry = findEntry('extension_CalledDecisionId', getPropertiesPanel());
const selectList = findSelect(entry); const selectList = findSelect(entry);
expect(selectList.value, "initial value is wrong").to.equal('test_decision'); expect(selectList.value, "initial value is wrong").to.equal('test_decision');

View File

@ -33,7 +33,8 @@ describe('Input/Output Interceptor', function() {
// THEN - the process should now have an IO Specification // THEN - the process should now have an IO Specification
const iospec = canvas.getRootElement().businessObject.ioSpecification; const iospec = canvas.getRootElement().businessObject.ioSpecification;
expect(iospec).to.not.be.null; expect(iospec).to.not.be.null;
expect(iospec.dataInputs.length).to.equal(1) expect(iospec.dataInputs.length).to.equal(1);
expect(iospec.inputSets[0].dataInputRefs.length).to.equal(1);
})); }));
@ -60,4 +61,16 @@ describe('Input/Output Interceptor', function() {
modeling.removeShape(dataInput) modeling.removeShape(dataInput)
expect(canvas.getRootElement().businessObject.ioSpecification).to.be.null; expect(canvas.getRootElement().businessObject.ioSpecification).to.be.null;
})); }));
it('deleting a data input should remove it from the input set', inject(function(canvas, modeling) {
let rootShape = canvas.getRootElement();
const dataInput = modeling.createShape({type: 'bpmn:DataInput'},
{x: 220, y: 220}, rootShape);
const dataOutput = modeling.createShape({type: 'bpmn:DataOutput'},
{x: 240, y: 220}, rootShape);
modeling.removeShape(dataInput);
const iospec = canvas.getRootElement().businessObject.ioSpecification;
expect(iospec.dataInputs.length).to.equal(0);
expect(iospec.inputSets[0].dataInputRefs.length).to.equal(0);
}));
}); });

View File

@ -80,7 +80,7 @@ describe('Messages should work', function () {
// Select the second Task // Select the second Task
const sendShape = await expectSelected('ActivitySendLetter'); const sendShape = await expectSelected('ActivitySendLetter');
expect(sendShape, "Can't find Send Task").to.exist; expect(sendShape, "Can't find Send Task").to.exist;
// THEN - there is a payload. // THEN - there is a payload.
const payload = findEntry('messagePayload', container); const payload = findEntry('messagePayload', container);
expect(payload, "Can't find the message payload").to.exist; expect(payload, "Can't find the message payload").to.exist;

View File

@ -62,10 +62,10 @@ describe('Properties Panel Script Tasks', function () {
expect(scriptInput.value).to.equal('x = 100'); expect(scriptInput.value).to.equal('x = 100');
}); });
it('should parse the bpmn:script tag when you open an existing file', async function () { it('should parse the spiffworkflow:prescript tag when you open an existing file', async function () {
await expectSelected('task_confirm'); await expectSelected('task_confirm');
const entry = findEntry( const entry = findEntry(
'pythonScript_spiffworkflow:preScript', 'pythonScript_spiffworkflow:PreScript',
PROPERTIES_PANEL_CONTAINER PROPERTIES_PANEL_CONTAINER
); );
const scriptInput = domQuery('textarea', entry); const scriptInput = domQuery('textarea', entry);

View File

@ -18,7 +18,7 @@ import {
} from './helpers'; } from './helpers';
import extensions from '../../app/spiffworkflow/extensions'; import extensions from '../../app/spiffworkflow/extensions';
import {query as domQuery} from 'min-dom'; import {query as domQuery} from 'min-dom';
import {default as diagram_xml} from './bpmn/diagram.bpmn';
describe('Properties Panel for User Tasks', function () { describe('Properties Panel for User Tasks', function () {
const user_form_xml = require('./bpmn/user_form.bpmn').default; const user_form_xml = require('./bpmn/user_form.bpmn').default;
@ -30,8 +30,8 @@ describe('Properties Panel for User Tasks', function () {
}); });
function addOptionsToEventBus(bpmnModeler) { function addOptionsToEventBus(bpmnModeler) {
bpmnModeler.on('spiff.json_files.requested', (event) => { bpmnModeler.on('spiff.json_schema_files.requested', (event) => {
event.eventBus.fire('spiff.json_files.returned', { event.eventBus.fire('spiff.json_schema_files.returned', {
options: [ options: [
{ label: 'pizza_form.json', value: 'pizza_form.json' }, { label: 'pizza_form.json', value: 'pizza_form.json' },
{ label: 'credit_card_form.json', value: 'credit_card_form.json' }, { label: 'credit_card_form.json', value: 'credit_card_form.json' },
@ -79,9 +79,10 @@ describe('Properties Panel for User Tasks', function () {
const entry = findEntry('extension_formJsonSchemaFilename', group); const entry = findEntry('extension_formJsonSchemaFilename', group);
const selectList = findSelect(entry); const selectList = findSelect(entry);
expect(selectList).to.exist; expect(selectList).to.exist;
expect(selectList.options.length).to.equal(4); expect(selectList.options.length).to.equal(5); // including the empty option
expect(selectList.options[0].label).to.equal('pizza_form.json'); expect(selectList.options[0].label).to.equal('');
expect(selectList.options[1].label).to.equal('credit_card_form.json'); expect(selectList.options[1].label).to.equal('pizza_form.json');
expect(selectList.options[2].label).to.equal('credit_card_form.json');
changeInput(selectList, 'pizza_form.json'); changeInput(selectList, 'pizza_form.json');
@ -106,9 +107,35 @@ describe('Properties Panel for User Tasks', function () {
const formJsonSchemaFilenameEntry = findEntry('extension_formJsonSchemaFilename', group); const formJsonSchemaFilenameEntry = findEntry('extension_formJsonSchemaFilename', group);
const formJsonSchemaFilenameInput = findSelect(formJsonSchemaFilenameEntry); const formJsonSchemaFilenameInput = findSelect(formJsonSchemaFilenameEntry);
expect(formJsonSchemaFilenameInput.value).to.equal('give_me_a_number_form.json'); expect(formJsonSchemaFilenameInput.value).to.equal('give_me_a_number_form.json');
const formUiSchemaFilenameEntry = findEntry('extension_formUiSchemaFilename', group); });
const formUiSchemaFilenameInput = findSelect(formUiSchemaFilenameEntry);
expect(formUiSchemaFilenameInput.value).to.equal('number_form_schema.json'); it('should update both the json and ui extensions if the json file is set', async function () {
await preparePropertiesPanelWithXml(diagram_xml)();
const modeler = getBpmnJS();
addOptionsToEventBus(modeler);
const userElement = await expectSelected('task_confirm');
const group = findGroupEntry('user_task_properties', container);
const button = findButton(
'launch_editor_button_formJsonSchemaFilename',
group
);
expect(button).to.exist;
let launchEvent;
let eventBus = modeler.get('eventBus');
eventBus.on('spiff.file.edit', function (event) {
launchEvent = event;
});
await pressButton(button);
expect(launchEvent.value).to.exist;
eventBus.fire('spiff.jsonSchema.update', {
value: 'new-schema.json',
});
const jsonFile = getExtensionValue(userElement, 'formJsonSchemaFilename');
const uiFile = getExtensionValue(userElement, 'formUiSchemaFilename');
expect(jsonFile).to.equal('new-schema.json');
expect(uiFile).to.equal('new-uischema.json');
}); });
it('should allow you to change the instructions to the end user', async function () { it('should allow you to change the instructions to the end user', async function () {
@ -129,7 +156,7 @@ describe('Properties Panel for User Tasks', function () {
// The change is reflected in the business object // The change is reflected in the business object
let instructions = getExtensionValue( let instructions = getExtensionValue(
userElement, userElement,
'spiffworkflow:instructionsForEndUser' 'spiffworkflow:InstructionsForEndUser'
); );
expect(instructions).to.equal('#Hello!'); expect(instructions).to.equal('#Hello!');
}); });