This is an automated email from the ASF dual-hosted git repository.
thiagoelg pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git
The following commit(s) were added to refs/heads/main by this push:
new 0959b002371 kie-issues#2220: BPMN Editor: Interface tags configured in
a service task are missing from the generated XML (#3445)
0959b002371 is described below
commit 0959b002371010fccfe4e8eb74fb047803787a67
Author: Kbowers <[email protected]>
AuthorDate: Tue Mar 10 20:12:48 2026 +0100
kie-issues#2220: BPMN Editor: Interface tags configured in a service task
are missing from the generated XML (#3445)
---
.../nodes/morphing/useTaskNodeMorphingActions.tsx | 8 ++
.../{addOrGetMessages.ts => addOrGetInterfaces.ts} | 28 +++---
.../bpmn-editor/src/mutations/addOrGetMessages.ts | 2 +-
.../src/mutations/addOrGetOperations.ts | 105 +++++++++++++++++++++
.../src/mutations/deleteInterfaceAndOperation.ts | 78 +++++++++++++++
packages/bpmn-editor/src/mutations/deleteNode.ts | 9 ++
.../messageSelector/MessageSelector.tsx | 17 ++--
.../propertiesManager/PropertiesManager.tsx | 10 +-
.../singleNodeProperties/ServiceTaskProperties.tsx | 15 +++
.../src/store/getServiceTaskMessageIds.ts | 41 ++++++++
10 files changed, 287 insertions(+), 26 deletions(-)
diff --git
a/packages/bpmn-editor/src/diagram/nodes/morphing/useTaskNodeMorphingActions.tsx
b/packages/bpmn-editor/src/diagram/nodes/morphing/useTaskNodeMorphingActions.tsx
index d02d71714db..403745dd760 100644
---
a/packages/bpmn-editor/src/diagram/nodes/morphing/useTaskNodeMorphingActions.tsx
+++
b/packages/bpmn-editor/src/diagram/nodes/morphing/useTaskNodeMorphingActions.tsx
@@ -34,6 +34,7 @@ import { useCustomTasks } from
"../../../customTasks/BpmnEditorCustomTasksContex
import { CustomTask } from "../../../BpmnEditor";
import { WritableDraft } from "immer";
import { State } from "../../../store/Store";
+import { deleteInterfaceAndOperation } from
"../../../mutations/deleteInterfaceAndOperation";
export function useTaskNodeMorphingActions(task: Task) {
const bpmnEditorStoreApi = useBpmnEditorStoreApi();
@@ -96,6 +97,13 @@ export function useTaskNodeMorphingActions(task: Task) {
return false; // Will stop visiting.
}
});
+
+ if (task.__$$element === "serviceTask" && newTaskElement !==
"serviceTask" && task["@_operationRef"]) {
+ deleteInterfaceAndOperation({
+ definitions: s.bpmn.model.definitions,
+ operationRef: task["@_operationRef"],
+ });
+ }
},
[task]
);
diff --git a/packages/bpmn-editor/src/mutations/addOrGetMessages.ts
b/packages/bpmn-editor/src/mutations/addOrGetInterfaces.ts
similarity index 66%
copy from packages/bpmn-editor/src/mutations/addOrGetMessages.ts
copy to packages/bpmn-editor/src/mutations/addOrGetInterfaces.ts
index 28fffe4113e..5eea486c63f 100644
--- a/packages/bpmn-editor/src/mutations/addOrGetMessages.ts
+++ b/packages/bpmn-editor/src/mutations/addOrGetInterfaces.ts
@@ -23,30 +23,30 @@ import { Unpacked } from
"@kie-tools/xyflow-react-kie-diagram/dist/tsExt/tsExt";
import { Normalized } from "../normalization/normalize";
import { generateUuid } from
"@kie-tools/xyflow-react-kie-diagram/dist/uuid/uuid";
-export function addOrGetMessages({
+export function addOrGetInterfaces({
definitions,
- messageName,
+ interfaceName,
}: {
definitions: Normalized<BPMN20__tDefinitions>;
- messageName: string;
+ interfaceName: string;
}): {
- messageRef: string;
+ interface:
ElementFilter<Unpacked<Normalized<BPMN20__tDefinitions["rootElement"]>>,
"interface">;
} {
definitions.rootElement ??= [];
- const messages = definitions.rootElement.filter((s) => s.__$$element ===
"message");
- const existingMessage = messages.find((s) => s["@_id"] === messageName);
+ const interfaces = definitions.rootElement.filter((s) => s.__$$element ===
"interface");
- if (existingMessage) {
- return { messageRef: existingMessage["@_id"] };
+ const existingInterface = interfaces.find((s) => s["@_name"] ===
interfaceName);
+ if (existingInterface) {
+ return { interface: existingInterface };
}
- const newMessage:
ElementFilter<Unpacked<Normalized<BPMN20__tDefinitions["rootElement"]>>,
"message"> = {
- __$$element: "message",
+ const newInterface:
ElementFilter<Unpacked<Normalized<BPMN20__tDefinitions["rootElement"]>>,
"interface"> = {
+ __$$element: "interface",
"@_id": generateUuid(),
- "@_itemRef": `${messageName}Type`,
- "@_name": messageName,
+ "@_name": interfaceName,
+ operation: [],
};
- definitions.rootElement.push(newMessage);
- return { messageRef: newMessage["@_id"] };
+ definitions.rootElement.push(newInterface);
+ return { interface: newInterface };
}
diff --git a/packages/bpmn-editor/src/mutations/addOrGetMessages.ts
b/packages/bpmn-editor/src/mutations/addOrGetMessages.ts
index 28fffe4113e..0d9fd990abf 100644
--- a/packages/bpmn-editor/src/mutations/addOrGetMessages.ts
+++ b/packages/bpmn-editor/src/mutations/addOrGetMessages.ts
@@ -43,7 +43,7 @@ export function addOrGetMessages({
const newMessage:
ElementFilter<Unpacked<Normalized<BPMN20__tDefinitions["rootElement"]>>,
"message"> = {
__$$element: "message",
"@_id": generateUuid(),
- "@_itemRef": `${messageName}Type`,
+ "@_itemRef": `${messageName}Type`, // broken reference to a placeholder
type that has no meaning to the jBPM Workflow Engine
"@_name": messageName,
};
diff --git a/packages/bpmn-editor/src/mutations/addOrGetOperations.ts
b/packages/bpmn-editor/src/mutations/addOrGetOperations.ts
new file mode 100644
index 00000000000..53e860baef0
--- /dev/null
+++ b/packages/bpmn-editor/src/mutations/addOrGetOperations.ts
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { BPMN20__tDefinitions } from
"@kie-tools/bpmn-marshaller/dist/schemas/bpmn-2_0/ts-gen/types";
+import { ElementFilter } from "@kie-tools/xml-parser-ts/dist/elementFilter";
+import { Unpacked } from
"@kie-tools/xyflow-react-kie-diagram/dist/tsExt/tsExt";
+import { Normalized } from "../normalization/normalize";
+import { addOrGetInterfaces } from "./addOrGetInterfaces";
+import { addOrGetMessages } from "./addOrGetMessages";
+import { generateUuid } from
"@kie-tools/xyflow-react-kie-diagram/dist/uuid/uuid";
+
+export function addOrGetOperations({
+ definitions,
+ interfaceName,
+ operationRef,
+ operationName,
+}: {
+ definitions: Normalized<BPMN20__tDefinitions>;
+ interfaceName: string;
+ operationRef?: string;
+ operationName: string;
+}): {
+ interface:
ElementFilter<Unpacked<Normalized<BPMN20__tDefinitions["rootElement"]>>,
"interface">;
+ operation:
ElementFilter<Unpacked<Normalized<BPMN20__tDefinitions["rootElement"]>>,
"interface">["operation"][number];
+} {
+ const { interface: serviceTaskInterface } = addOrGetInterfaces({
+ definitions,
+ interfaceName,
+ });
+
+ serviceTaskInterface.operation ??= [];
+
+ let operation:
+ | ElementFilter<Unpacked<Normalized<BPMN20__tDefinitions["rootElement"]>>,
"interface">["operation"][number]
+ | undefined;
+
+ if (operationRef) {
+ const interfaces = definitions.rootElement?.filter((s) => s.__$$element
=== "interface") ?? [];
+
+ for (const iface of interfaces) {
+ if (iface.__$$element === "interface") {
+ iface.operation ??= [];
+ const operationIndex = iface.operation.findIndex((op) => op["@_id"]
=== operationRef);
+
+ if (operationIndex >= 0) {
+ operation = iface.operation[operationIndex];
+
+ if (iface["@_id"] !== serviceTaskInterface["@_id"]) {
+ iface.operation.splice(operationIndex, 1);
+ serviceTaskInterface.operation.push(operation);
+
+ if (iface.operation.length === 0) {
+ const interfaceIndex = definitions.rootElement?.findIndex(
+ (s) => s.__$$element === "interface" && s["@_id"] ===
iface["@_id"]
+ );
+ if (interfaceIndex !== undefined && interfaceIndex >= 0) {
+ definitions.rootElement?.splice(interfaceIndex, 1);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (!operation) {
+ const { messageRef: inMessageId } = addOrGetMessages({
+ definitions,
+ messageName: "",
+ });
+ const { messageRef: outMessageId } = addOrGetMessages({
+ definitions,
+ messageName: "",
+ });
+
+ operation = {
+ "@_id": generateUuid(),
+ "@_name": operationName,
+ inMessageRef: { __$$text: inMessageId },
+ outMessageRef: { __$$text: outMessageId },
+ };
+ serviceTaskInterface.operation.push(operation);
+ } else if (operation["@_name"] !== operationName) {
+ operation["@_name"] = operationName;
+ }
+
+ return { interface: serviceTaskInterface, operation: operation };
+}
diff --git a/packages/bpmn-editor/src/mutations/deleteInterfaceAndOperation.ts
b/packages/bpmn-editor/src/mutations/deleteInterfaceAndOperation.ts
new file mode 100644
index 00000000000..fed6e23fff4
--- /dev/null
+++ b/packages/bpmn-editor/src/mutations/deleteInterfaceAndOperation.ts
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { BPMN20__tDefinitions } from
"@kie-tools/bpmn-marshaller/dist/schemas/bpmn-2_0/ts-gen/types";
+import { Normalized } from "../normalization/normalize";
+
+export function deleteInterfaceAndOperation({
+ definitions,
+ operationRef,
+}: {
+ definitions: Normalized<BPMN20__tDefinitions>;
+ operationRef: string;
+}) {
+ if (!definitions.rootElement) {
+ return;
+ }
+
+ const serviceTaskInterface = definitions.rootElement.find(
+ (s) => s.__$$element === "interface" && s.operation?.some((op) =>
op["@_id"] === operationRef)
+ );
+
+ if (!serviceTaskInterface || serviceTaskInterface.__$$element !==
"interface") {
+ return;
+ }
+
+ serviceTaskInterface.operation ??= [];
+
+ const operationIndex = serviceTaskInterface.operation.findIndex((op) =>
op["@_id"] === operationRef);
+ if (operationIndex < 0) {
+ return;
+ }
+
+ const operation = serviceTaskInterface.operation[operationIndex];
+
+ const inMessageId = operation.inMessageRef.__$$text;
+ const outMessageId = operation.outMessageRef?.__$$text;
+
+ const existingInMessageIndex = definitions.rootElement.findIndex(
+ (s) => s.__$$element === "message" && s["@_id"] === inMessageId
+ );
+ const existingOutMessageIndex = outMessageId
+ ? definitions.rootElement.findIndex((s) => s.__$$element === "message" &&
s["@_id"] === outMessageId)
+ : -1;
+
+ [existingInMessageIndex, existingOutMessageIndex]
+ .filter((index): index is number => index >= 0)
+ .sort((a, b) => b - a)
+ .forEach((index) => {
+ definitions.rootElement?.splice(index, 1);
+ });
+
+ serviceTaskInterface.operation.splice(operationIndex, 1);
+
+ if (serviceTaskInterface.operation.length === 0) {
+ const interfaceIndex = definitions.rootElement.findIndex(
+ (s) => s.__$$element === "interface" && s["@_id"] ===
serviceTaskInterface["@_id"]
+ );
+ if (interfaceIndex >= 0) {
+ definitions.rootElement.splice(interfaceIndex, 1);
+ }
+ }
+}
diff --git a/packages/bpmn-editor/src/mutations/deleteNode.ts
b/packages/bpmn-editor/src/mutations/deleteNode.ts
index dfe60478596..16c20fc38c3 100644
--- a/packages/bpmn-editor/src/mutations/deleteNode.ts
+++ b/packages/bpmn-editor/src/mutations/deleteNode.ts
@@ -29,6 +29,7 @@ import { deleteEdge } from "./deleteEdge";
import { FoundElement, visitFlowElementsAndArtifacts } from
"./_elementVisitor";
import { Unpacked } from
"@kie-tools/xyflow-react-kie-diagram/dist/tsExt/tsExt";
import { ElementExclusion } from "@kie-tools/xml-parser-ts/dist/elementFilter";
+import { deleteInterfaceAndOperation } from "./deleteInterfaceAndOperation";
export function deleteNode({
definitions,
@@ -84,6 +85,14 @@ export function deleteNode({
deletedBpmnElement = foundElement.array.splice(foundElement.index, 1)?.[0]
as BpmnNodeElement | undefined;
}
+ // if Service Task
+ if (deletedBpmnElement?.__$$element === "serviceTask" &&
deletedBpmnElement["@_operationRef"]) {
+ deleteInterfaceAndOperation({
+ definitions,
+ operationRef: deletedBpmnElement["@_operationRef"],
+ });
+ }
+
// if lane
else if (
(laneIndex = (process.laneSet?.[0].lane ?? []).findIndex((d) => d["@_id"]
=== __readonly_bpmnElementId)) >= 0
diff --git
a/packages/bpmn-editor/src/propertiesPanel/messageSelector/MessageSelector.tsx
b/packages/bpmn-editor/src/propertiesPanel/messageSelector/MessageSelector.tsx
index ff21be74749..753301cd180 100644
---
a/packages/bpmn-editor/src/propertiesPanel/messageSelector/MessageSelector.tsx
+++
b/packages/bpmn-editor/src/propertiesPanel/messageSelector/MessageSelector.tsx
@@ -31,6 +31,7 @@ import { useCallback, useMemo } from "react";
import { addOrGetMessages } from "../../mutations/addOrGetMessages";
import "./MessageSelector.css";
import { useBpmnEditorI18n } from "../../i18n";
+import { getServiceTaskMessageIds } from
"../../store/getServiceTaskMessageIds";
export type EventWithMessage =
| undefined
@@ -57,14 +58,14 @@ export function MessageSelector({
const bpmnEditorStoreApi = useBpmnEditorStoreApi();
- const messagesById = useBpmnEditorStore(
- (s) =>
- new Map(
- s.bpmn.model.definitions.rootElement
- ?.filter((e) => e.__$$element === "message")
- .map((m) => [m["@_id"], m] as [string, BPMN20__tMessage])
- )
- );
+ const messagesById = useBpmnEditorStore((s) => {
+ const allMessages = s.bpmn.model.definitions.rootElement?.filter((e) =>
e.__$$element === "message") ?? [];
+ const serviceTaskMessageIds =
getServiceTaskMessageIds(s.bpmn.model.definitions);
+
+ const filteredMessages = allMessages.filter((msg) =>
!serviceTaskMessageIds.has(msg["@_id"]));
+
+ return new Map(filteredMessages.map((m) => [m["@_id"], m] as [string,
BPMN20__tMessage]));
+ });
const disableIdsSet = useMemo(() => new Set<string |
undefined>(disableValues), [disableValues]);
diff --git
a/packages/bpmn-editor/src/propertiesPanel/propertiesManager/PropertiesManager.tsx
b/packages/bpmn-editor/src/propertiesPanel/propertiesManager/PropertiesManager.tsx
index f0fd4cd0c33..4110b935cc0 100644
---
a/packages/bpmn-editor/src/propertiesPanel/propertiesManager/PropertiesManager.tsx
+++
b/packages/bpmn-editor/src/propertiesPanel/propertiesManager/PropertiesManager.tsx
@@ -62,6 +62,7 @@ import { renameEscalation } from
"../../mutations/renameEscalation";
import { deleteError } from "../../mutations/deleteError";
import { renameError } from "../../mutations/renameError";
import { useBpmnEditorI18n } from "../../i18n";
+import { getServiceTaskMessageIds } from
"../../store/getServiceTaskMessageIds";
export type WithVariables = Normalized<
| ElementFilter<Unpacked<NonNullable<BPMN20__tDefinitions["rootElement"]>>,
"process">
@@ -87,9 +88,12 @@ export function PropertiesManager({ p }: { p: undefined |
WithVariables }) {
)?.filter(
(s) => Object.values(DEFAULT_DATA_TYPES).findIndex((defaultDataType) =>
defaultDataType === s["@_structureRef"]) < 0
);
- const messages = useBpmnEditorStore((s) =>
- s.bpmn.model.definitions.rootElement?.filter((s) => s.__$$element ===
"message")
- );
+ const messages = useBpmnEditorStore((s) => {
+ const allMessages = s.bpmn.model.definitions.rootElement?.filter((s) =>
s.__$$element === "message") ?? [];
+ const serviceTaskMessageIds =
getServiceTaskMessageIds(s.bpmn.model.definitions);
+
+ return allMessages.filter((msg) =>
!serviceTaskMessageIds.has(msg["@_id"]));
+ });
const signals = useBpmnEditorStore((s) =>
s.bpmn.model.definitions.rootElement?.filter((s) => s.__$$element ===
"signal")
);
diff --git
a/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/ServiceTaskProperties.tsx
b/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/ServiceTaskProperties.tsx
index 3f3c14577e0..6c64760aa65 100644
---
a/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/ServiceTaskProperties.tsx
+++
b/packages/bpmn-editor/src/propertiesPanel/singleNodeProperties/ServiceTaskProperties.tsx
@@ -42,6 +42,7 @@ import { addOrGetProcessAndDiagramElements } from
"../../mutations/addOrGetProce
import { useBpmnEditorStore, useBpmnEditorStoreApi } from
"../../store/StoreContext";
import { TextInput } from
"@patternfly/react-core/dist/js/components/TextInput";
import { useBpmnEditorI18n } from "../../i18n";
+import { addOrGetOperations } from "../../mutations/addOrGetOperations";
export function ServiceTaskProperties({
serviceTask,
@@ -128,7 +129,14 @@ export function ServiceTaskProperties({
});
visitFlowElementsAndArtifacts(process, ({ element: e }) => {
if (e["@_id"] === serviceTask["@_id"] && e.__$$element ===
serviceTask.__$$element) {
+ const { operation } = addOrGetOperations({
+ definitions: s.bpmn.model.definitions,
+ interfaceName: newInterface,
+ operationRef: e["@_operationRef"],
+ operationName: e["@_drools:serviceoperation"] || "",
+ });
e["@_drools:serviceinterface"] = newInterface;
+ e["@_operationRef"] = operation["@_id"];
}
});
})
@@ -150,7 +158,14 @@ export function ServiceTaskProperties({
});
visitFlowElementsAndArtifacts(process, ({ element: e }) => {
if (e["@_id"] === serviceTask["@_id"] && e.__$$element ===
serviceTask.__$$element) {
+ const { operation } = addOrGetOperations({
+ definitions: s.bpmn.model.definitions,
+ interfaceName: e["@_drools:serviceinterface"] || "",
+ operationRef: e["@_operationRef"],
+ operationName: newOperation,
+ });
e["@_drools:serviceoperation"] = newOperation;
+ e["@_operationRef"] = operation["@_id"];
}
});
})
diff --git a/packages/bpmn-editor/src/store/getServiceTaskMessageIds.ts
b/packages/bpmn-editor/src/store/getServiceTaskMessageIds.ts
new file mode 100644
index 00000000000..4de4da99d9e
--- /dev/null
+++ b/packages/bpmn-editor/src/store/getServiceTaskMessageIds.ts
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { BPMN20__tDefinitions } from
"@kie-tools/bpmn-marshaller/dist/schemas/bpmn-2_0/ts-gen/types";
+import { Normalized } from "../normalization/normalize";
+
+export function getServiceTaskMessageIds(definitions:
Normalized<BPMN20__tDefinitions>): Set<string> {
+ const interfaces = definitions.rootElement?.filter((e) => e.__$$element ===
"interface") ?? [];
+ const operationMessageIds = new Set<string>();
+
+ interfaces.forEach((iface) => {
+ if (iface.__$$element === "interface") {
+ iface.operation?.forEach((op) => {
+ if (op.inMessageRef?.__$$text) {
+ operationMessageIds.add(op.inMessageRef.__$$text);
+ }
+ if (op.outMessageRef?.__$$text) {
+ operationMessageIds.add(op.outMessageRef.__$$text);
+ }
+ });
+ }
+ });
+
+ return operationMessageIds;
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]