Feature/spiff message branch (#100)

* Add new props from editor

* Fix receive message

* Handle no messages found

* remove debounce

* Spiff message : Custom MultiSelect Input

* Add event for open json schema editor

* Get Schemas Ids - Mock API

* Implement MessageJsonSchemaSelect

* Lint

* Refactoring

* Add some missing dependencies

* Branch Refactoring

* Fix broken tests

clear extensions element on change message selected

Fix Send Task

Add Is Matching Correlation enabled

Add Variable condition

Move variable

Move Payload

List correlation props based on element

AddMatching condition Array

Branch Refactoring

* Adding spiffworkflow namespace

* Handle message APIs

* hooking message button to post and listen to the event bus.

* Fix Message Editor Button Labeling

* load files with jsx as jsx w/ burnettk

* updates to work with vite in the frontend w/ burnettk

* Add Correlation Key

* fix automatic deletion of messages&&correlationProps

* Hide message on process level

* Sync Messages and correlations on DeleteEvent

* Fix deletion of correlation properties

* Message Payload issue

* Fix payload for message events

* Fix matching condition

* fix matching condition - css issue

* Fire add new message event

* Fix AddMessage event

* Fix AddMessage event

* Fix/message tests - duplicated correlations keys (#99)

* Append correlation Properties in Launch editor event

* Not allow two parent correlation keys

* Fix Message tests #1

* fix broken tests

* Add unit test for add message event

* some updates while reviewing w/ burnettk

---------

Co-authored-by: jasquat <jasquat@users.noreply.github.com>

---------

Co-authored-by: Ayoub Ait Lachgar <44379029+theaubmov@users.noreply.github.com>
Co-authored-by: Ayoub ait lachgar <ayoubaitlachgar98@gmail.com>
Co-authored-by: Dan Funk <daniel.h.funk@gmail.com>
Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
Kevin Burnett 2024-06-10 16:00:11 +00:00 committed by GitHub
parent 38f5b5d756
commit ee86b31cd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 3153 additions and 1068 deletions

View File

@ -152,6 +152,7 @@ bpmnModeler.on('spiff.callactivity.edit', (newEvent) => {
bpmnModeler.on('spiff.file.edit', (newEvent) => {
console.log('Open new window to edit file: ', newEvent.value);
});
bpmnModeler.on('spiff.dmn.edit', (newEvent) => {
console.log('Open new window to edit DMN table: ', newEvent.value);
});
@ -169,6 +170,10 @@ bpmnModeler.on('spiff.json_schema_files.requested', (event) => {
});
});
bpmnModeler.on('spiff.msg_json_schema_files.requested', (event) => {
console.log('Open new window to edit Message json schema: ');
});
bpmnModeler.on('spiff.dmn_files.requested', (event) => {
event.eventBus.fire('spiff.dmn_files.returned', {
options: [
@ -188,6 +193,75 @@ bpmnModeler.on('spiff.data_stores.requested', (event) => {
});
});
bpmnModeler.on('spiff.messages.requested', (event) => {
event.eventBus.fire('spiff.messages.returned', {
configuration: {
"messages": [
{
"identifier": "basic_message",
"location": "examples/1-basic-concepts",
"schema": {},
"correlation_properties": []
},
{
"identifier": "end_of_day_receipts",
"location": "examples",
"schema": {},
"correlation_properties": []
},
{
"identifier": "order_ready",
"location": "examples",
"schema": {},
"correlation_properties": [
{
"identifier": "table_number",
"retrieval_expression": "table_number"
},
{
"identifier": "franchise_id",
"retrieval_expression": "franchise_id"
}
]
},
{
"identifier": "table_seated",
"location": "examples",
"schema": {},
"correlation_properties": [
{
"identifier": "table_number",
"retrieval_expression": "table_number-v2"
},
{
"identifier": "franchise_id",
"retrieval_expression": "franchise_id-v2"
}
]
}
]
}
});
});
bpmnModeler.on('spiff.add_message.requested', (event) => {
event.eventBus.fire('spiff.add_message.returned', {
name: 'msgName',
correlation_properties: {
"c1": {
"retrieval_expressions": [
"c1x"
]
},
"c2": {
"retrieval_expressions": [
"cxxxx1x"
]
}
}
});
});
// 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) => {
@ -206,7 +280,7 @@ bpmnModeler.on('import.parse.complete', event => {
refs.forEach(ref => {
const props = {
id: ref.id,
name: ref.id ? typeof(ref.name) === 'undefined': ref.name,
name: ref.id ? typeof (ref.name) === 'undefined' : ref.name,
};
let elem = bpmnModeler._moddle.create(desc, props);
elem.$parent = ref.element;
@ -214,7 +288,7 @@ bpmnModeler.on('import.parse.complete', event => {
});
});
bpmnModeler.importXML(diagramXML).then(() => {});
bpmnModeler.importXML(diagramXML).then(() => { });
// This handles the download and upload buttons - it isn't specific to
// the BPMN modeler or these extensions, just a quick way to allow you to

View File

@ -24,6 +24,7 @@ html, body {
border-radius: 2px;
font-family: 'Arial', sans-serif;
padding: 10px;
/* width: 400px; */
min-width: 400px;
}
@ -50,6 +51,11 @@ adjust CSS props so that padding won't add to dimensions.
padding-right: 2px;
}
/* .properties-panel-parent {
border-left: unset !important;
overflow: unset !important;
} */
/* Style buttons */
.bpmn-js-spiffworkflow-btn {
background-color: DodgerBlue;
@ -61,6 +67,16 @@ adjust CSS props so that padding won't add to dimensions.
margin: 12px;
}
.bio-properties-panel-description {
word-wrap: break-word;
max-width: 350px;
}
.bio-properties-panel-group-entries > .bio-properties-panel-description {
padding-inline: 15px;
padding-block: 8px;
}
/* Darker background on mouse-over */
.bpmn-js-spiffworkflow-btn:hover {
background-color: RoyalBlue;
@ -95,3 +111,29 @@ adjust CSS props so that padding won't add to dimensions.
.djs-palette.two-column.open {
width: 95px;
}
.nice-select {
background-color: #f7f7f8 !important;
border: 1px solid var(--input-border-color) !important;
border-radius: 2px !important;
padding-left: 8px !important;
}
.nice-select .nice-select-dropdown {
z-index: 50 !important;
width: 100%;
}
.nice-select .multiple-options .current {
margin: 2px;
padding: 5px;
background-color: #ffffff;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 0.9em;
color: #333;
}
.d-grid {
display: grid !important;
}

View File

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<!--
IMPORTANT:
@ -8,15 +9,15 @@
-->
<title>bpmn-js-spiffworkflow</title>
<meta charset="utf-8"/>
<meta charset="utf-8" />
<!-- here are the core dependencies you will need to include -->
<link rel="stylesheet" href="vendor/bpmn-js/assets/diagram-js.css"/>
<link rel="stylesheet" href="vendor/bpmn-js/assets/bpmn-js.css"/>
<link rel="stylesheet" href="vendor/bpmn-js/assets/bpmn-font/css/bpmn-embedded.css"/>
<link rel="stylesheet" href="vendor/bpmn-js-properties-panel/assets/properties-panel.css"/>
<link rel="stylesheet" href="vendor/bpmn-js/assets/diagram-js.css" />
<link rel="stylesheet" href="vendor/bpmn-js/assets/bpmn-js.css" />
<link rel="stylesheet" href="vendor/bpmn-js/assets/bpmn-font/css/bpmn-embedded.css" />
<link rel="stylesheet" href="vendor/bpmn-js-properties-panel/assets/properties-panel.css" />
<!-- Some local css settings -->
<link rel="stylesheet" href="css/app.css"/>
<link rel="stylesheet" href="css/app.css" />
<link rel="shortcut icon" href="#">
<!-- A python code editor, we are using CodeMirror here -- see app.js for how this is wired in -->
@ -31,34 +32,38 @@
<!-- Just have this for the download file icon -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nice-select2/dist/css/nice-select2.css">
<!-- <script src="https://cdn.jsdelivr.net/npm/nice-select2/dist/js/nice-select2.js"></script> -->
</head>
<body>
<div id="menu">
<button id="downloadButton" class="bpmn-js-spiffworkflow-btn"><i class="fa fa-download"></i> Download</button>
<button id="uploadButton" class="bpmn-js-spiffworkflow-btn">Open a file</button>
</div>
<div id="container">
<div id="modeler"></div>
<div id="panel"></div>
</div>
<!-- the following are overlays to provide editors for Python and Markdown -->
<div id="code_overlay" class="overlay">
<div id="code_editor"></div>
<div id="code_buttons">
<button id="saveCode" class="bpmn-js-spiffworkflow-btn">Save</button>
</div>
</div>
<div id="markdown_overlay" class="overlay">
<div id="markdown_editor">
<textarea id="markdown_textarea"></textarea>
</div>
<div id="markdown_buttons">
<button id="saveMarkdown" class="bpmn-js-spiffworkflow-btn">Save</button>
</div>
</div>
<!-- Here we load up our application, it's where the configuration happens. -->
<script src="app.js"></script>
<body>
<div id="menu">
<button id="downloadButton" class="bpmn-js-spiffworkflow-btn"><i class="fa fa-download"></i> Download</button>
<button id="uploadButton" class="bpmn-js-spiffworkflow-btn">Open a file</button>
</div>
<div id="container">
<div id="modeler"></div>
<div id="panel"></div>
</div>
<!-- the following are overlays to provide editors for Python and Markdown -->
<div id="code_overlay" class="overlay">
<div id="code_editor"></div>
<div id="code_buttons">
<button id="saveCode" class="bpmn-js-spiffworkflow-btn">Save</button>
</div>
</div>
<div id="markdown_overlay" class="overlay">
<div id="markdown_editor">
<textarea id="markdown_textarea"></textarea>
</div>
<div id="markdown_buttons">
<button id="saveMarkdown" class="bpmn-js-spiffworkflow-btn">Save</button>
</div>
</div>
<!-- Here we load up our application, it's where the configuration happens. -->
<script src="app.js"></script>
</body>
</html><!---->
</html><!---->

View File

@ -123,7 +123,7 @@ function getSelectorForType(eventDetails) {
const getOptions = (val) => {
const matching = root.rootElements ? root.rootElements.filter(elem => elem.$type === eventType) : [];
const options = [];
matching.map(option => options.push({label: option.name, value: option.id}));
matching.map(option => options.push({ label: option.name, value: option.id }));
return options;
}
@ -169,7 +169,7 @@ function getTextFieldForExtension(eventDetails, label, description, catching) {
}
}
const setValue = (value) => {
const setValue = (value) => {
const bpmnEvent = getEvent();
if (bpmnEvent) {
if (!bpmnEvent.extensionElements)

View File

@ -19,6 +19,7 @@ import CallActivityPropertiesProvider from './callActivity/propertiesPanel/CallA
import StandardLoopPropertiesProvider from './loops/propertiesPanel/StandardLoopPropertiesProvider';
import MultiInstancePropertiesProvider from './loops/propertiesPanel/MultiInstancePropertiesProvider';
import CallActivityInterceptor from './callActivity/CallActivityInterceptor';
import MessageInterceptor from './messages/MessageInterceptor';
export default {
__depends__: [RulesModule],
@ -32,6 +33,7 @@ export default {
'conditionsPropertiesProvider',
'extensionsPropertiesProvider',
'messagesPropertiesProvider',
'messageInterceptor',
'signalPropertiesProvider',
'errorPropertiesProvider',
'escalationPropertiesProvider',
@ -57,6 +59,7 @@ export default {
errorPropertiesProvider: ['type', ErrorPropertiesProvider],
escalationPropertiesProvider: ['type', EscalationPropertiesProvider],
messagesPropertiesProvider: ['type', MessagesPropertiesProvider],
messageInterceptor: ['type', MessageInterceptor],
callActivityPropertiesProvider: ['type', CallActivityPropertiesProvider],
ioPalette: ['type', IoPalette],
ioRules: ['type', IoRules],

636
app/spiffworkflow/messages/MessageHelpers.js Normal file → Executable file
View File

@ -14,16 +14,28 @@ export function getRoot(businessObject, moddle) {
}
} else {
// todo: Do we want businessObject to be a shape or moddle object?
if (businessObject.$type === 'bpmn:Definitions') {
if (businessObject && businessObject.$type === 'bpmn:Definitions') {
return businessObject;
}
if (typeof businessObject.$parent !== 'undefined') {
if (businessObject && typeof businessObject.$parent !== 'undefined') {
return getRoot(businessObject.$parent);
}
}
return businessObject;
}
export function getAbsoluteRoot(businessObject, moddle) {
// todo: Do we want businessObject to be a shape or moddle object?
if (businessObject.$type === 'bpmn:Definitions') {
return businessObject;
}
if (typeof businessObject.$parent !== 'undefined') {
return getRoot(businessObject.$parent);
}
return businessObject;
}
export function isMessageElement(shapeElement) {
return (
is(shapeElement, 'bpmn:SendTask') ||
@ -33,11 +45,18 @@ export function isMessageElement(shapeElement) {
}
export function isMessageEvent(shapeElement) {
const { eventDefinitions } = shapeElement.businessObject;
if (eventDefinitions && eventDefinitions[0]) {
return eventDefinitions[0].$type === 'bpmn:MessageEventDefinition';
try {
const bo = shapeElement.businessObject
? shapeElement.businessObject
: shapeElement;
const { eventDefinitions } = bo;
if (eventDefinitions && eventDefinitions[0]) {
return eventDefinitions[0].$type === 'bpmn:MessageEventDefinition';
}
return false;
} catch (error) {
return false;
}
return false;
}
export function canReceiveMessage(shapeElement) {
@ -83,7 +102,7 @@ export function findCorrelationKeyForCorrelationProperty(shapeElement, moddle) {
}
export function findCorrelationPropertiesAndRetrievalExpressionsForMessage(
shapeElement,
shapeElement
) {
const formalExpressions = [];
const messageRefElement = getMessageRefElement(shapeElement);
@ -95,7 +114,7 @@ export function findCorrelationPropertiesAndRetrievalExpressionsForMessage(
const retrievalExpression =
getRetrievalExpressionFromCorrelationProperty(
childElement,
messageRefElement,
messageRefElement
);
if (retrievalExpression) {
const formalExpression = {
@ -128,7 +147,7 @@ export function getMessageElementForShapeElement(shapeElement) {
function getRetrievalExpressionFromCorrelationProperty(
correlationProperty,
message,
message
) {
if (correlationProperty.correlationPropertyRetrievalExpression) {
for (const retrievalExpression of correlationProperty.correlationPropertyRetrievalExpression) {
@ -158,6 +177,50 @@ export function findCorrelationProperties(businessObject, moddle) {
return correlationProperties;
}
export function findCorrelationPropertiesByMessage(element) {
let messageId;
const { businessObject } = element;
const root = getRoot(businessObject);
const correlationProperties = [];
if (isMessageEvent(element)) {
if (
!businessObject.eventDefinitions ||
!businessObject.eventDefinitions[0].messageRef
) {
return [];
} else {
messageId = businessObject.eventDefinitions[0].messageRef.id;
}
} else if (isMessageElement(element)) {
if (!businessObject.messageRef) return;
messageId = businessObject.messageRef.id;
}
if (isIterable(root.rootElements)) {
for (const rootElement of root.rootElements) {
if (rootElement.$type === 'bpmn:CorrelationProperty') {
rootElement.correlationPropertyRetrievalExpression =
rootElement.correlationPropertyRetrievalExpression
? rootElement.correlationPropertyRetrievalExpression
: [];
const existingExpressionIndex =
rootElement.correlationPropertyRetrievalExpression.findIndex(
(retrievalExpr) =>
retrievalExpr.messageRef &&
retrievalExpr.messageRef.id === messageId
);
existingExpressionIndex !== -1
? correlationProperties.push(rootElement)
: null;
}
}
}
return correlationProperties;
}
function isIterable(obj) {
// checks for null and undefined
if (obj == null) {
@ -196,3 +259,558 @@ export function findMessageModdleElements(businessObject) {
}
return messages;
}
export function findMessageElement(businessObject, messageId, definitions) {
let root = getRoot(businessObject);
// This case is to handle root for deleted elements
if (!root && definitions) {
root = definitions;
}
if (root.rootElements) {
for (const rootElement of root.rootElements) {
if (
rootElement.$type === 'bpmn:Message' &&
rootElement.name == messageId
) {
return rootElement;
}
}
}
return null;
}
export function createOrUpdateCorrelationProperties(
bpmnFactory,
commandStack,
element,
propertiesConfig,
messageId
) {
let definitions = getRoot(element.businessObject);
if (propertiesConfig) {
// Iterate over each property configuration
propertiesConfig.forEach((propConfig) => {
if (isMessageIdInRetrievalExpressions(propConfig, messageId)) {
let correlationProperty = findCorrelationPropertyById(
definitions,
propConfig.id
);
// If the correlationProperty does not exist, create it
if (correlationProperty === null) {
correlationProperty = bpmnFactory.create('bpmn:CorrelationProperty');
correlationProperty.id = propConfig.id;
correlationProperty.name = propConfig.id;
correlationProperty.correlationPropertyRetrievalExpression = [];
} else if (
correlationProperty &&
!correlationProperty.correlationPropertyRetrievalExpression
) {
correlationProperty.correlationPropertyRetrievalExpression = [];
}
// Iterate over retrieval expressions and add them to the correlationProperty
propConfig.retrieval_expressions.forEach((expr) => {
const existingExpressionIndex =
correlationProperty.correlationPropertyRetrievalExpression.findIndex(
(retrievalExpr) =>
retrievalExpr.messageRef &&
retrievalExpr.messageRef.id === messageId
);
if (expr.message_ref == messageId && existingExpressionIndex === -1) {
const retrievalExpression = bpmnFactory.create(
'bpmn:CorrelationPropertyRetrievalExpression'
);
const formalExpression = bpmnFactory.create(
'bpmn:FormalExpression'
);
formalExpression.body = expr.formal_expression
? expr.formal_expression
: '';
retrievalExpression.messagePath = formalExpression;
const msgElement = findMessageElement(
element.businessObject,
expr.message_ref
);
retrievalExpression.messageRef = msgElement;
correlationProperty.correlationPropertyRetrievalExpression.push(
retrievalExpression
);
}
});
const existingIndex = definitions.rootElements.findIndex(
(element) =>
element.id === correlationProperty.id &&
element.$type === correlationProperty.$type
);
if (existingIndex !== -1) {
// Update existing correlationProperty
definitions.rootElements[existingIndex] = correlationProperty;
} else {
// Add new correlationProperty
definitions.rootElements.push(correlationProperty);
}
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
}
});
}
}
export function createOrUpdateCorrelationPropertiesV2(
bpmnFactory,
commandStack,
element,
propertiesConfig,
messageId
) {
let definitions = getRoot(element.businessObject);
if (propertiesConfig) {
// Iterate over each property configuration
propertiesConfig.forEach((propConfig) => {
let correlationProperty = findCorrelationPropertyById(
definitions,
propConfig.identifier
);
const msgElement = findMessageElement(element.businessObject, messageId);
if (correlationProperty === null) {
correlationProperty = bpmnFactory.create('bpmn:CorrelationProperty');
correlationProperty.id = propConfig.identifier;
correlationProperty.name = propConfig.identifier;
correlationProperty.correlationPropertyRetrievalExpression = [];
}
correlationProperty.correlationPropertyRetrievalExpression =
!correlationProperty.correlationPropertyRetrievalExpression
? []
: correlationProperty.correlationPropertyRetrievalExpression;
const existingExpressionIndex =
correlationProperty.correlationPropertyRetrievalExpression.findIndex(
(retrievalExpr) =>
retrievalExpr.messageRef &&
retrievalExpr.messageRef.id === messageId
);
if (existingExpressionIndex === -1) {
const retrievalExpression = bpmnFactory.create(
'bpmn:CorrelationPropertyRetrievalExpression'
);
const formalExpression = bpmnFactory.create('bpmn:FormalExpression');
formalExpression.body = propConfig.retrieval_expression
? propConfig.retrieval_expression
: '';
retrievalExpression.messagePath = formalExpression;
retrievalExpression.messageRef = msgElement;
correlationProperty.correlationPropertyRetrievalExpression.push(
retrievalExpression
);
}
const existingIndex = definitions.rootElements.findIndex(
(element) =>
element.id === correlationProperty.id &&
element.$type === correlationProperty.$type
);
if (existingIndex !== -1) {
// Update existing correlationProperty
definitions.rootElements[existingIndex] = correlationProperty;
} else {
// Add new correlationProperty
definitions.rootElements.push(correlationProperty);
}
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
});
}
}
export function findCorrelationPropertyById(definitions, id) {
let foundCorrelationProperty = null;
definitions.rootElements.forEach((rootElement) => {
if (
rootElement.$type === 'bpmn:CorrelationProperty' &&
rootElement.id === id
) {
foundCorrelationProperty = rootElement;
}
});
return foundCorrelationProperty;
}
export function isMessageRefUsed(definitions, messageRef) {
if (!definitions.rootElements) {
return true;
}
for (const rootElement of definitions.rootElements) {
if (rootElement.$type === 'bpmn:Process') {
const process = rootElement;
for (const element of process.flowElements) {
if (
isMessageEvent(element) &&
element.eventDefinitions &&
element.eventDefinitions[0] &&
element.eventDefinitions[0].messageRef &&
element.eventDefinitions[0].messageRef.id === messageRef
) {
return true;
} else if (
isMessageElement(element) &&
element.messageRef &&
element.messageRef.id === messageRef
) {
return true;
}
}
}
}
return false;
}
function isMessageIdInRetrievalExpressions(propConfig, messageId) {
return propConfig.retrieval_expressions.some(
(expr) => expr.message_ref === messageId
);
}
function isMessageRefInCorrelationPropertiesRetrivalExpression(
correlationProperty,
messageRef
) {
return correlationProperty.correlationPropertyRetrievalExpression
? correlationProperty.correlationPropertyRetrievalExpression.some(
(expr) => expr.messageRef === messageRef
)
: false;
}
// Create new correlation property from editor
export function createNewCorrelationProperty(
element,
moddle,
commandStack,
messageRef
) {
const rootElement = getRoot(element.businessObject);
const { rootElements } = rootElement;
const newCorrelationPropertyElement = moddle.create(
'bpmn:CorrelationProperty'
);
const correlationPropertyId = moddle.ids.nextPrefixed('CorrelationProperty_');
newCorrelationPropertyElement.id = correlationPropertyId;
newCorrelationPropertyElement.name = correlationPropertyId;
newCorrelationPropertyElement.correlationPropertyRetrievalExpression = [];
if (messageRef) {
const retrievalExpression = moddle.create(
'bpmn:CorrelationPropertyRetrievalExpression'
);
const formalExpression = moddle.create('bpmn:FormalExpression');
formalExpression.body = '';
retrievalExpression.messagePath = formalExpression;
retrievalExpression.messageRef = messageRef;
newCorrelationPropertyElement.correlationPropertyRetrievalExpression.push(
retrievalExpression
);
}
rootElements.push(newCorrelationPropertyElement);
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
}
// Create new correlation key from editor
export function createNewCorrelationKey(
element,
moddle,
commandStack,
messageRef
) {
if (element.type === 'bpmn:Collaboration') {
const newCorrelationKeyElement = moddle.create('bpmn:CorrelationKey');
const newCorrelationKey = moddle.ids.nextPrefixed('CorrelationKey_');
newCorrelationKeyElement.name = newCorrelationKey;
newCorrelationKeyElement.id = newCorrelationKey;
const currentCorrelationKeyElements =
element.businessObject.get('correlationKeys');
currentCorrelationKeyElements.push(newCorrelationKeyElement);
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
}
}
// Create new message from editor
export function createNewMessage(element, moddle, commandStack) {
if (
element.type === 'bpmn:Collaboration' ||
element.type === 'bpmn:Process'
) {
const rootElement = getRoot(element.businessObject);
const { rootElements } = rootElement;
const messageId = moddle.ids.nextPrefixed('Message_');
const newMessageElement = moddle.create('bpmn:Message');
newMessageElement.id = messageId;
newMessageElement.name = messageId;
rootElements.push(newMessageElement);
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
}
}
// Set a message to a list of correlation properties
export function setMessageRefToListofCorrelationProperties(
messageRef,
correlationPropertyIDs,
element,
moddle,
commandStack
) {
let definitions = getRoot(element.businessObject);
if (definitions) {
definitions.rootElements.forEach((rootElement) => {
if (
rootElement.$type === 'bpmn:CorrelationProperty' &&
correlationPropertyIDs.includes(rootElement.id) &&
!isMessageRefInCorrelationPropertiesRetrivalExpression(
rootElement,
messageRef
)
) {
const retrievalExpression = moddle.create(
'bpmn:CorrelationPropertyRetrievalExpression'
);
const formalExpression = moddle.create('bpmn:FormalExpression');
formalExpression.body = '';
retrievalExpression.messagePath = formalExpression;
retrievalExpression.messageRef = messageRef;
rootElement.correlationPropertyRetrievalExpression
? rootElement.correlationPropertyRetrievalExpression.push(
retrievalExpression
)
: (rootElement.correlationPropertyRetrievalExpression = [
retrievalExpression,
]);
} else if (
rootElement.$type === 'bpmn:CorrelationProperty' &&
!correlationPropertyIDs.includes(rootElement.id) &&
isMessageRefInCorrelationPropertiesRetrivalExpression(
rootElement,
messageRef
)
) {
rootElement.correlationPropertyRetrievalExpression =
rootElement.correlationPropertyRetrievalExpression.filter(
(expr) => expr.messageRef !== messageRef
);
}
});
}
}
export function getCorrelationPropertiesIDsFiltredByMessageRef(
businessObject,
moddle,
messageRef
) {
const root = getRoot(businessObject, moddle);
const IDs = [];
if (isIterable(root.rootElements)) {
for (const rootElement of root.rootElements) {
if (
rootElement.$type === 'bpmn:CorrelationProperty' &&
isMessageRefInCorrelationPropertiesRetrivalExpression(
rootElement,
messageRef
)
) {
IDs.push({
value: rootElement.id,
text: rootElement.name || rootElement.id,
selected: true,
});
} else if (
rootElement.$type === 'bpmn:CorrelationProperty' &&
!isMessageRefInCorrelationPropertiesRetrivalExpression(
rootElement,
messageRef
)
) {
IDs.push({
value: rootElement.id,
text: rootElement.name || rootElement.id,
selected: false,
});
}
}
}
return IDs;
}
export function setParentCorrelationKeys(
definitions,
bpmnFactory,
element,
moddle
) {
// Retrieve all correlation properties
let correlationProperties = findCorrelationProperties(
element.businessObject,
moddle
);
correlationProperties = correlationProperties || [];
let mainCorrelationKey = findOrCreateMainCorrelationKey(
definitions,
bpmnFactory,
moddle
);
// Clear existing ones
mainCorrelationKey.get('correlationPropertyRef').length = 0;
// Sync correlation properties
correlationProperties.forEach((cP) => {
const cPElement = bpmnFactory.create('bpmn:CorrelationProperty', {
id: cP.id,
name: cP.name,
});
mainCorrelationKey.get('correlationPropertyRef').push(cPElement);
});
// check if process has collaboration
let collaboration = definitions
.get('rootElements')
.find((element) => element.$type === 'bpmn:Collaboration');
if (collaboration) {
// Remove existing correlation keys other than the main correlation key
collaboration.get('correlationKeys').forEach((key, index) => {
if (key.name !== 'MainCorrelationKey') {
collaboration.get('correlationKeys').splice(index, 1);
}
});
const existingKey = collaboration
.get('correlationKeys')
.find((key) => key.name === 'MainCorrelationKey');
if (!existingKey) {
collaboration.get('correlationKeys').push(mainCorrelationKey);
}
} else {
// Handle case where no collaboration is found
definitions.get('rootElements').forEach((element, index) => {
if (
element.$type === 'bpmn:CorrelationKey' &&
element.name !== 'MainCorrelationKey'
) {
definitions.get('rootElements').splice(index, 1);
}
});
const existingKey = definitions
.get('rootElements')
.find(
(key) =>
key.$type === 'bpmn:CorrelationKey' &&
key.name === 'MainCorrelationKey'
);
if (!existingKey) {
definitions.get('rootElements').push(mainCorrelationKey);
}
}
}
function findOrCreateMainCorrelationKey(definitions, bpmnFactory, moddle) {
let mainCorrelationKey = definitions
.get('rootElements')
.find(
(element) =>
element.$type === 'bpmn:CorrelationKey' &&
element.name === 'MainCorrelationKey'
);
if (!mainCorrelationKey) {
const newCorrelationKeyId = moddle.ids.nextPrefixed('CorrelationKey_');
mainCorrelationKey = bpmnFactory.create('bpmn:CorrelationKey', {
id: newCorrelationKeyId,
name: 'MainCorrelationKey',
});
}
return mainCorrelationKey;
}
export function synCorrleationProperties(element, definitions, moddle) {
const { businessObject } = element;
const correlationProps = findCorrelationProperties(businessObject, moddle);
const expressionsToDelete = [];
for (let cProperty of correlationProps) {
let isUsed = false;
for (const cpExpression of cProperty.correlationPropertyRetrievalExpression) {
const msgRef = findMessageElement(
businessObject,
cpExpression.messageRef.id,
definitions
);
isUsed = msgRef ? true : isUsed;
// if unused false, delete retrival expression
if (!msgRef) {
expressionsToDelete.push(cpExpression);
}
}
// Delete the retrieval expressions that are not used
for (const expression of expressionsToDelete) {
const index =
cProperty.correlationPropertyRetrievalExpression.indexOf(expression);
if (index > -1) {
cProperty.correlationPropertyRetrievalExpression.splice(index, 1);
const cPropertyIndex = definitions
.get('rootElements')
.indexOf(cProperty);
definitions.rootElements.splice(cPropertyIndex, 1, cProperty);
}
}
// If Unused, delete the correlation property
if (!isUsed) {
const index = definitions.get('rootElements').indexOf(cProperty);
if (index > -1) {
definitions.rootElements.splice(index, 1);
}
}
}
}

View File

@ -0,0 +1,57 @@
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
import { isMessageElement, isMessageRefUsed, setParentCorrelationKeys, synCorrleationProperties } from './MessageHelpers';
const HIGH_PRIORITY = 90500;
export default class MessageInterceptor extends CommandInterceptor {
constructor(eventBus, bpmnFactory, commandStack, bpmnUpdater, moddle) {
super(eventBus);
this.postExecuted(['shape.delete'], HIGH_PRIORITY, function (event) {
const { context } = event;
const { shape, rootElement } = context;
const { businessObject } = shape;
if (isMessageElement(shape)) {
let oldMessageRef = (businessObject.eventDefinitions) ? businessObject.eventDefinitions[0].messageRef : businessObject.messageRef;
let definitions = rootElement.businessObject.$parent;
if (!definitions.get('rootElements')) {
definitions.set('rootElements', []);
}
if (oldMessageRef) {
// Remove previous message in case it's not used anymore
const isOldMessageUsed = isMessageRefUsed(definitions, oldMessageRef.id);
if (!isOldMessageUsed) {
const rootElements = definitions.get('rootElements');
const oldMessageIndex = rootElements.findIndex(
(element) =>
element.$type === 'bpmn:Message' && element.id === oldMessageRef.id,
);
if (oldMessageIndex !== -1) {
rootElements.splice(oldMessageIndex, 1);
definitions.rootElements = rootElements;
}
}
// Automatic deletion of previous message correlation properties
synCorrleationProperties(shape, definitions, moddle);
}
// Update Correlation key if Process has collaboration
try {
setParentCorrelationKeys(definitions, bpmnFactory, shape, moddle);
} catch (error) {
console.error('Error Caught while synchronizing Correlation key', error);
}
}
});
}
}
MessageInterceptor.$inject = ['eventBus', 'bpmnFactory', 'commandStack', 'bpmnUpdater', 'moddle'];

5
app/spiffworkflow/messages/index.js Normal file → Executable file
View File

@ -1,6 +1,9 @@
import MessageInterceptor from './MessageInterceptor';
import MessagesPropertiesProvider from './propertiesPanel/MessagesPropertiesProvider';
export default {
__init__: ['messagesPropertiesProvider'],
__init__: ['messagesPropertiesProvider', 'messageInterceptor'],
messagesPropertiesProvider: ['type', MessagesPropertiesProvider],
messageInterceptor: [ 'type', MessageInterceptor ],
};

View File

@ -1,291 +0,0 @@
import { useService } from 'bpmn-js-properties-panel';
import {
SelectEntry,
isTextFieldEntryEdited,
TextFieldEntry,
} from '@bpmn-io/properties-panel';
import {
getRoot,
findCorrelationKeys,
findCorrelationProperties,
findCorrelationKeyForCorrelationProperty,
} from '../MessageHelpers';
import { removeFirstInstanceOfItemFromArrayInPlace } from '../../helpers';
/**
* Allows the creation, or editing of messageCorrelations at the bpmn:sendTask level of a BPMN document.
*/
export function CorrelationPropertiesArray(props) {
const { moddle } = props;
const { element } = props;
const { commandStack } = props;
const { translate } = props;
const correlationPropertyArray = findCorrelationProperties(
element.businessObject
);
const items = correlationPropertyArray.map(
(correlationPropertyModdleElement, index) => {
const id = `correlation-${index}`;
const entries = MessageCorrelationPropertyGroup({
idPrefix: id,
correlationPropertyModdleElement,
translate,
element,
commandStack,
moddle,
});
return {
id,
label: correlationPropertyModdleElement.name,
entries,
autoFocusEntry: id,
remove: removeFactory({
element,
correlationPropertyModdleElement,
commandStack,
moddle,
}),
};
}
);
function add(event) {
event.stopPropagation();
const newCorrelationPropertyElement = moddle.create(
'bpmn:CorrelationProperty'
);
const correlationPropertyId = moddle.ids.nextPrefixed(
'CorrelationProperty_'
);
newCorrelationPropertyElement.id = correlationPropertyId;
newCorrelationPropertyElement.name = correlationPropertyId;
const rootElement = getRoot(element.businessObject);
const { rootElements } = rootElement;
rootElements.push(newCorrelationPropertyElement);
const correlationKeyElements = findCorrelationKeys(
newCorrelationPropertyElement,
moddle
);
const correlationKeyElement = correlationKeyElements[0];
if (correlationKeyElement.correlationPropertyRef) {
correlationKeyElement.correlationPropertyRef.push(
newCorrelationPropertyElement
);
} else {
correlationKeyElement.correlationPropertyRef = [
newCorrelationPropertyElement,
];
}
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
}
return { items, add };
}
function removeFactory(props) {
const { element, correlationPropertyModdleElement, moddle, commandStack } =
props;
return function (event) {
event.stopPropagation();
const rootElement = getRoot(element.businessObject);
const { rootElements } = rootElement;
const oldCorrelationKeyElement = findCorrelationKeyForCorrelationProperty(
correlationPropertyModdleElement,
moddle
);
if (oldCorrelationKeyElement) {
removeFirstInstanceOfItemFromArrayInPlace(
oldCorrelationKeyElement.correlationPropertyRef,
correlationPropertyModdleElement
);
}
removeFirstInstanceOfItemFromArrayInPlace(
rootElements,
correlationPropertyModdleElement
);
commandStack.execute('element.updateProperties', {
element,
properties: {
messages: rootElements,
},
});
};
}
function MessageCorrelationPropertyGroup(props) {
const {
idPrefix,
correlationPropertyModdleElement,
translate,
element,
commandStack,
moddle,
} = props;
return [
{
id: `${idPrefix}-correlation-key`,
component: MessageCorrelationKeySelect,
isEdited: isTextFieldEntryEdited,
idPrefix,
element,
correlationPropertyModdleElement,
translate,
moddle,
commandStack,
},
{
id: `${idPrefix}-correlation-property-name`,
component: CorrelationPropertyNameTextField,
isEdited: isTextFieldEntryEdited,
idPrefix,
element,
correlationPropertyModdleElement,
translate,
commandStack,
},
];
}
function MessageCorrelationKeySelect(props) {
const {
idPrefix,
correlationPropertyModdleElement,
translate,
element,
moddle,
commandStack,
} = props;
const debounce = useService('debounceInput');
const setValue = (value) => {
if (
value === 'placeholder-never-should-be-used-as-an-actual-correlation-key'
) {
return;
}
const correlationKeyElements = findCorrelationKeys(
correlationPropertyModdleElement,
moddle
);
let newCorrelationKeyElement;
for (const cke of correlationKeyElements) {
if (cke.name === value) {
newCorrelationKeyElement = cke;
}
}
const oldCorrelationKeyElement = findCorrelationKeyForCorrelationProperty(
correlationPropertyModdleElement,
moddle
);
if (newCorrelationKeyElement.correlationPropertyRef) {
newCorrelationKeyElement.correlationPropertyRef.push(
correlationPropertyModdleElement
);
} else {
newCorrelationKeyElement.correlationPropertyRef = [
correlationPropertyModdleElement,
];
}
if (oldCorrelationKeyElement) {
removeFirstInstanceOfItemFromArrayInPlace(
oldCorrelationKeyElement.correlationPropertyRef,
correlationPropertyModdleElement
);
}
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: correlationPropertyModdleElement,
properties: {},
});
};
const getValue = () => {
const correlationKeyElement = findCorrelationKeyForCorrelationProperty(
correlationPropertyModdleElement,
moddle
);
if (correlationKeyElement) {
return correlationKeyElement.name;
}
return null;
};
const getOptions = () => {
const correlationKeyElements = findCorrelationKeys(
correlationPropertyModdleElement,
moddle
);
const options = [];
if (correlationKeyElements.length === 0) {
options.push({
label: 'Please Create a Correlation Key',
value: 'placeholder-never-should-be-used-as-an-actual-correlation-key',
disabled: true,
});
}
for (const correlationKeyElement of correlationKeyElements) {
options.push({
label: correlationKeyElement.name,
value: correlationKeyElement.name,
});
}
return options;
};
return SelectEntry({
id: `${idPrefix}-select`,
element,
label: translate('Correlation Key'),
getValue,
setValue,
getOptions,
debounce,
});
}
function CorrelationPropertyNameTextField(props) {
const {
id,
element,
correlationPropertyModdleElement,
commandStack,
translate,
} = props;
const debounce = useService('debounceInput');
const setValue = (value) => {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: correlationPropertyModdleElement,
properties: {
name: value,
id: value,
},
});
};
const getValue = () => {
return correlationPropertyModdleElement.name;
};
return TextFieldEntry({
element,
id: `${id}-name-textField`,
label: translate('Name'),
getValue,
setValue,
debounce,
});
}

View File

@ -11,268 +11,271 @@ import {
} from '../MessageHelpers';
import { removeFirstInstanceOfItemFromArrayInPlace } from '../../helpers';
// UNUSED MODULE
/**
* Allows the creation, or editing of messageCorrelations at the bpmn:sendTask level of a BPMN document.
*/
export function MessageCorrelationPropertiesArray(props) {
const { moddle } = props;
const { element } = props;
const { commandStack } = props;
const { translate } = props;
// const { moddle } = props;
// const { element } = props;
// const { commandStack } = props;
// const { translate } = props;
const correlationPropertyObjectsForCurrentMessage =
findCorrelationPropertiesAndRetrievalExpressionsForMessage(element);
const allCorrelationPropertyModdleElements = findCorrelationProperties(
element,
moddle
);
const items = correlationPropertyObjectsForCurrentMessage.map(
(correlationPropertyObject, index) => {
const {
correlationPropertyModdleElement,
correlationPropertyRetrievalExpressionModdleElement,
} = correlationPropertyObject;
const id = `correlation-${index}`;
const entries = MessageCorrelationPropertyGroup({
idPrefix: id,
correlationPropertyModdleElement,
correlationPropertyRetrievalExpressionModdleElement,
translate,
moddle,
element,
commandStack,
});
return {
id,
label: correlationPropertyModdleElement.name,
entries,
autoFocusEntry: id,
remove: removeFactory({
element,
correlationPropertyModdleElement,
correlationPropertyRetrievalExpressionModdleElement,
commandStack,
}),
};
}
);
// const correlationPropertyObjectsForCurrentMessage =
// findCorrelationPropertiesAndRetrievalExpressionsForMessage(element);
// const allCorrelationPropertyModdleElements = findCorrelationProperties(
// element,
// moddle
// );
// const items = correlationPropertyObjectsForCurrentMessage.map(
// (correlationPropertyObject, index) => {
// const {
// correlationPropertyModdleElement,
// correlationPropertyRetrievalExpressionModdleElement,
// } = correlationPropertyObject;
// const id = `correlation-${index}`;
// const entries = MessageCorrelationPropertyGroup({
// idPrefix: id,
// correlationPropertyModdleElement,
// correlationPropertyRetrievalExpressionModdleElement,
// translate,
// moddle,
// element,
// commandStack,
// });
// return {
// id,
// label: correlationPropertyModdleElement.name,
// entries,
// autoFocusEntry: id,
// remove: removeFactory({
// element,
// correlationPropertyModdleElement,
// correlationPropertyRetrievalExpressionModdleElement,
// commandStack,
// }),
// };
// }
// );
function add(event) {
event.stopPropagation();
// function add(event) {
// event.stopPropagation();
let correlationPropertyElement;
allCorrelationPropertyModdleElements.forEach((cpe) => {
let foundElement = false;
correlationPropertyObjectsForCurrentMessage.forEach((cpo) => {
const cpme = cpo.correlationPropertyModdleElement;
if (cpme.id === cpe.id) {
foundElement = true;
}
});
if (!foundElement) {
correlationPropertyElement = cpe;
}
});
// let correlationPropertyElement;
// allCorrelationPropertyModdleElements.forEach((cpe) => {
// let foundElement = false;
// correlationPropertyObjectsForCurrentMessage.forEach((cpo) => {
// const cpme = cpo.correlationPropertyModdleElement;
// if (cpme.id === cpe.id) {
// foundElement = true;
// }
// });
// if (!foundElement) {
// correlationPropertyElement = cpe;
// }
// });
// TODO: we should have some way to show an error if element is not found instead
// we need to check this since the code assumes each message only has one ref
// and will not display all properties if there are multiple
if (correlationPropertyElement) {
const newRetrievalExpressionElement = moddle.create(
'bpmn:CorrelationPropertyRetrievalExpression'
);
const messageRefElement = getMessageRefElement(element);
const newFormalExpression = moddle.create('bpmn:FormalExpression');
newFormalExpression.body = '';
// // TODO: we should have some way to show an error if element is not found instead
// // we need to check this since the code assumes each message only has one ref
// // and will not display all properties if there are multiple
// if (correlationPropertyElement) {
// const newRetrievalExpressionElement = moddle.create(
// 'bpmn:CorrelationPropertyRetrievalExpression'
// );
// const messageRefElement = getMessageRefElement(element);
// const newFormalExpression = moddle.create('bpmn:FormalExpression');
// newFormalExpression.body = '';
newRetrievalExpressionElement.messageRef = messageRefElement;
newRetrievalExpressionElement.messagePath = newFormalExpression;
// newRetrievalExpressionElement.messageRef = messageRefElement;
// newRetrievalExpressionElement.messagePath = newFormalExpression;
if (!correlationPropertyElement.correlationPropertyRetrievalExpression) {
correlationPropertyElement.correlationPropertyRetrievalExpression = [];
}
correlationPropertyElement.correlationPropertyRetrievalExpression.push(
newRetrievalExpressionElement
);
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
} else {
console.error(
'ERROR: There are not any more correlation properties this message can be added to'
);
}
}
// if (!correlationPropertyElement.correlationPropertyRetrievalExpression) {
// correlationPropertyElement.correlationPropertyRetrievalExpression = [];
// }
// correlationPropertyElement.correlationPropertyRetrievalExpression.push(
// newRetrievalExpressionElement
// );
// commandStack.execute('element.updateProperties', {
// element,
// properties: {},
// });
// } else {
// console.error(
// 'ERROR: There are not any more correlation properties this message can be added to'
// );
// }
// }
const returnObject = { items };
if (allCorrelationPropertyModdleElements.length !== 0) {
returnObject.add = add;
}
return returnObject;
// const returnObject = { items };
// if (allCorrelationPropertyModdleElements.length !== 0) {
// returnObject.add = add;
// }
// return returnObject;
}
function removeFactory(props) {
const {
element,
correlationPropertyModdleElement,
correlationPropertyRetrievalExpressionModdleElement,
commandStack,
} = props;
// function removeFactory(props) {
// const {
// element,
// correlationPropertyModdleElement,
// correlationPropertyRetrievalExpressionModdleElement,
// commandStack,
// } = props;
return function (event) {
event.stopPropagation();
removeFirstInstanceOfItemFromArrayInPlace(
correlationPropertyModdleElement.correlationPropertyRetrievalExpression,
correlationPropertyRetrievalExpressionModdleElement
);
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
};
}
// return function (event) {
// event.stopPropagation();
// removeFirstInstanceOfItemFromArrayInPlace(
// correlationPropertyModdleElement.correlationPropertyRetrievalExpression,
// correlationPropertyRetrievalExpressionModdleElement
// );
// commandStack.execute('element.updateProperties', {
// element,
// properties: {},
// });
// };
// }
function MessageCorrelationPropertyGroup(props) {
const {
idPrefix,
correlationPropertyModdleElement,
correlationPropertyRetrievalExpressionModdleElement,
translate,
moddle,
element,
commandStack,
} = props;
return [
{
id: `${idPrefix}-correlation-key`,
component: MessageCorrelationPropertySelect,
isEdited: isTextFieldEntryEdited,
idPrefix,
correlationPropertyModdleElement,
correlationPropertyRetrievalExpressionModdleElement,
translate,
moddle,
element,
commandStack,
},
{
id: `${idPrefix}-expression`,
component: MessageCorrelationExpressionTextField,
isEdited: isTextFieldEntryEdited,
idPrefix,
correlationPropertyRetrievalExpressionModdleElement,
translate,
},
];
}
// function MessageCorrelationPropertyGroup(props) {
// const {
// idPrefix,
// correlationPropertyModdleElement,
// correlationPropertyRetrievalExpressionModdleElement,
// translate,
// moddle,
// element,
// commandStack,
// } = props;
// return [
// {
// id: `${idPrefix}-correlation-key`,
// component: MessageCorrelationPropertySelect,
// isEdited: isTextFieldEntryEdited,
// idPrefix,
// correlationPropertyModdleElement,
// correlationPropertyRetrievalExpressionModdleElement,
// translate,
// moddle,
// element,
// commandStack,
// },
// {
// id: `${idPrefix}-expression`,
// component: MessageCorrelationExpressionTextField,
// isEdited: isTextFieldEntryEdited,
// idPrefix,
// correlationPropertyRetrievalExpressionModdleElement,
// translate,
// },
// ];
// }
function MessageCorrelationPropertySelect(props) {
const {
idPrefix,
correlationPropertyModdleElement,
correlationPropertyRetrievalExpressionModdleElement,
translate,
parameter,
moddle,
element,
commandStack,
} = props;
const debounce = useService('debounceInput');
// function MessageCorrelationPropertySelect(props) {
// const {
// idPrefix,
// correlationPropertyModdleElement,
// correlationPropertyRetrievalExpressionModdleElement,
// translate,
// parameter,
// moddle,
// element,
// commandStack,
// } = props;
// const debounce = useService('debounceInput');
const setValue = (value) => {
const allCorrelationPropertyModdleElements = findCorrelationProperties(
correlationPropertyModdleElement,
moddle
);
const newCorrelationPropertyElement =
allCorrelationPropertyModdleElements.find((cpe) => cpe.id === value);
// const setValue = (value) => {
// const allCorrelationPropertyModdleElements = findCorrelationProperties(
// correlationPropertyModdleElement,
// moddle
// );
// const newCorrelationPropertyElement =
// allCorrelationPropertyModdleElements.find((cpe) => cpe.id === value);
if (!newCorrelationPropertyElement.correlationPropertyRetrievalExpression) {
newCorrelationPropertyElement.correlationPropertyRetrievalExpression = [];
}
newCorrelationPropertyElement.correlationPropertyRetrievalExpression.push(
correlationPropertyRetrievalExpressionModdleElement
);
removeFirstInstanceOfItemFromArrayInPlace(
correlationPropertyModdleElement.correlationPropertyRetrievalExpression,
correlationPropertyRetrievalExpressionModdleElement
);
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
};
// if (!newCorrelationPropertyElement.correlationPropertyRetrievalExpression) {
// newCorrelationPropertyElement.correlationPropertyRetrievalExpression = [];
// }
// newCorrelationPropertyElement.correlationPropertyRetrievalExpression.push(
// correlationPropertyRetrievalExpressionModdleElement
// );
// removeFirstInstanceOfItemFromArrayInPlace(
// correlationPropertyModdleElement.correlationPropertyRetrievalExpression,
// correlationPropertyRetrievalExpressionModdleElement
// );
// commandStack.execute('element.updateProperties', {
// element,
// properties: {},
// });
// };
const getValue = () => {
return correlationPropertyModdleElement.id;
};
// const getValue = () => {
// return correlationPropertyModdleElement.id;
// };
const getOptions = () => {
const allCorrelationPropertyModdleElements = findCorrelationProperties(
correlationPropertyModdleElement,
moddle
);
const correlationPropertyObjectsForCurrentMessage =
findCorrelationPropertiesAndRetrievalExpressionsForMessage(element);
const options = [];
for (const cpe of allCorrelationPropertyModdleElements) {
const foundElement = correlationPropertyObjectsForCurrentMessage.find(
(cpo) => {
const cpme = cpo.correlationPropertyModdleElement;
return cpme.id === cpe.id;
}
);
if (
!foundElement ||
foundElement.correlationPropertyModdleElement ===
correlationPropertyModdleElement
) {
options.push({
label: cpe.name,
value: cpe.id,
});
}
}
return options;
};
// const getOptions = () => {
// const allCorrelationPropertyModdleElements = findCorrelationProperties(
// correlationPropertyModdleElement,
// moddle
// );
// const correlationPropertyObjectsForCurrentMessage =
// findCorrelationPropertiesAndRetrievalExpressionsForMessage(element);
// const options = [];
// for (const cpe of allCorrelationPropertyModdleElements) {
// const foundElement = correlationPropertyObjectsForCurrentMessage.find(
// (cpo) => {
// const cpme = cpo.correlationPropertyModdleElement;
// return cpme.id === cpe.id;
// }
// );
// if (
// !foundElement ||
// foundElement.correlationPropertyModdleElement ===
// correlationPropertyModdleElement
// ) {
// options.push({
// label: cpe.name,
// value: cpe.id,
// });
// }
// }
// return options;
// };
return SelectEntry({
id: `${idPrefix}-select`,
element: parameter,
label: translate('Correlation Property'),
getValue,
setValue,
getOptions,
debounce,
});
}
// return SelectEntry({
// id: `${idPrefix}-select`,
// element: parameter,
// label: translate('Correlation Property'),
// getValue,
// setValue,
// getOptions,
// debounce,
// });
// }
function MessageCorrelationExpressionTextField(props) {
const {
idPrefix,
parameter,
correlationPropertyRetrievalExpressionModdleElement,
translate,
} = props;
// function MessageCorrelationExpressionTextField(props) {
// const {
// idPrefix,
// parameter,
// correlationPropertyRetrievalExpressionModdleElement,
// translate,
// } = props;
const debounce = useService('debounceInput');
// const debounce = useService('debounceInput');
const setValue = (value) => {
correlationPropertyRetrievalExpressionModdleElement.messagePath.body =
value;
};
// const setValue = (value) => {
// correlationPropertyRetrievalExpressionModdleElement.messagePath.body =
// value;
// };
const getValue = (_parameter) => {
return correlationPropertyRetrievalExpressionModdleElement.messagePath.body;
};
// const getValue = (_parameter) => {
// return correlationPropertyRetrievalExpressionModdleElement.messagePath.body;
// };
return TextFieldEntry({
element: parameter,
id: `${idPrefix}-textField`,
label: translate('Expression'),
getValue,
setValue,
debounce,
});
}
// return TextFieldEntry({
// element: parameter,
// id: `${idPrefix}-textField`,
// label: translate('Expression'),
// getValue,
// setValue,
// debounce,
// });
// }

View File

@ -1,65 +0,0 @@
import React from 'react';
import { useService } from 'bpmn-js-properties-panel';
import { TextAreaEntry } from '@bpmn-io/properties-panel';
import { getMessageElementForShapeElement } from '../MessageHelpers';
/**
* Allows the creation, or editing of messagePayload at the bpmn:sendTask level of a BPMN document.
*/
export function MessagePayload(props) {
const shapeElement = props.element;
const debounce = useService('debounceInput');
const messageElement = getMessageElementForShapeElement(shapeElement);
const disabled = !messageElement;
const getMessagePayloadObject = () => {
if (messageElement) {
const { extensionElements } = messageElement;
if (extensionElements) {
return messageElement.extensionElements
.get('values')
.filter(function getInstanceOfType(e) {
return e.$instanceOf('spiffworkflow:MessagePayload');
})[0];
}
}
return null;
};
const getValue = () => {
const messagePayloadObject = getMessagePayloadObject();
if (messagePayloadObject) {
return messagePayloadObject.value;
}
return '';
};
const setValue = (value) => {
let messagePayloadObject = getMessagePayloadObject();
if (!messagePayloadObject) {
messagePayloadObject = messageElement.$model.create(
'spiffworkflow:MessagePayload',
);
if (!messageElement.extensionElements) {
messageElement.extensionElements = messageElement.$model.create(
'bpmn:ExtensionElements',
);
}
messageElement.extensionElements.get('values').push(messagePayloadObject);
}
messagePayloadObject.value = value;
};
return (
<TextAreaEntry
id="messagePayload"
element={shapeElement}
description="The payload of the message."
label="Payload"
disabled={disabled}
getValue={getValue}
setValue={setValue}
debounce={debounce}
/>
);
}

View File

@ -1,87 +0,0 @@
import React from 'react';
import { useService } from 'bpmn-js-properties-panel';
import { SelectEntry } from '@bpmn-io/properties-panel';
import {
findMessageModdleElements,
getMessageRefElement,
isMessageEvent,
} from '../MessageHelpers';
/**
* Allows the selection, or creation, of Message at the Definitions level of a BPMN document.
*/
export function MessageSelect(props) {
const shapeElement = props.element;
const { commandStack } = props;
const debounce = useService('debounceInput');
const getValue = () => {
const messageRefElement = getMessageRefElement(shapeElement);
if (messageRefElement) {
return messageRefElement.id;
}
return '';
};
const setValue = (value) => {
/* Need to add the selected message as the messageRef on the current message task */
const { businessObject } = shapeElement;
const messages = findMessageModdleElements(shapeElement.businessObject);
for (const message of messages) {
if (message.id === value) {
if (isMessageEvent(shapeElement)) {
const messageEventDefinition = businessObject.eventDefinitions[0];
messageEventDefinition.messageRef = message;
// call this to update the other elements in the props panel like payload
commandStack.execute('element.updateModdleProperties', {
element: shapeElement,
moddleElement: businessObject,
properties: {
messageRef: message,
},
});
} else if (
businessObject.$type === 'bpmn:ReceiveTask' ||
businessObject.$type === 'bpmn:SendTask'
) {
commandStack.execute('element.updateModdleProperties', {
element: shapeElement,
moddleElement: businessObject,
properties: {
messageRef: message,
},
});
commandStack.execute('element.updateProperties', {
element: shapeElement,
moddleElement: businessObject,
properties: {
messageRef: message,
},
});
}
}
}
};
const getOptions = (_value) => {
const messages = findMessageModdleElements(shapeElement.businessObject);
const options = [];
for (const message of messages) {
options.push({ label: message.name, value: message.id });
}
return options;
};
return (
<SelectEntry
id="selectMessage"
element={shapeElement}
description="Select the Message to associate with this task or event."
label="Which message is this associated with?"
getValue={getValue}
setValue={setValue}
getOptions={getOptions}
debounce={debounce}
/>
);
}

View File

@ -1,13 +1,8 @@
import { ListGroup, isTextFieldEntryEdited } from '@bpmn-io/properties-panel';
import { is } from 'bpmn-js/lib/util/ModelUtil';
import { CorrelationKeysArray } from './CorrelationKeysArray';
import { MessageSelect } from './MessageSelect';
import { MessagePayload } from './MessagePayload';
import { MessageVariable } from './MessageVariable';
import { CorrelationPropertiesArray } from './CorrelationPropertiesArray';
import { MessageCorrelationPropertiesArray } from './MessageCorrelationPropertiesArray';
import { MessageArray } from './MessageArray';
import { isMessageElement, canReceiveMessage } from '../MessageHelpers';
import { isMessageElement } from '../MessageHelpers';
import { createCollaborationGroup } from './processLevelProvider/CollaborationPropertiesProvider';
import { createMessageGroup } from './elementLevelProvider/TaskEventMessageProvider';
const LOW_PRIORITY = 500;
@ -20,23 +15,24 @@ export default function MessagesPropertiesProvider(
) {
this.getGroups = function getGroupsCallback(element) {
return function pushGroup(groups) {
if (is(element, 'bpmn:Collaboration')) {
groups.push(
...createCollaborationGroup(
element,
translate,
moddle,
commandStack,
elementRegistry
)
);
} else if (isMessageElement(element)) {
// if (is(element, 'bpmn:Collaboration') || is(element, 'bpmn:Process')) {
// groups.push(
// ...createCollaborationGroup(
// element,
// translate,
// moddle,
// commandStack,
// elementRegistry
// )
// );
// } else
if (isMessageElement(element)) {
const messageIndex = findEntry(groups, 'message');
if (messageIndex) {
groups.splice(messageIndex, 1);
}
groups.push(
createMessageGroup(
...createMessageGroup(
element,
translate,
moddle,
@ -58,6 +54,7 @@ export default function MessagesPropertiesProvider(
});
return entryIndex;
}
propertiesPanel.registerProvider(LOW_PRIORITY, this);
}
@ -67,120 +64,4 @@ MessagesPropertiesProvider.$inject = [
'moddle',
'commandStack',
'elementRegistry',
];
/**
* Adds a group to the properties panel for the script task that allows you
* to set the script.
* @param element
* @param translate
* @returns The components to add to the properties panel. */
function createCollaborationGroup(
element,
translate,
moddle,
commandStack,
elementRegistry
) {
return [
{
id: 'correlation_keys',
label: translate('Correlation Keys'),
component: ListGroup,
...CorrelationKeysArray({
element,
moddle,
commandStack,
elementRegistry,
translate,
}),
},
{
id: 'correlation_properties',
label: translate('Correlation Properties'),
component: ListGroup,
...CorrelationPropertiesArray({
element,
moddle,
commandStack,
elementRegistry,
translate,
}),
},
{
id: 'messages',
label: translate('Messages'),
component: ListGroup,
...MessageArray({
element,
moddle,
commandStack,
elementRegistry,
translate,
}),
},
];
}
/**
* Adds a group to the properties panel for editing messages for the SendTask
* @param element
* @param translate
* @returns The components to add to the properties panel. */
function createMessageGroup(
element,
translate,
moddle,
commandStack,
elementRegistry
) {
const entries = [
{
id: 'selectMessage',
element,
component: MessageSelect,
isEdited: isTextFieldEntryEdited,
moddle,
commandStack,
},
];
if (canReceiveMessage(element)) {
entries.push({
id: 'messageVariable',
element,
component: MessageVariable,
isEdited: isTextFieldEntryEdited,
moddle,
commandStack,
});
} else {
entries.push({
id: 'messagePayload',
element,
component: MessagePayload,
isEdited: isTextFieldEntryEdited,
moddle,
commandStack,
});
}
entries.push({
id: 'correlationProperties',
label: translate('Correlation'),
component: ListGroup,
...MessageCorrelationPropertiesArray({
element,
moddle,
commandStack,
elementRegistry,
translate,
}),
});
return {
id: 'messages',
label: translate('Message'),
entries,
};
}
];

View File

@ -0,0 +1,42 @@
import React from 'react';
import { useService } from 'bpmn-js-properties-panel';
import { CheckboxEntry } from '@bpmn-io/properties-panel';
/**
* A generic properties' editor for text area.
*/
export function CorrelationCheckboxEntry(props) {
const { element, commandStack, moddle } = props;
const { name, label, description } = props;
const { businessObject } = element;
const debounce = useService('debounceInput');
const getValue = () => {
const value = businessObject.get('isCorrelated')
? businessObject.get('isCorrelated')
: false;
return value;
};
const setValue = (value) => {
commandStack.execute('element.updateProperties', {
element,
properties: {
isCorrelated: value,
},
});
};
return (
<CheckboxEntry
id={'correlation_extension_' + name}
element={element}
description={description}
label={label}
getValue={getValue}
setValue={setValue}
debounce={debounce}
/>
);
}

View File

@ -0,0 +1,189 @@
import {
isTextFieldEntryEdited,
TextFieldEntry,
} from '@bpmn-io/properties-panel';
import {
getRoot,
findCorrelationKeyForCorrelationProperty,
findCorrelationPropertiesByMessage,
isMessageEvent,
createNewCorrelationProperty,
getMessageRefElement
} from '../../MessageHelpers';
import { removeFirstInstanceOfItemFromArrayInPlace } from '../../../helpers';
import { useService } from 'bpmn-js-properties-panel';
/**
* Allows the creation, or editing of messageCorrelations at the bpmn:sendTask level of a BPMN document.
*/
export function CorrelationPropertiesList(props) {
const { moddle } = props;
const { element } = props;
const { commandStack } = props;
const { translate } = props;
const correlationPropertyArray = findCorrelationPropertiesByMessage(
element
);
const items = (correlationPropertyArray) ? correlationPropertyArray.map(
(correlationPropertyModdleElement, index) => {
const id = `correlation-${index}`;
const entries = MessageCorrelationPropertyGroup({
idPrefix: id,
correlationPropertyModdleElement,
translate,
element,
commandStack,
moddle,
});
return {
id,
label: correlationPropertyModdleElement.name,
entries,
autoFocusEntry: id,
// remove: removeFactory({
// element,
// correlationPropertyModdleElement,
// commandStack,
// moddle,
// }),
};
}
): [];
function add(event) {
event.stopPropagation();
const messageRef = getMessageRefElement(element);
if(!messageRef){
alert('Please select a message');
return;
}
createNewCorrelationProperty(element, moddle, commandStack, messageRef);
}
return { items, add };
}
function removeFactory(props) {
const { element, correlationPropertyModdleElement, moddle, commandStack } = props;
return function (event) {
event.stopPropagation();
const rootElement = getRoot(element.businessObject);
const { rootElements } = rootElement;
const oldCorrelationKeyElement = findCorrelationKeyForCorrelationProperty(
correlationPropertyModdleElement,
moddle
);
if (oldCorrelationKeyElement) {
removeFirstInstanceOfItemFromArrayInPlace(
oldCorrelationKeyElement.correlationPropertyRef,
correlationPropertyModdleElement
);
}
removeFirstInstanceOfItemFromArrayInPlace(
rootElements,
correlationPropertyModdleElement
);
commandStack.execute('element.updateProperties', {
element,
properties: {
messages: rootElements,
},
});
};
}
function MessageCorrelationPropertyGroup(props) {
const {
idPrefix,
correlationPropertyModdleElement,
translate,
element,
commandStack,
moddle,
} = props;
return [
{
id: `${idPrefix}-correlation-property-retrivial-expression`,
component: CorrelationPropertyRetrivialExpressionTextField,
isEdited: isTextFieldEntryEdited,
idPrefix,
element,
correlationPropertyModdleElement,
translate,
commandStack
},
];
}
function CorrelationPropertyRetrivialExpressionTextField(props) {
const {
id,
element,
correlationPropertyModdleElement,
commandStack,
translate,
idPrefix
} = props;
const debounce = useService('debounceInput');
const setValue = (value) => {
const message = (isMessageEvent(element)) ? element.businessObject.eventDefinitions[0].messageRef : element.businessObject.messageRef;
const process = element.businessObject.$parent;
const definitions = process.$parent;
if (!definitions.get('rootElements')) {
definitions.set('rootElements', []);
}
definitions.rootElements.forEach(rootElement => {
if(rootElement.id == correlationPropertyModdleElement.id && rootElement.$type == 'bpmn:CorrelationProperty'){
const matchingRetrievalExpression = rootElement.correlationPropertyRetrievalExpression.find(expr =>
expr.messageRef && expr.messageRef === message
);
if(matchingRetrievalExpression){
matchingRetrievalExpression.messagePath.body = value;
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
return;
}
}
});
};
const getValue = () => {
const message = (isMessageEvent(element)) ? element.businessObject.eventDefinitions[0].messageRef : element.businessObject.messageRef;
const matchingRetrievalExpression = correlationPropertyModdleElement.correlationPropertyRetrievalExpression.find(expr =>
expr.messageRef && expr.messageRef === message
);
return (matchingRetrievalExpression) ? matchingRetrievalExpression.messagePath.body : '' ;
};
return TextFieldEntry({
element,
id: `${id}-name-xp-textField`,
label: translate('Retrivial Expression'),
getValue,
setValue,
debounce
});
}

View File

@ -0,0 +1,117 @@
import { useService } from 'bpmn-js-properties-panel';
import {
TextFieldEntry,
DescriptionEntry
} from '@bpmn-io/properties-panel';
import {
findCorrelationPropertiesByMessage, isMessageEvent,
} from '../../MessageHelpers';
/**
* Matching correlation conditions
*/
export function MatchingCorrelationEntries(props) {
const {
idPrefix,
translate,
element,
commandStack,
moddle,
} = props;
const correlationPropertyArray = findCorrelationPropertiesByMessage(
element
);
const entries = (correlationPropertyArray && correlationPropertyArray.length !== 0) ? correlationPropertyArray.map(
(correlationPropertyModdleElement, index) => {
return {
id: `${idPrefix}-correlation-property-name`,
component: MatchingConditionTextField,
correlationPropertyModdleElement,
element,
translate,
commandStack,
moddle
};
}
) : [{
id: `${idPrefix}-name-textField`,
component: DescriptionEntry,
value: ' No matching conditions can be established since the selected message has no correlation properties',
element,
translate,
commandStack,
}];
return { entries };
}
function MatchingConditionTextField(props) {
const {
id,
element,
commandStack,
translate,
correlationPropertyModdleElement,
moddle
} = props;
const debounce = useService('debounceInput');
const getVariableCorrelationObject = () => {
if (element) {
const { extensionElements } = (isMessageEvent(element)) ? element.businessObject.eventDefinitions[0] : element.businessObject;
if (extensionElements) {
return extensionElements
.get('values')
.filter(function getInstanceOfType(e) {
return e.$instanceOf('spiffworkflow:ProcessVariableCorrelation') &&
e.propertyId === correlationPropertyModdleElement.id;
})[0];
}
}
return null;
};
const setValue = (value) => {
var extensions = (isMessageEvent(element)) ?
element.businessObject.eventDefinitions[0].get('extensionElements') || moddle.create('bpmn:ExtensionElements') :
element.businessObject.get('extensionElements') || moddle.create('bpmn:ExtensionElements');
let variableCorrelationObject = getVariableCorrelationObject();
if (!variableCorrelationObject) {
variableCorrelationObject = moddle.create('spiffworkflow:ProcessVariableCorrelation', {
propertyId: correlationPropertyModdleElement.id,
expression: value
});
extensions
.get('values')
.push(variableCorrelationObject);
}
variableCorrelationObject.expression = value;
(isMessageEvent(element)) ? element.businessObject.eventDefinitions[0].set('extensionElements', extensions) : element.businessObject.set('extensionElements', extensions);
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
};
const getValue = () => {
const variableCorrelationObject = getVariableCorrelationObject();
if (variableCorrelationObject && variableCorrelationObject.propertyId === correlationPropertyModdleElement.id) {
return variableCorrelationObject.expression;
}
return '';
};
return TextFieldEntry({
element,
id: `${id}-name-textField`,
label: translate(correlationPropertyModdleElement.id),
getValue,
setValue,
debounce,
});
}

View File

@ -0,0 +1,43 @@
import React from 'react';
import { useService } from 'bpmn-js-properties-panel';
import { CheckboxEntry } from '@bpmn-io/properties-panel';
/**
* A generic properties' editor for text area.
*/
export function MatchingCorrelationCheckboxEntry(props) {
const { element, commandStack, moddle } = props;
const { name, label, description } = props;
const { businessObject } = element;
const debounce = useService('debounceInput');
const getValue = () => {
const isMatchingCorrelation = businessObject.get('spiffworkflow:isMatchingCorrelation');
const value = isMatchingCorrelation && isMatchingCorrelation !== 'false'
? true
: false;
return value;
};
const setValue = (value) => {
commandStack.execute('element.updateProperties', {
element,
properties: {
'spiffworkflow:isMatchingCorrelation': value,
},
});
};
return (
<CheckboxEntry
id={'correlation_extension_' + name}
element={element}
description={description}
label={label}
getValue={getValue}
setValue={setValue}
debounce={debounce}
/>
);
}

View File

@ -0,0 +1,134 @@
import { useService } from 'bpmn-js-properties-panel';
import { SelectEntry } from '@bpmn-io/properties-panel';
import { getMessageRefElement, getRoot } from '../../MessageHelpers';
export const spiffExtensionOptions = {};
export function MessageJsonSchemaSelect(props) {
const { id, label, description } = props;
const { element } = props;
const { commandStack } = props;
const debounce = useService('debounceInput');
const eventBus = useService('eventBus');
const bpmnFactory = useService('bpmnFactory');
const optionType = "messages_schemas"
const getValue = () => {
const { businessObject } = element;
const msgRef = getMessageRefElement(element);
if (!msgRef) {
return '';
}
let definitions = getRoot(businessObject);
if (!definitions.get('rootElements')) {
definitions.set('rootElements', []);
}
// Retrieve Message
let bpmnMessage = definitions.get('rootElements').find(element =>
element.$type === 'bpmn:Message' && (element.id === msgRef.id || element.name === msgRef.id)
);
if (!bpmnMessage) {
return '';
}
return bpmnMessage.get('jsonSchemaId');
};
const setValue = (value) => {
const { businessObject } = element;
const msgRef = getMessageRefElement(element);
if (!msgRef) {
alert('Please select a message');
return;
}
let definitions = getRoot(businessObject);
if (!definitions.get('rootElements')) {
definitions.set('rootElements', []);
}
// Retrieve Message
let bpmnMessage = definitions.get('rootElements').find(element =>
element.$type === 'bpmn:Message' && (element.id === msgRef.id || element.name === msgRef.id)
);
bpmnMessage.set('jsonSchemaId', value);
// let extensions = businessObject.extensionElements;
// if (!extensions) {
// extensions = moddle.create('bpmn:ExtensionElements');
// extensions.values = []
// }
// const properties = moddle.create(
// MESSAGE_JSONSCHEMA_PARAMETER_ELEMENT_NAME
// );
// properties.values = []
// const property = moddle.create(
// 'spiffworkflow:Property'
// );
// property.name = "jsonSchemaId";
// property.value = value;
// properties.values.push(property)
// extensions.values.push(properties)
// let definitions = getRoot(businessObject);
// if (!definitions.get('rootElements')) {
// definitions.set('rootElements', []);
// }
};
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.schema_id,
value: opt.schema_id,
});
});
}
return optionList;
};
return SelectEntry({
id,
element,
label,
description,
getValue,
setValue,
getOptions,
debounce,
});
}
function requestOptions(eventBus, element, commandStack, optionType) {
eventBus.on('spiff.message_schemas.returned', (event) => {
spiffExtensionOptions[optionType] = event.options;
});
eventBus.fire(`spiff.message_schemas.requested`, { eventBus });
}

View File

@ -0,0 +1,57 @@
import { HeaderButton } from '@bpmn-io/properties-panel';
import { useService } from 'bpmn-js-properties-panel';
import {
findCorrelationPropertiesAndRetrievalExpressionsForMessage,
getMessageRefElement,
} from '../../MessageHelpers';
/**
* Sends a notification to the host application saying the user
* would like to edit something. Hosting application can then
* update the value and send it back.
*/
export function MessageLaunchEditorButton(props) {
const { element, moddle } = props;
const sendEvent = 'spiff.message.edit';
const listenEvent = 'spiff.message.update';
const eventBus = useService('eventBus');
const messageRef = getMessageRefElement(element);
const correlationProperties =
findCorrelationPropertiesAndRetrievalExpressionsForMessage(element);
const parsedCorrelationProperties = correlationProperties.map((item) => ({
id: item.correlationPropertyModdleElement.id,
retrievalExpression:
item.correlationPropertyRetrievalExpressionModdleElement.messagePath.body,
}));
let messageId = null;
if (messageRef && messageRef.id) {
messageId = messageRef.id;
}
if (messageRef && messageRef.name && messageRef.name !== messageId) {
messageId = messageRef.name;
}
return HeaderButton({
className: 'spiffworkflow-properties-panel-button',
id: `message_launch_message_editor_button`,
onClick: () => {
eventBus.fire(sendEvent, {
value: {
messageId,
correlation_properties: parsedCorrelationProperties,
},
eventBus,
listenEvent,
});
// Listen for a response, to update the script.
eventBus.once(listenEvent, (response) => {
messageRef.id = response.value;
});
},
children: 'Open message editor',
});
}

View File

@ -0,0 +1,104 @@
import React from 'react';
import { useService } from 'bpmn-js-properties-panel';
import { TextAreaEntry } from '@bpmn-io/properties-panel';
import {
getMessageElementForShapeElement,
isMessageEvent,
} from '../../MessageHelpers';
/**
* Allows the creation, or editing of messagePayload at the bpmn:sendTask level of a BPMN document.
*/
export function MessagePayload(props) {
const shapeElement = props.element;
const { moddle, element, commandStack } = props;
const debounce = useService('debounceInput');
const messageElement = getMessageElementForShapeElement(shapeElement);
const disabled = !messageElement;
const getMessagePayloadObject = () => {
if (element) {
const { extensionElements } = isMessageEvent(element)
? element.businessObject.eventDefinitions[0]
: element.businessObject;
if (extensionElements && extensionElements.get('values')) {
let payloadResp = extensionElements
.get('values')
.filter(function getInstanceOfType(e) {
return e.$instanceOf('spiffworkflow:MessagePayload');
})[0];
return payloadResp;
}
}
return null;
};
const getValue = () => {
const messagePayloadObject = getMessagePayloadObject();
if (messagePayloadObject) {
return messagePayloadObject.value;
} else {
// Check : for old models where payload exists on message level
const bo = isMessageEvent(element)
? element.businessObject.eventDefinitions[0]
: element.businessObject;
const { messageRef } = bo;
if (messageRef) {
const { extensionElements } = messageRef;
const payloadResp = (extensionElements) ? extensionElements
.get('values')
.filter(function getInstanceOfType(e) {
return e.$instanceOf('spiffworkflow:MessagePayload');
})[0] : undefined;
if (payloadResp) {
setValue(payloadResp.value);
return payloadResp.value;
}
}
}
return '';
};
const setValue = (value) => {
var extensions = isMessageEvent(element)
? element.businessObject.eventDefinitions[0].get('extensionElements') ||
moddle.create('bpmn:ExtensionElements')
: element.businessObject.get('extensionElements') ||
moddle.create('bpmn:ExtensionElements');
let messagePayloadObject = getMessagePayloadObject();
if (!messagePayloadObject) {
messagePayloadObject = moddle.create('spiffworkflow:MessagePayload');
extensions.get('values').push(messagePayloadObject);
}
messagePayloadObject.value = value;
isMessageEvent(element)
? element.businessObject.eventDefinitions[0].set(
'extensionElements',
extensions
)
: element.businessObject.set('extensionElements', extensions);
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
};
return (
<TextAreaEntry
id="messagePayload"
element={shapeElement}
description="Enter a JSON object to define the message payload directly or provide the variable name that holds the payload data."
label="Payload"
disabled={disabled}
getValue={getValue}
setValue={setValue}
debounce={debounce}
/>
);
}

View File

@ -0,0 +1,206 @@
import React from 'react';
import { useService } from 'bpmn-js-properties-panel';
import { SelectEntry } from '@bpmn-io/properties-panel';
import {
createOrUpdateCorrelationProperties,
createOrUpdateCorrelationPropertiesV2,
findMessageModdleElements,
getMessageRefElement,
getRoot,
isMessageElement,
isMessageEvent,
isMessageRefUsed,
setParentCorrelationKeys,
synCorrleationProperties,
} from '../../MessageHelpers';
export const spiffExtensionOptions = {};
/**
* Allows the selection, or creation, of Message at the Definitions level of a BPMN document.
*/
export function MessageSelect(props) {
const shapeElement = props.element;
const { commandStack, element, moddle } = props;
const debounce = useService('debounceInput');
const eventBus = useService('eventBus');
const bpmnFactory = useService('bpmnFactory');
const getValue = () => {
const messageRefElement = getMessageRefElement(shapeElement);
if (messageRefElement) {
return messageRefElement.id;
}
return '';
};
const setValue = async (value) => {
// Define variables
const messageId = value;
const { businessObject } = element;
let oldMessageRef = (businessObject.eventDefinitions) ? businessObject.eventDefinitions[0].messageRef : businessObject.messageRef;
let definitions = getRoot(element.businessObject);
if (!definitions || !definitions.get('rootElements')) {
definitions.set('rootElements', []);
}
// Retrieve Message
let bpmnMessage = definitions
.get('rootElements')
.find(
(element) =>
element.$type === 'bpmn:Message' &&
(element.id === messageId || element.name === messageId),
);
// If the Message doesn't exist, create new one
if (!bpmnMessage) {
bpmnMessage = bpmnFactory.create('bpmn:Message', {
id: messageId,
name: messageId,
});
definitions.get('rootElements').push(bpmnMessage);
}
// Update messageRef of current Element
if (isMessageEvent(shapeElement)) {
element.businessObject.eventDefinitions[0].set(
'extensionElements',
moddle.create('bpmn:ExtensionElements'),
); // Clear extension element
const messageEventDefinition = element.businessObject.eventDefinitions[0];
messageEventDefinition.messageRef = bpmnMessage;
// call this to update the other elements in the props panel like payload
commandStack.execute('element.updateModdleProperties', {
element: element,
moddleElement: element.businessObject,
properties: {},
});
} else if (isMessageElement(shapeElement)) {
element.businessObject.set(
'extensionElements',
moddle.create('bpmn:ExtensionElements'),
); // Clear extension element
element.businessObject.messageRef = bpmnMessage;
commandStack.execute('element.updateProperties', {
element: element,
properties: {},
});
}
// Add Correlation Properties of for the new message
const msgObject = spiffExtensionOptions['spiff.messages']
? spiffExtensionOptions['spiff.messages'].find(
(msg) => msg.identifier === messageId,
)
: undefined;
if (msgObject) {
createOrUpdateCorrelationPropertiesV2(
bpmnFactory,
commandStack,
element,
msgObject['correlation_properties'],
messageId,
);
}
if (oldMessageRef) {
// Remove previous message in case it's not used anymore
const isOldMessageUsed = isMessageRefUsed(definitions, oldMessageRef.id);
if (!isOldMessageUsed) {
const rootElements = definitions.get('rootElements');
const oldMessageIndex = rootElements.findIndex(
(element) =>
element.$type === 'bpmn:Message' && element.id === oldMessageRef.id,
);
if (oldMessageIndex !== -1) {
rootElements.splice(oldMessageIndex, 1);
definitions.rootElements = rootElements;
}
}
// Automatic deletion of previous message correlation properties
synCorrleationProperties(element, definitions, moddle);
}
// Update Correlation key if Process has collaboration
try {
setParentCorrelationKeys(definitions, bpmnFactory, element, moddle);
} catch (error) {
console.error('Error Caught while synchronizing Correlation key', error);
}
};
eventBus.on('spiff.add_message.returned', (event) => {
const cProperties = Object.entries(event.correlation_properties).map(([identifier, properties]) => ({
identifier,
retrieval_expression: properties.retrieval_expressions[0]
}));
let newMsg = {
identifier: event.name,
correlation_properties: cProperties
};
spiffExtensionOptions['spiff.messages'] = (Array.isArray(spiffExtensionOptions['spiff.messages']) && spiffExtensionOptions['spiff.messages']) ? spiffExtensionOptions['spiff.messages'] : [];
spiffExtensionOptions['spiff.messages'].push(newMsg);
setValue(event.name);
});
requestOptions(eventBus, bpmnFactory, element, moddle);
const getOptions = () => {
// Load messages from XML
const options = [];
const messages = findMessageModdleElements(shapeElement.businessObject);
for (const message of messages) {
options.push({ label: message.name, value: message.id });
}
// Load messages from API
if (
spiffExtensionOptions['spiff.messages'] &&
spiffExtensionOptions['spiff.messages'] !== null
) {
spiffExtensionOptions['spiff.messages'].forEach((opt) => {
options.push({
label: opt.identifier,
value: opt.identifier,
});
});
}
// Remove duplicated options
const uniqueArray = removeDuplicatesByLabel(options);
return uniqueArray;
};
return (
<SelectEntry
id="selectMessage"
element={shapeElement}
description="Select the Message to associate with this task or event."
label="Which message is this associated with?"
getValue={getValue}
setValue={setValue}
getOptions={getOptions}
debounce={debounce}
/>
);
}
function requestOptions(eventBus, bpmnFactory, element, moddle) {
eventBus.on(`spiff.messages.returned`, (event) => {
spiffExtensionOptions['spiff.messages'] = event.configuration.messages;
});
eventBus.fire(`spiff.messages.requested`, { eventBus });
}
function removeDuplicatesByLabel(array) {
const seen = new Map();
return array.filter((item) => {
return seen.has(item.label) ? false : seen.set(item.label, true);
});
}

View File

@ -1,22 +1,23 @@
import React from 'react';
import { useService } from 'bpmn-js-properties-panel';
import { TextFieldEntry } from '@bpmn-io/properties-panel';
import { getMessageElementForShapeElement } from '../MessageHelpers';
import { getMessageElementForShapeElement, isMessageEvent } from '../../MessageHelpers';
/**
* Allows the creation, or editing of messageVariable at the bpmn:sendTask level of a BPMN document.
*/
export function MessageVariable(props) {
const shapeElement = props.element;
const { moddle, element, commandStack } = props;
const debounce = useService('debounceInput');
const messageElement = getMessageElementForShapeElement(shapeElement);
const disabled = !messageElement;
const getMessageVariableObject = () => {
if (messageElement) {
const { extensionElements } = messageElement;
if (element) {
const { extensionElements } = (isMessageEvent(element)) ? element.businessObject.eventDefinitions[0] : element.businessObject;
if (extensionElements) {
return messageElement.extensionElements
return extensionElements
.get('values')
.filter(function getInstanceOfType(e) {
return e.$instanceOf('spiffworkflow:MessageVariable');
@ -35,21 +36,25 @@ export function MessageVariable(props) {
};
const setValue = (value) => {
var extensions = (isMessageEvent(element)) ?
element.businessObject.eventDefinitions[0].get('extensionElements') || moddle.create('bpmn:ExtensionElements') :
element.businessObject.get('extensionElements') || moddle.create('bpmn:ExtensionElements');
let messageVariableObject = getMessageVariableObject();
if (!messageVariableObject) {
messageVariableObject = messageElement.$model.create(
messageVariableObject = moddle.create(
'spiffworkflow:MessageVariable'
);
if (!messageElement.extensionElements) {
messageElement.extensionElements = messageElement.$model.create(
'bpmn:ExtensionElements'
);
}
messageElement.extensionElements
extensions
.get('values')
.push(messageVariableObject);
}
messageVariableObject.value = value;
(isMessageEvent(element)) ? element.businessObject.eventDefinitions[0].set('extensionElements', extensions) : element.businessObject.set('extensionElements', extensions);
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
};
return (

View File

@ -0,0 +1,199 @@
import { useService } from "bpmn-js-properties-panel";
import { HeaderButton, ListGroup, isTextFieldEntryEdited } from '@bpmn-io/properties-panel';
import { canReceiveMessage, getMessageRefElement, getRoot } from "../../MessageHelpers";
import { CorrelationPropertiesList } from './CorrelationPropertiesList';
import { MessageSelect } from './MessageSelect';
import { MessagePayload } from './MessagePayload';
import { CorrelationCheckboxEntry } from './CorrelationCheckbox';
import { MessageJsonSchemaSelect } from './MessageJsonSchemaSelect';
import { MessageVariable } from './MessageVariable';
import { MessageLaunchEditorButton } from "./MessageLaunchEditorButton";
import { MatchingCorrelationEntries } from "./MatchingConditionArray";
import { MatchingCorrelationCheckboxEntry } from "./MatchingCorrelationCheckbox";
/**
* Adds a group to the properties panel for editing messages for the SendTask
* @param element
* @param translate
* @returns The components to add to the properties panel. */
export function createMessageGroup(
element,
translate,
moddle,
commandStack,
elementRegistry
) {
const { businessObject } = element;
const entries = [
{
id: 'selectMessage',
element,
component: MessageSelect,
isEdited: isTextFieldEntryEdited,
moddle,
commandStack,
},
{
id: 'messageLaunchEditorButton',
element,
translate,
component: MessageLaunchEditorButton,
moddle
}
];
if (canReceiveMessage(element)) {
entries.push({
id: 'messageVariable',
element,
component: MessageVariable,
isEdited: isTextFieldEntryEdited,
moddle,
commandStack,
});
} else {
entries.push({
id: 'messagePayload',
element,
component: MessagePayload,
isEdited: isTextFieldEntryEdited,
moddle,
commandStack,
});
}
if (canReceiveMessage(element)) {
// Given the user the possibility to either enable/disable showing correlations conditions.
entries.push({
id: 'spiffworkflow:isMatchingCorrelation',
element,
moddle,
commandStack,
component: MatchingCorrelationCheckboxEntry,
name: 'enable.correlation',
label: translate('Enable Condition Matching'),
description: 'Determine when this message should be received based on matching data values.',
});
}
// Given the user the possibility to either enable/disable showing correlations.
// entries.push({
// id: 'isCorrelated',
// element,
// moddle,
// commandStack,
// component: CorrelationCheckboxEntry,
// name: 'enable.correlation',
// label: translate('Enable Correlation'),
// description: 'You can define specific correlation properties for your message.',
// });
var results = [
{
id: 'messages',
label: translate('Message'),
isDefault: true,
entries,
}
]
// Showing Correlation Properties Group if correlation is enabled
// if (businessObject.get('isCorrelated')) {
// results.push({
// id: 'correlationProperties',
// label: translate('Correlation Properties'),
// isDefault: true,
// component: ListGroup,
// ...CorrelationPropertiesList({
// element,
// moddle,
// commandStack,
// elementRegistry,
// translate,
// }),
// })
// }
// Adding JsonSchema Group
// results.push({
// id: 'messageSchema',
// label: translate('Json-Schema'),
// entries: [
// {
// component: MessageJsonSchemaSelect,
// element,
// name: 'msgJsonSchema',
// label: translate('Define JSON Schema'),
// description: translate('Select a JSON schema for your message'),
// moddle,
// commandStack
// },
// {
// component: LaunchJsonSchemaEditorButton,
// element,
// name: 'messageRef',
// label: translate('Launch Editor')
// }
// ]
// })
// Adding Correlation Conditions Section
const isMatchingCorrelation = businessObject.get('spiffworkflow:isMatchingCorrelation');
if (isMatchingCorrelation && isMatchingCorrelation !== "false" && canReceiveMessage(element)) {
results.push({
id: "correlationConditions",
label: translate('Matching Conditions'),
...MatchingCorrelationEntries({
element,
moddle,
commandStack,
translate,
})
})
}
return results;
}
function LaunchJsonSchemaEditorButton(props) {
const { element } = props;
const eventBus = useService('eventBus');
return HeaderButton({
id: 'spiffworkflow-search-call-activity-button',
class: 'spiffworkflow-properties-panel-button',
children: 'Launch Editor',
onClick: () => {
const { businessObject } = element;
const msgRef = getMessageRefElement(element);
if (!msgRef) {
alert('Please select a message');
return '';
}
let definitions = getRoot(businessObject);
if (!definitions.get('rootElements')) {
definitions.set('rootElements', []);
}
// Retrieve Message
let bpmnMessage = definitions.get('rootElements').find(element =>
element.$type === 'bpmn:Message' && (element.id === msgRef.id || element.name === msgRef.id)
);
if (!bpmnMessage) {
alert('Error : Message not found!');
return '';
}
eventBus.fire('spiff.msg_json_schema_editor.requested', {
messageId: msgRef.name,
schemaId: bpmnMessage.get('jsonSchemaId'),
eventBus,
element
});
}
});
}

View File

@ -0,0 +1,69 @@
import { MessageArray } from './MessageArray';
import { CorrelationKeysArray } from './CorrelationKeysArray';
import { CorrelationPropertiesArray } from './CorrelationPropertiesArray';
import { ListGroup } from '@bpmn-io/properties-panel';
/**
* Adds a group to the properties panel for the script task that allows you
* to set the script.
* @param element
* @param translate
* @returns The components to add to the properties panel. */
export function createCollaborationGroup(
element,
translate,
moddle,
commandStack,
elementRegistry
) {
const results = [
{
id: 'messages',
label: translate('Messages'),
isDefault: true,
component: ListGroup,
...MessageArray({
element,
moddle,
commandStack,
elementRegistry,
translate,
}),
},
{
id: 'correlation_properties',
class: 'correlation_properties',
className: 'correlation_properties',
label: translate('Correlation Properties'),
isDefault: true,
component: ListGroup,
...CorrelationPropertiesArray({
element,
moddle,
commandStack,
elementRegistry,
translate,
}),
}
];
// Hide Correlation Keys
// if (element.type === 'bpmn:Collaboration') {
// results.push({
// id: 'correlation_keys',
// label: translate('Correlation Keys'),
// isDefault: true,
// component: ListGroup,
// ...CorrelationKeysArray({
// element,
// moddle,
// commandStack,
// elementRegistry,
// translate,
// }),
// })
// }
return results;
}

View File

@ -1,7 +1,7 @@
import { useService } from 'bpmn-js-properties-panel';
import { SimpleEntry, TextFieldEntry } from '@bpmn-io/properties-panel';
import { findCorrelationKeys, getRoot } from '../MessageHelpers';
import { removeFirstInstanceOfItemFromArrayInPlace } from '../../helpers';
import { createNewCorrelationKey, findCorrelationKeys, getRoot } from '../../MessageHelpers';
import { removeFirstInstanceOfItemFromArrayInPlace } from '../../../helpers';
/**
* Provides a list of data objects, and allows you to add / remove data objects, and change their ids.
@ -12,6 +12,7 @@ export function CorrelationKeysArray(props) {
const { element, moddle, commandStack } = props;
const correlationKeyElements = findCorrelationKeys(element.businessObject);
const items = correlationKeyElements.map((correlationKeyElement, index) => {
const id = `correlationGroup-${index}`;
return {
@ -35,18 +36,7 @@ export function CorrelationKeysArray(props) {
function add(event) {
event.stopPropagation();
if (element.type === 'bpmn:Collaboration') {
const newCorrelationKeyElement = moddle.create('bpmn:CorrelationKey');
newCorrelationKeyElement.name =
moddle.ids.nextPrefixed('CorrelationKey_');
const currentCorrelationKeyElements =
element.businessObject.get('correlationKeys');
currentCorrelationKeyElements.push(newCorrelationKeyElement);
commandStack.execute('element.updateProperties', {
element,
properties: {}
});
}
createNewCorrelationKey(element, moddle, commandStack);
}
return { items, add };
@ -70,11 +60,6 @@ function removeFactory(props) {
};
}
// <bpmn:correlationKey name="lover"> <--- The correlationGroup
// <bpmn:correlationPropertyRef>lover_name</bpmn:correlationPropertyRef>
// <bpmn:correlationPropertyRef>lover_instrument</bpmn:correlationPropertyRef>
// </bpmn:correlationKey>
// <bpmn:correlationKey name="singer" />
function correlationGroup(props) {
const { idPrefix, correlationKeyElement, commandStack } = props;
const entries = [

View File

@ -0,0 +1,150 @@
import { useService } from 'bpmn-js-properties-panel';
import {
isTextFieldEntryEdited,
TextFieldEntry,
} from '@bpmn-io/properties-panel';
import {
getRoot,
findCorrelationProperties,
findCorrelationKeyForCorrelationProperty,
createNewCorrelationProperty,
} from '../../MessageHelpers';
import { removeFirstInstanceOfItemFromArrayInPlace } from '../../../helpers';
/**
* Allows the creation, or editing of messageCorrelations at the bpmn:sendTask level of a BPMN document.
*/
export function CorrelationPropertiesArray(props) {
const { moddle } = props;
const { element } = props;
const { commandStack } = props;
const { translate } = props;
const correlationPropertyArray = findCorrelationProperties(
element.businessObject
);
const items = correlationPropertyArray.map(
(correlationPropertyModdleElement, index) => {
const id = `correlation-${index}`;
const entries = MessageCorrelationPropertyGroup({
idPrefix: id,
correlationPropertyModdleElement,
translate,
element,
commandStack,
moddle,
});
return {
id,
label: correlationPropertyModdleElement.name,
entries,
autoFocusEntry: id,
remove: removeFactory({
element,
correlationPropertyModdleElement,
commandStack,
moddle,
}),
};
}
);
function add(event) {
event.stopPropagation();
createNewCorrelationProperty(element, moddle, commandStack);
}
return { items, add };
}
function removeFactory(props) {
const { element, correlationPropertyModdleElement, moddle, commandStack } =
props;
return function (event) {
event.stopPropagation();
const rootElement = getRoot(element.businessObject);
const { rootElements } = rootElement;
const oldCorrelationKeyElement = findCorrelationKeyForCorrelationProperty(
correlationPropertyModdleElement,
moddle
);
if (oldCorrelationKeyElement) {
removeFirstInstanceOfItemFromArrayInPlace(
oldCorrelationKeyElement.correlationPropertyRef,
correlationPropertyModdleElement
);
}
removeFirstInstanceOfItemFromArrayInPlace(
rootElements,
correlationPropertyModdleElement
);
commandStack.execute('element.updateProperties', {
element,
properties: {
messages: rootElements,
},
});
};
}
function MessageCorrelationPropertyGroup(props) {
const {
idPrefix,
correlationPropertyModdleElement,
translate,
element,
commandStack,
moddle,
} = props;
return [
{
id: `${idPrefix}-correlation-property-name`,
component: CorrelationPropertyNameTextField,
isEdited: isTextFieldEntryEdited,
idPrefix,
element,
correlationPropertyModdleElement,
translate,
commandStack,
},
];
}
function CorrelationPropertyNameTextField(props) {
const {
id,
element,
correlationPropertyModdleElement,
commandStack,
translate,
} = props;
const debounce = useService('debounceInput');
const setValue = (value) => {
commandStack.execute('element.updateModdleProperties', {
element,
moddleElement: correlationPropertyModdleElement,
properties: {
name: value,
id: value,
},
});
};
const getValue = () => {
return correlationPropertyModdleElement.name;
};
return TextFieldEntry({
element,
id: `${id}-name-textField`,
label: translate('Name'),
getValue,
setValue,
debounce,
});
}

View File

@ -1,7 +1,8 @@
import { useService } from 'bpmn-js-properties-panel';
import { TextFieldEntry } from '@bpmn-io/properties-panel';
import { getRoot, findMessageModdleElements } from '../MessageHelpers';
import { removeFirstInstanceOfItemFromArrayInPlace } from '../../helpers';
import { getRoot, findMessageModdleElements, createNewMessage } from '../../MessageHelpers';
import { removeFirstInstanceOfItemFromArrayInPlace } from '../../../helpers';
import { MessagePropertiesMultiSelect } from './MessagePropertiesMultiSelect';
/**
* Provides a list of data objects, and allows you to add / remove data objects, and change their ids.
@ -10,8 +11,8 @@ import { removeFirstInstanceOfItemFromArrayInPlace } from '../../helpers';
*/
export function MessageArray(props) {
const { element, moddle, commandStack, translate } = props;
const messageElements = findMessageModdleElements(element.businessObject);
const items = messageElements.map((messageElement, index) => {
const id = `messageElement-${index}`;
return {
@ -22,6 +23,7 @@ export function MessageArray(props) {
element,
messageElement,
commandStack,
moddle,
translate,
}),
autoFocusEntry: id,
@ -36,19 +38,7 @@ export function MessageArray(props) {
function add(event) {
event.stopPropagation();
if (element.type === 'bpmn:Collaboration') {
const newMessageElement = moddle.create('bpmn:Message');
const messageId = moddle.ids.nextPrefixed('Message_');
newMessageElement.id = messageId;
newMessageElement.name = messageId;
const rootElement = getRoot(element.businessObject);
const { rootElements } = rootElement;
rootElements.push(newMessageElement);
commandStack.execute('element.updateProperties', {
element,
properties: {},
});
}
createNewMessage(element, moddle, commandStack);
}
return { items, add };
@ -94,7 +84,7 @@ function removeFactory(props) {
}
function messageGroup(props) {
const { messageElement, commandStack, translate, idPrefix } = props;
const { messageElement, commandStack, moddle, translate, idPrefix } = props;
return [
{
id: `${idPrefix}-name`,
@ -103,6 +93,14 @@ function messageGroup(props) {
commandStack,
translate,
},
{
id: `${idPrefix}-properties`,
component: MessagePropertiesSelectField,
messageElement,
commandStack,
moddle,
translate,
}
];
}
@ -161,3 +159,18 @@ function MessageNameTextField(props) {
debounce,
});
}
function MessagePropertiesSelectField(props) {
const { id, element, moddle, translate, messageElement } = props;
const debounce = useService('debounceInput');
return MessagePropertiesMultiSelect({
element,
id: `${id}-properties-input`,
label: translate('Properties'),
debounce,
messageElement,
moddle
});
}

View File

@ -0,0 +1,87 @@
import { html } from 'htm/preact';
import { useService } from 'bpmn-js-properties-panel';
import { getCorrelationPropertiesIDsFiltredByMessageRef, setMessageRefToListofCorrelationProperties } from '../../MessageHelpers';
import NiceSelect from 'nice-select2/dist/js/nice-select2';
let niceSelectInputs = {};
export function MessagePropertiesMultiSelect(props) {
const { element, id, moddle, messageElement, commandStack } = props;
const modeling = useService('modeling');
const translate = useService('translate');
const debounce = useService('debounceInput');
const setValue = (value) => {
// Add message ref to the selected correlation properties
setMessageRefToListofCorrelationProperties(messageElement, value, element, moddle, commandStack)
};
const getOptions = () => {
const correlationProperties = getCorrelationPropertiesIDsFiltredByMessageRef(element.businessObject, moddle, messageElement);
return correlationProperties;
};
function handleSelectChange(e) {
const selectedOptions = Array.from(e.target.selectedOptions).map(option => option.value);
setValue(selectedOptions);
}
const initializeNiceSelect = () => {
const selectElement = document.getElementById(`${id}_select`);
if (!selectElement) return;
if (niceSelectInputs[id] && niceSelectInputs[id] !== null ) {
niceSelectInputs[id].destroy();
}
const opts = {
// data: options,
searchable: true,
placeholder: 'Select message properties',
showSelectedItems: true
};
niceSelectInputs[id] = new NiceSelect(selectElement, opts);
updateSelectOptions(selectElement)
};
function updateSelectOptions(selectElement) {
for (let option of selectElement.options) {
const matchingOption = options.find(opt => opt.value === option.value);
if (matchingOption && matchingOption.selected) {
option.setAttribute('selected', 'true');
}
}
}
const renderSelect = (options) => {
return html`
<select id='${id}_select' class="wipe" multiple onchange=${(e) => handleSelectChange(e)}>
${options.length === 0 ? html`
<option disabled>${translate('No elements found')}</option>
` : options.map(option => html`
<option value=${option.value} ${option.selected ? 'selected="true"' : ''}>${translate(option.text)}</option>
`)}
</select>
`;
}
const options = getOptions();
setTimeout(() => {
initializeNiceSelect();
}, 0);
return html`
<div class="bio-properties-panel-entry d-grid">
<label class="bio-properties-panel-label">${translate('Correlation Properties')}</label>
${renderSelect(options)}
</div>
`;
}

View File

@ -49,6 +49,26 @@
}
]
},
{
"name": "ProcessVariableCorrelation",
"superClass": [ "Element" ],
"properties": [
{
"name": "propertyId",
"type": "String",
"xml": {
"serialize": "property"
}
},
{
"name": "expression",
"type": "String",
"xml": {
"serialize": "property"
}
}
]
},
{
"name": "CalledDecisionId",
"superClass": [ "Element" ],

501
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -83,6 +83,7 @@
"min-dash": "^3.8.1",
"min-dom": "^3.2.1",
"moddle": "^5.0.3",
"nice-select2": "^2.1.0",
"react": "^18.2.0",
"react-dom": "18.2.0",
"tiny-svg": "^2.2.3"

View File

@ -1,4 +1,7 @@
import TestContainer from 'mocha-test-container-support';
import {
query as domQuery,
} from 'min-dom';
import {
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
@ -8,15 +11,20 @@ import {
expectSelected,
findEntry,
findGroupEntry,
// findInput,
findSelect,
findTextarea,
// findButtonByClass,
// pressButton,
// findDivByClass,
findInput,
pressButton,
findButtonByClass,
getPropertiesPanel,
changeInput,
findDivByClass
} from './helpers';
import spiffModdleExtension from '../../app/spiffworkflow/moddle/spiffworkflow.json';
import messages from '../../app/spiffworkflow/messages';
import { fireEvent } from '@testing-library/preact';
import { getBpmnJS, inject } from 'bpmn-js/test/helper';
import { findCorrelationProperties, findMessageModdleElements } from '../../app/spiffworkflow/messages/MessageHelpers';
describe('Messages should work', function () {
const xml = require('./bpmn/collaboration.bpmn').default;
@ -41,12 +49,23 @@ describe('Messages should work', function () {
})
);
it('should allow you to see the collaborations section', async function () {
// THEN - a select Data Object section should appear in the properties panel
const entry = findGroupEntry('correlation_keys', container);
expect(entry).to.exist;
await expectSelected('my_collaboration');
});
const new_message_event = (eventBus) => {
eventBus.fire('spiff.add_message.returned', {
name: 'msgName',
correlation_properties: {
"c1": {
"retrieval_expressions": [
"c1x"
]
},
"c2": {
"retrieval_expressions": [
"cxxxx1x"
]
}
}
});
};
it('should show a Message Properties group when a send task is selected', async function () {
// Select the send Task
@ -80,7 +99,7 @@ describe('Messages should work', function () {
// Select the second Task
const sendShape = await expectSelected('ActivitySendLetter');
expect(sendShape, "Can't find Send Task").to.exist;
// THEN - there is a payload.
const payload = findEntry('messagePayload', container);
expect(payload, "Can't find the message payload").to.exist;
@ -95,42 +114,152 @@ describe('Messages should work', function () {
await expectSelected('my_collaboration');
});
it('should show the correlations inside the message group', async function () {
// Select the second Task
it('should be able to create new message from sendMessageTask if no message is found', inject(async function (canvas, modeling) {
let rootShape = canvas.getRootElement();
// Retrieve the current number of existing messages
const messageElemendBeforeDelete = findMessageModdleElements(rootShape.businessObject);
expect(messageElemendBeforeDelete.length).to.equal(2);
// Select message element
const sendShape = await expectSelected('ActivitySendLetter');
expect(sendShape, "Can't find Send Task").to.exist;
// THEN - there are correlations.
const correlations = findGroupEntry('correlationProperties', container);
expect(correlations, "Can't find the message correlations").to.exist;
await expectSelected('my_collaboration');
const entry = findEntry('selectMessage', getPropertiesPanel());
expect(entry).to.exist;
let selector = findSelect(entry);
expect(selector.options.length).to.equal(2);
changeInput(selector, 'love_letter_response');
const messageElemend = findMessageModdleElements(rootShape.businessObject);
expect(messageElemend.length).to.equal(2);
}));
it('should be able to add new message and correlation properties on add_message_event', async function () {
const modeler = getBpmnJS();
// Select message element
const sendShape = await expectSelected('ActivitySendLetter');
expect(sendShape, "Can't find Send Task").to.exist;
const entry = findEntry('selectMessage', getPropertiesPanel());
expect(entry).to.exist;
// Expect to find two existing messages
let selector = findSelect(entry);
expect(selector.options.length).to.equal(2);
// Fire add new message event
modeler.get('eventBus').on('spiff.add_message.requested', (event) => {
new_message_event(modeler.get('eventBus'))
});
modeler.get('eventBus').fire('spiff.add_message.requested');
const sendShapecc = await expectSelected('ActivitySendLetter');
expect(sendShapecc, "Can't find Send Task").to.exist;
const updatedEntry = findEntry('selectMessage', getPropertiesPanel());
expect(updatedEntry).to.exist;
const updatedSelector = findSelect(updatedEntry);
expect(updatedSelector.options.length).to.equal(3);
expect(updatedSelector.options[2].value).to.equal('msgName');
});
// it('should add a new correlation when clicked', async function () {
it('should be able to generate default Correlation keys on changing message', async function () {
})
// 🔶🔶 OLD Features
// // it('should show the correlations inside the message group', async function () {
// // // Select the second Task
// // const sendShape = await expectSelected('ActivitySendLetter');
// // expect(sendShape, "Can't find Send Task").to.exist;
// // // Enable correlation
// // const checkbox = findInput('checkbox', container);
// // pressButton(checkbox);
// // // THEN - there are correlations.
// // const correlations = findGroupEntry('correlationProperties', container);
// // expect(correlations, "Can't find the message correlations").to.exist;
// // await expectSelected('my_collaboration');
// // });
// it('should not showing the correlations inside the message group if is not enabled', async function () {
// // Select the second Task
// const sendShape = await expectSelected('ActivitySendLetter');
// expect(sendShape, "Can't find Send Task").to.exist;
//
// const buttonClass =
// 'bio-properties-panel-group-header-button bio-properties-panel-add-entry';
// const button = findButtonByClass(buttonClass, container);
// pressButton(button);
//
// // Check correlation properties
// const correlations = findGroupEntry('correlationProperties', container);
// expect(correlations, "Can't find the message correlations").not.to.exist;
// });
//
// // Old Feature
// // it('should add a new correlation when clicked', async function () {
// // // Select the second Task
// // const sendShape = await expectSelected('ActivitySendLetter');
// // expect(sendShape, "Can't find Send Task").to.exist;
// // // Enable correlation
// // const checkbox = findInput('checkbox', container);
// // pressButton(checkbox);
// // const buttonClass =
// // 'bio-properties-panel-group-header-button bio-properties-panel-add-entry';
// // const button = findButtonByClass(buttonClass, container);
// // pressButton(button);
// // });
// it('should add a new Correlation Key when clicked', async function () {
// const divClass = 'bio-properties-panel-list';
// const divs = findDivByClass(divClass, container);
//
// const buttonClass =
// 'bio-properties-panel-group-header-button bio-properties-panel-add-entry';
// const button = findButtonByClass(buttonClass, container);
// pressButton(button);
//
// // THEN - a select Data Object section should appear in the properties panel
// const entry = findGroupEntry('correlation_keys', container);
// expect(entry).to.exist;
//
// const divs2 = findDivByClass(divClass, container);
// });
// it('should be able to delete an existing message', inject(function (canvas, modeling) {
// let rootShape = canvas.getRootElement();
// // Retrieve current number of existing messages
// const messageElemendBeforeDelete = findMessageModdleElements(rootShape.businessObject);
// expect(messageElemendBeforeDelete.length).to.equal(2);
// // Trigger delete action
// let deleteButton = domQuery('.bio-properties-panel-remove-entry', container);
// fireEvent.click(deleteButton);
// const messageElemendAfterDelete = findMessageModdleElements(rootShape.businessObject);
// expect(messageElemendAfterDelete.length).to.equal(1);
// }));
// it('should be able to delete an existing correlation property', inject(function (canvas, modeling) {
// let rootShape = canvas.getRootElement();
// // Retrieve current number of existing messages
// const correlationPropertiesBeforeDelete = findCorrelationProperties(rootShape.businessObject);
// expect(correlationPropertiesBeforeDelete.length).to.equal(3);
// // Trigger delete action
// const correlationPropertiesDiv = domQuery(`div[data-group-id='group-correlation_properties']`, container);
// let deleteButton = domQuery('.bio-properties-panel-remove-entry', correlationPropertiesDiv);
// fireEvent.click(deleteButton);
// const correlationPropertiesAfterDelete = findCorrelationProperties(rootShape.businessObject);
// expect(correlationPropertiesAfterDelete.length).to.equal(2);
// }));
// it('should allow you to see the collaborations section', async function () {
// // THEN - a select Data Object section should appear in the properties panel
// const entry = findGroupEntry('correlation_keys', container);
// expect(entry).to.exist;
// await expectSelected('my_collaboration');
// });
});

View File

@ -1,10 +1,18 @@
<?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:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0qmxumb" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.0.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.17.0">
<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:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core"
xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0qmxumb"
targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.0.0"
modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.17.0">
<bpmn:collaboration id="my_collaboration">
<bpmn:participant id="buddy" name="Buddy" processRef="process_buddy" />
<bpmn:participant id="Person" name="Person" processRef="random_person_process" />
<bpmn:participant id="Participant_1tlkvw1" name="Tom Petty" processRef="Process_04o9jmt" />
<bpmn:messageFlow id="love_letter_flow" name="Love Letter Flow" sourceRef="ActivitySendLetter" targetRef="Event_0ym6ptw" />
<bpmn:messageFlow id="love_letter_flow" name="Love Letter Flow" sourceRef="ActivitySendLetter"
targetRef="Event_0ym6ptw" />
<bpmn:messageFlow id="Flow_10ya1ap" sourceRef="Activity_0ra5uc1" targetRef="Participant_1tlkvw1" />
<bpmn:messageFlow id="Flow_0vonyt2" sourceRef="Participant_1tlkvw1" targetRef="Event_06829ki" />
<bpmn:messageFlow id="Flow_0wwbhd6" sourceRef="Event_0e1t8xh" targetRef="EventReceiveLetter" />
@ -14,15 +22,7 @@
</bpmn:correlationKey>
<bpmn:correlationKey name="singer" />
</bpmn:collaboration>
<bpmn:message id="love_letter" name="Love Letter">
<bpmn:extensionElements>
<spiffworkflow:messagePayload>
{
'to': { 'name': my_lover_variable }
}
</spiffworkflow:messagePayload>
</bpmn:extensionElements>
</bpmn:message>
<bpmn:message id="love_letter" name="Love Letter" />
<bpmn:message id="love_letter_response" name="Love Letter Response" />
<bpmn:correlationProperty id="lover_instrument" name="Lover&#39;s Instrument">
<bpmn:correlationPropertyRetrievalExpression messageRef="love_letter">
@ -55,6 +55,13 @@
<bpmn:sendTask id="ActivitySendLetter" name="Send Letter" messageRef="love_letter">
<bpmn:incoming>Flow_1bl6jeh</bpmn:incoming>
<bpmn:outgoing>Flow_0tp8uut</bpmn:outgoing>
<bpmn:extensionElements>
<spiffworkflow:messagePayload>
{
'to': { 'name': my_lover_variable }
}
</spiffworkflow:messagePayload>
</bpmn:extensionElements>
</bpmn:sendTask>
<bpmn:intermediateCatchEvent id="EventReceiveLetter" name="receive Letter">
<bpmn:incoming>Flow_0tp8uut</bpmn:incoming>
@ -75,7 +82,8 @@
</bpmn:intermediateCatchEvent>
<bpmn:sequenceFlow id="Flow_1f0m6hd" sourceRef="Activity_0ra5uc1" targetRef="Event_06829ki" />
<bpmn:sequenceFlow id="Flow_1ai45pq" sourceRef="EventReceiveLetter" targetRef="Activity_0ra5uc1" />
<bpmn:sequenceFlow id="Flow_0tp8uut" sourceRef="ActivitySendLetter" targetRef="EventReceiveLetter" />
<bpmn:sequenceFlow id="Flow_0tp8uut" sourceRef="ActivitySendLetter"
targetRef="EventReceiveLetter" />
<bpmn:sequenceFlow id="Flow_1bl6jeh" sourceRef="StartEvent_1" targetRef="ActivitySendLetter" />
<bpmn:sequenceFlow id="Flow_0rygg2d" sourceRef="Event_06829ki" targetRef="Event_01h5zqa" />
</bpmn:process>
@ -165,7 +173,8 @@
<bpmndi:BPMNShape id="Event_12u2p44_di" bpmnElement="Event_0e1t8xh">
<dc:Bounds x="412" y="512" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Participant_1tlkvw1_di" bpmnElement="Participant_1tlkvw1" isHorizontal="true">
<bpmndi:BPMNShape id="Participant_1tlkvw1_di" bpmnElement="Participant_1tlkvw1"
isHorizontal="true">
<dc:Bounds x="129" y="80" width="721" height="60" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
@ -192,4 +201,4 @@
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
</bpmn:definitions>