This is an automated email from the ASF dual-hosted git repository.

tiagobento 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 95867ae60f6 kie-issues#888: Decision Services & multiple DRDs: Adding 
a Decision Service to a DRD (#2181)
95867ae60f6 is described below

commit 95867ae60f6d72bb4a9052e7b8fda7081fd2039a
Author: Tiago Bento <[email protected]>
AuthorDate: Tue Feb 27 17:25:24 2024 -0500

    kie-issues#888: Decision Services & multiple DRDs: Adding a Decision 
Service to a DRD (#2181)
---
 .../dmn-editor/src/autolayout/AutolayoutButton.tsx |   4 +-
 packages/dmn-editor/src/dataTypes/TypeRefLabel.tsx |   2 +-
 packages/dmn-editor/src/diagram/Diagram.tsx        | 250 +++++++++++++--------
 packages/dmn-editor/src/diagram/DrgNodesPanel.tsx  |   2 +-
 packages/dmn-editor/src/diagram/nodes/Nodes.tsx    |  27 +--
 .../src/externalNodes/ExternalNodesPanel.tsx       |   2 +-
 .../mutations/addExistingDecisionServiceToDrd.ts   | 248 ++++++++++++++++++++
 packages/dmn-editor/src/mutations/addOrGetDrd.ts   |   2 +-
 packages/dmn-editor/src/mutations/deleteImport.ts  |   2 +-
 packages/dmn-editor/src/mutations/deleteNode.ts    |  52 +++--
 .../dmn-editor/src/propertiesPanel/FontOptions.tsx |   4 +-
 .../src/propertiesPanel/ShapeOptions.tsx           |   8 +-
 packages/dmn-editor/src/store/Store.ts             |  14 +-
 .../computeDecisionServiceHrefsByDecisionHrefs.ts  |  69 ++++++
 .../src/store/computed/computeDiagramData.ts       |  65 +++---
 .../src/store/computed/computeIndexes.ts           |   8 +-
 packages/dmn-editor/src/store/computed/initial.ts  |   2 +-
 ...lNamespaceDeclarations.ts => xmlHrefToQName.ts} |  33 ++-
 .../dmn-editor/src/xml/xmlNamespaceDeclarations.ts |   6 +-
 19 files changed, 602 insertions(+), 198 deletions(-)

diff --git a/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx 
b/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
index 17b4a44e0ea..0bf61e814c2 100644
--- a/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
+++ b/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
@@ -407,7 +407,7 @@ export function AutolayoutButton() {
           resizeNode({
             definitions: s.dmn.model.definitions,
             drdIndex: s.diagram.drdIndex,
-            dmnShapesByHref: s.computed(s).indexes().dmnShapesByHref,
+            dmnShapesByHref: s.computed(s).indexedDrd().dmnShapesByHref,
             snapGrid,
             change: {
               index: node.data.index,
@@ -455,7 +455,7 @@ export function AutolayoutButton() {
         updateDecisionServiceDividerLine({
           definitions: s.dmn.model.definitions,
           drdIndex: s.diagram.drdIndex,
-          dmnShapesByHref: s.computed(s).indexes().dmnShapesByHref,
+          dmnShapesByHref: s.computed(s).indexedDrd().dmnShapesByHref,
           drgElementIndex: parentNode.data.index,
           shapeIndex: parentNode.data.shape.index,
           snapGrid,
diff --git a/packages/dmn-editor/src/dataTypes/TypeRefLabel.tsx 
b/packages/dmn-editor/src/dataTypes/TypeRefLabel.tsx
index 31f048e5d38..124788860bb 100644
--- a/packages/dmn-editor/src/dataTypes/TypeRefLabel.tsx
+++ b/packages/dmn-editor/src/dataTypes/TypeRefLabel.tsx
@@ -52,7 +52,7 @@ export function TypeRefLabel({
     const parsedFeelQName = parseFeelQName(typeRef);
 
     const xmlNamespaceName = getXmlNamespaceDeclarationName({
-      model: thisDmn.model.definitions,
+      rootElement: thisDmn.model.definitions,
       namespace: relativeToNamespace ?? "",
     });
 
diff --git a/packages/dmn-editor/src/diagram/Diagram.tsx 
b/packages/dmn-editor/src/diagram/Diagram.tsx
index dd84d70cc69..0703dad042c 100644
--- a/packages/dmn-editor/src/diagram/Diagram.tsx
+++ b/packages/dmn-editor/src/diagram/Diagram.tsx
@@ -120,6 +120,11 @@ import {
   UnknownNode,
 } from "./nodes/Nodes";
 import { useExternalModels } from 
"../includedModels/DmnEditorDependenciesContext";
+import { xmlHrefToQName } from "../xml/xmlHrefToQName";
+import {
+  addExistingDecisionServiceToDrd,
+  getDecisionServicePropertiesRelativeToThisDmn,
+} from "../mutations/addExistingDecisionServiceToDrd";
 
 const isFirefox = typeof (window as any).InstallTrigger !== "undefined"; // 
See 
https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browsers
 
@@ -338,15 +343,20 @@ export const Diagram = React.forwardRef<DiagramRef, { 
container: React.RefObject
           ) as ExternalNode;
 
           // --------- This is where we draw the line between the diagram and 
the model.
+
           dmnEditorStoreApi.setState((state) => {
-            const externalDrgElement = (
-              state
-                .computed(state)
-                .getExternalModelTypesByNamespace(externalModelsByNamespace)
-                
.dmns.get(externalNode.externalDrgElementNamespace)?.model.definitions.drgElement
 ?? []
-            ).find((s) => s["@_id"] === externalNode.externalDrgElementId);
-            if (!externalDrgElement) {
-              throw new Error(`Can't find DRG element with id 
'${externalNode.externalDrgElementId}'.`);
+            const externalDmnsIndex = state
+              .computed(state)
+              
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns;
+
+            const externalNodeDmn = 
externalDmnsIndex.get(externalNode.externalDrgElementNamespace);
+            const externalDrgElement = 
(externalNodeDmn?.model.definitions.drgElement ?? []).find(
+              (e) => e["@_id"] === externalNode.externalDrgElementId
+            );
+            if (!externalNodeDmn || !externalDrgElement) {
+              throw new Error(
+                `Can't find DRG element with id 
'${externalNode.externalDrgElementId}' on/or model with namespace 
'${externalNode.externalDrgElementNamespace}'.`
+              );
             }
 
             const externalNodeType = 
getNodeTypeFromDmnObject(externalDrgElement)!;
@@ -357,39 +367,61 @@ export const Diagram = React.forwardRef<DiagramRef, { 
container: React.RefObject
             });
 
             const namespaceName = getXmlNamespaceDeclarationName({
-              model: state.dmn.model.definitions,
+              rootElement: state.dmn.model.definitions,
               namespace: externalNode.externalDrgElementNamespace,
             });
 
-            if (!namespaceName) {
-              throw new Error(`Can't find namespace name for 
'${externalNode.externalDrgElementNamespace}'.`);
-            }
+            const externalNodeHref = buildXmlHref({
+              namespace: externalNode.externalDrgElementNamespace,
+              id: externalNode.externalDrgElementId,
+            });
 
-            addShape({
-              definitions: state.dmn.model.definitions,
-              drdIndex: state.diagram.drdIndex,
-              nodeType: externalNodeType,
-              shape: {
-                "@_dmnElementRef": buildXmlQName({
-                  type: "xml-qname",
-                  prefix: namespaceName,
-                  localPart: externalDrgElement["@_id"]!,
-                }),
-                "@_isCollapsed": true,
-                "dc:Bounds": {
-                  "@_x": dropPoint.x,
-                  "@_y": dropPoint.y,
-                  "@_width": defaultExternalNodeDimensions["@_width"],
-                  "@_height": defaultExternalNodeDimensions["@_height"],
+            if (externalDrgElement.__$$element === "decisionService") {
+              addExistingDecisionServiceToDrd({
+                decisionService: externalDrgElement,
+                decisionServiceNamespace: 
externalNodeDmn.model.definitions["@_namespace"],
+                drdIndex: state.diagram.drdIndex,
+                dropPoint,
+                externalDmnsIndex,
+                thisDmnsDefinitions: state.dmn.model.definitions,
+                thisDmnsIndexedDrd: state.computed(state).indexedDrd(),
+                thisDmnsNamespace: state.dmn.model.definitions["@_namespace"],
+              });
+            } else if (externalDrgElement.__$$element === "decision") {
+              // TODO: Tiago --> Add logic for Decision contained by DS.
+              const externalNodeType = 
getNodeTypeFromDmnObject(externalDrgElement)!;
+              addShape({
+                definitions: state.dmn.model.definitions,
+                drdIndex: state.diagram.drdIndex,
+                nodeType: externalNodeType,
+                shape: {
+                  "@_dmnElementRef": xmlHrefToQName(externalNodeHref, 
state.dmn.model.definitions),
+                  "dc:Bounds": {
+                    "@_x": dropPoint.x,
+                    "@_y": dropPoint.y,
+                    "@_width": defaultExternalNodeDimensions["@_width"],
+                    "@_height": defaultExternalNodeDimensions["@_height"],
+                  },
                 },
-              },
-            });
-            state.diagram._selectedNodes = [
-              buildXmlHref({
-                namespace: externalNode.externalDrgElementNamespace,
-                id: externalNode.externalDrgElementId,
-              }),
-            ];
+              });
+            } else {
+              const externalNodeType = 
getNodeTypeFromDmnObject(externalDrgElement)!;
+              addShape({
+                definitions: state.dmn.model.definitions,
+                drdIndex: state.diagram.drdIndex,
+                nodeType: externalNodeType,
+                shape: {
+                  "@_dmnElementRef": xmlHrefToQName(externalNodeHref, 
state.dmn.model.definitions),
+                  "dc:Bounds": {
+                    "@_x": dropPoint.x,
+                    "@_y": dropPoint.y,
+                    "@_width": defaultExternalNodeDimensions["@_width"],
+                    "@_height": defaultExternalNodeDimensions["@_height"],
+                  },
+                },
+              });
+            }
+            state.diagram._selectedNodes = [externalNodeHref];
           });
 
           console.debug(`DMN DIAGRAM: Adding external node`, 
JSON.stringify(externalNode));
@@ -398,31 +430,65 @@ export const Diagram = React.forwardRef<DiagramRef, { 
container: React.RefObject
             DMN15__tDefinitions["drgElement"]
           >;
 
-          const nodeType = getNodeTypeFromDmnObject(drgElement);
-          if (nodeType === undefined) {
-            throw new Error("DMN DIAGRAM: It wasn't possible to determine the 
node type");
-          }
-
           dmnEditorStoreApi.setState((state) => {
+            const nodeType = getNodeTypeFromDmnObject(drgElement);
+            if (nodeType === undefined) {
+              throw new Error("DMN DIAGRAM: It wasn't possible to determine 
the node type");
+            }
+
             const defaultNodeDimensions = DEFAULT_NODE_SIZES[nodeType]({
               snapGrid: state.diagram.snapGrid,
               isAlternativeInputDataShape: 
state.computed(state).isAlternativeInputDataShape(),
             });
-            addShape({
-              definitions: state.dmn.model.definitions,
-              drdIndex: state.diagram.drdIndex,
-              nodeType,
-              shape: {
-                "@_dmnElementRef": buildXmlQName({ type: "xml-qname", 
localPart: drgElement["@_id"]! }),
-                "@_isCollapsed": false,
-                "dc:Bounds": {
-                  "@_x": dropPoint.x,
-                  "@_y": dropPoint.y,
-                  "@_width": defaultNodeDimensions["@_width"],
-                  "@_height": defaultNodeDimensions["@_height"],
+
+            if (drgElement.__$$element === "decisionService") {
+              addExistingDecisionServiceToDrd({
+                decisionService: drgElement,
+                decisionServiceNamespace: 
state.dmn.model.definitions["@_namespace"],
+                drdIndex: state.diagram.drdIndex,
+                dropPoint,
+                externalDmnsIndex: 
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
+                  .dmns,
+                thisDmnsDefinitions: state.dmn.model.definitions,
+                thisDmnsIndexedDrd: state.computed(state).indexedDrd(),
+                thisDmnsNamespace: state.dmn.model.definitions["@_namespace"],
+              });
+            } else if (drgElement.__$$element === "decision") {
+              // TODO: Tiago --> Add logic for Decision contained by DS.
+              const nodeType = getNodeTypeFromDmnObject(drgElement)!;
+              addShape({
+                definitions: state.dmn.model.definitions,
+                drdIndex: state.diagram.drdIndex,
+                nodeType,
+                shape: {
+                  "@_dmnElementRef": buildXmlQName({ type: "xml-qname", 
localPart: drgElement["@_id"]! }),
+                  "@_isCollapsed": false,
+                  "dc:Bounds": {
+                    "@_x": dropPoint.x,
+                    "@_y": dropPoint.y,
+                    "@_width": defaultNodeDimensions["@_width"],
+                    "@_height": defaultNodeDimensions["@_height"],
+                  },
                 },
-              },
-            });
+              });
+            } else {
+              const nodeType = getNodeTypeFromDmnObject(drgElement)!;
+              addShape({
+                definitions: state.dmn.model.definitions,
+                drdIndex: state.diagram.drdIndex,
+                nodeType,
+                shape: {
+                  "@_dmnElementRef": buildXmlQName({ type: "xml-qname", 
localPart: drgElement["@_id"]! }),
+                  "@_isCollapsed": false,
+                  "dc:Bounds": {
+                    "@_x": dropPoint.x,
+                    "@_y": dropPoint.y,
+                    "@_width": defaultNodeDimensions["@_width"],
+                    "@_height": defaultNodeDimensions["@_height"],
+                  },
+                },
+              });
+            }
           });
 
           console.debug(`DMN DIAGRAM: Adding DRG node`, 
JSON.stringify(drgElement));
@@ -485,7 +551,7 @@ export const Diagram = React.forwardRef<DiagramRef, { 
container: React.RefObject
             return;
           }
 
-          const sourceNodeBounds = 
state.computed(state).indexes().dmnShapesByHref.get(sourceNode.id)?.["dc:Bounds"];
+          const sourceNodeBounds = 
state.computed(state).indexedDrd().dmnShapesByHref.get(sourceNode.id)?.["dc:Bounds"];
           if (!sourceNodeBounds) {
             return;
           }
@@ -608,7 +674,7 @@ export const Diagram = React.forwardRef<DiagramRef, { 
container: React.RefObject
                     resizeNode({
                       definitions: state.dmn.model.definitions,
                       drdIndex: state.diagram.drdIndex,
-                      dmnShapesByHref: 
state.computed(state).indexes().dmnShapesByHref,
+                      dmnShapesByHref: 
state.computed(state).indexedDrd().dmnShapesByHref,
                       snapGrid: state.diagram.snapGrid,
                       change: {
                         isExternal: !!node.data.dmnObjectQName.prefix,
@@ -671,22 +737,20 @@ export const Diagram = React.forwardRef<DiagramRef, { 
container: React.RefObject
                     },
                   });
 
-                  // FIXME: This should be inside `repositionNode` I guess?
-
-                  // Update nested
-                  // External Decision Services will have encapsulated and 
output decisions, but they aren't depicted in the graph.
-                  if (node.type === NODE_TYPES.decisionService && 
!node.data.dmnObjectQName.prefix) {
+                  // Update contained Decisions of Decision Service if in 
expanded form
+                  if (node.type === NODE_TYPES.decisionService && 
!(node.data.shape["@_isCollapsed"] ?? false)) {
                     const decisionService = node.data.dmnObject as 
DMN15__tDecisionService;
-                    const nested = [
-                      ...(decisionService.outputDecision ?? []),
-                      ...(decisionService.encapsulatedDecision ?? []),
-                    ];
 
-                    for (let i = 0; i < nested.length; i++) {
-                      const nestedNode = state
-                        .computed(state)
-                        .getDiagramData(externalModelsByNamespace)
-                        .nodesById.get(nested[i]["@_href"])!;
+                    const { containedDecisionHrefsRelativeToThisDmn } = 
getDecisionServicePropertiesRelativeToThisDmn({
+                      thisDmnsNamespace: 
state.dmn.model.definitions["@_namespace"],
+                      decisionService,
+                      decisionServiceNamespace:
+                        node.data.dmnObjectNamespace ?? 
state.dmn.model.definitions["@_namespace"],
+                    });
+
+                    for (let i = 0; i < 
containedDecisionHrefsRelativeToThisDmn.length; i++) {
+                      const diagramData = 
state.computed(state).getDiagramData(externalModelsByNamespace);
+                      const nestedNode = 
diagramData.nodesById.get(containedDecisionHrefsRelativeToThisDmn[i])!;
                       const snappedNestedNodeShapeWithAppliedDelta = 
snapShapePosition(
                         state.diagram.snapGrid,
                         offsetShapePosition(nestedNode.data.shape, delta)
@@ -698,23 +762,14 @@ export const Diagram = React.forwardRef<DiagramRef, { 
container: React.RefObject
                         change: {
                           type: "absolute",
                           nodeType: nestedNode.type as NodeType,
-                          selectedEdges: state
-                            .computed(state)
-                            .getDiagramData(externalModelsByNamespace)
-                            .edges.map((e) => e.id),
+                          selectedEdges: diagramData.edges.map((e) => e.id),
                           shapeIndex: nestedNode.data.shape.index,
-                          sourceEdgeIndexes: state
-                            .computed(state)
-                            .getDiagramData(externalModelsByNamespace)
-                            .edges.flatMap((e) =>
-                              e.source === nestedNode.id && e.data?.dmnEdge ? 
[e.data.dmnEdge.index] : []
-                            ),
-                          targetEdgeIndexes: state
-                            .computed(state)
-                            .getDiagramData(externalModelsByNamespace)
-                            .edges.flatMap((e) =>
-                              e.target === nestedNode.id && e.data?.dmnEdge ? 
[e.data.dmnEdge.index] : []
-                            ),
+                          sourceEdgeIndexes: diagramData.edges.flatMap((e) =>
+                            e.source === nestedNode.id && e.data?.dmnEdge ? 
[e.data.dmnEdge.index] : []
+                          ),
+                          targetEdgeIndexes: diagramData.edges.flatMap((e) =>
+                            e.target === nestedNode.id && e.data?.dmnEdge ? 
[e.data.dmnEdge.index] : []
+                          ),
                           position: snappedNestedNodeShapeWithAppliedDelta,
                         },
                       });
@@ -729,11 +784,13 @@ export const Diagram = React.forwardRef<DiagramRef, { 
container: React.RefObject
                   drgEdges: 
state.computed(state).getDiagramData(externalModelsByNamespace).drgEdges,
                   definitions: state.dmn.model.definitions,
                   drdIndex: state.diagram.drdIndex,
-                  dmnObjectNamespace: node.data.dmnObjectNamespace,
+                  dmnObjectNamespace: node.data.dmnObjectNamespace ?? 
state.dmn.model.definitions["@_namespace"],
                   dmnObjectQName: node.data.dmnObjectQName,
                   dmnObjectId: node.data.dmnObject?.["@_id"],
                   nodeNature: nodeNatures[node.type as NodeType],
                   mode: NodeDeletionMode.FROM_DRG_AND_ALL_DRDS,
+                  externalDmnsIndex: 
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
+                    .dmns,
                 });
                 state.dispatch(state).diagram.setNodeStatus(node.id, {
                   selected: false,
@@ -1561,11 +1618,13 @@ export function KeyboardShortcuts(props: {}) {
                 drgEdges: 
state.computed(state).getDiagramData(externalModelsByNamespace).drgEdges,
                 definitions: state.dmn.model.definitions,
                 drdIndex: state.diagram.drdIndex,
-                dmnObjectNamespace: node.data.dmnObjectNamespace,
+                dmnObjectNamespace: node.data.dmnObjectNamespace ?? 
state.dmn.model.definitions["@_namespace"],
                 dmnObjectQName: node.data.dmnObjectQName,
                 dmnObjectId: node.data.dmnObject?.["@_id"],
                 nodeNature: nodeNatures[node.type as NodeType],
                 mode: NodeDeletionMode.FROM_DRG_AND_ALL_DRDS,
+                externalDmnsIndex: 
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
+                  .dmns,
               });
               state.dispatch(state).diagram.setNodeStatus(node.id, {
                 selected: false,
@@ -1770,16 +1829,20 @@ export function KeyboardShortcuts(props: {}) {
         if (
           (selectedNodeIds.has(edge.source) &&
             canRemoveNodeFromDrdOnly({
+              externalDmnsIndex: 
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
               definitions: state.dmn.model.definitions,
               drdIndex: state.diagram.drdIndex,
-              dmnObjectNamespace: 
nodesById.get(edge.source)!.data.dmnObjectNamespace,
+              dmnObjectNamespace:
+                nodesById.get(edge.source)!.data.dmnObjectNamespace ?? 
state.dmn.model.definitions["@_namespace"],
               dmnObjectId: 
nodesById.get(edge.source)!.data.dmnObject?.["@_id"],
             })) ||
           (selectedNodeIds.has(edge.target) &&
             canRemoveNodeFromDrdOnly({
+              externalDmnsIndex: 
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
               definitions: state.dmn.model.definitions,
               drdIndex: state.diagram.drdIndex,
-              dmnObjectNamespace: 
nodesById.get(edge.target)!.data.dmnObjectNamespace,
+              dmnObjectNamespace:
+                nodesById.get(edge.target)!.data.dmnObjectNamespace ?? 
state.dmn.model.definitions["@_namespace"],
               dmnObjectId: 
nodesById.get(edge.target)!.data.dmnObject?.["@_id"],
             }))
         ) {
@@ -1801,8 +1864,9 @@ export function KeyboardShortcuts(props: {}) {
         const { deletedDmnShapeOnCurrentDrd: deletedShape } = deleteNode({
           drgEdges: [], // Deleting from DRD only.
           definitions: state.dmn.model.definitions,
+          externalDmnsIndex: 
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
           drdIndex: state.diagram.drdIndex,
-          dmnObjectNamespace: node.data.dmnObjectNamespace,
+          dmnObjectNamespace: node.data.dmnObjectNamespace ?? 
state.dmn.model.definitions["@_namespace"],
           dmnObjectQName: node.data.dmnObjectQName,
           dmnObjectId: node.data.dmnObject?.["@_id"],
           nodeNature: nodeNatures[node.type as NodeType],
@@ -1818,7 +1882,7 @@ export function KeyboardShortcuts(props: {}) {
         }
       }
     });
-  }, [x, dmnEditorStoreApi, rf]);
+  }, [x, dmnEditorStoreApi, rf, externalModelsByNamespace]);
 
   return <></>;
 }
diff --git a/packages/dmn-editor/src/diagram/DrgNodesPanel.tsx 
b/packages/dmn-editor/src/diagram/DrgNodesPanel.tsx
index 8efbed5fac1..101bc8adbda 100644
--- a/packages/dmn-editor/src/diagram/DrgNodesPanel.tsx
+++ b/packages/dmn-editor/src/diagram/DrgNodesPanel.tsx
@@ -36,7 +36,7 @@ export const MIME_TYPE_FOR_DMN_EDITOR_DRG_NODE = 
"kie-dmn-editor--drg-node";
 
 export function DrgNodesPanel() {
   const thisDmn = useDmnEditorStore((s) => s.dmn);
-  const dmnShapesByHref = useDmnEditorStore((s) => 
s.computed(s).indexes().dmnShapesByHref);
+  const dmnShapesByHref = useDmnEditorStore((s) => 
s.computed(s).indexedDrd().dmnShapesByHref);
 
   const dmnEditorStoreApi = useDmnEditorStoreApi();
 
diff --git a/packages/dmn-editor/src/diagram/nodes/Nodes.tsx 
b/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
index f9b263b52c5..6b9a62392f5 100644
--- a/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
+++ b/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
@@ -134,7 +134,6 @@ export const InputDataNode = React.memo(
       nodeType: type as typeof NODE_TYPES.inputData,
       snapGrid,
       shape,
-      isExternal,
       isAlternativeInputDataShape,
     });
 
@@ -365,7 +364,6 @@ export const DecisionNode = React.memo(
       nodeType: type as typeof NODE_TYPES.decision,
       snapGrid,
       shape,
-      isExternal,
     });
     const setName = useCallback<OnEditableNodeLabelChange>(
       (newName: string) => {
@@ -518,7 +516,7 @@ export const BkmNode = React.memo(
 
     const { isTargeted, isValidConnectionTarget } = 
useConnectionTargetStatus(id, shouldActLikeHovered);
     const className = useNodeClassName(isValidConnectionTarget, id);
-    const nodeDimensions = useNodeDimensions({ nodeType: type as typeof 
NODE_TYPES.bkm, snapGrid, shape, isExternal });
+    const nodeDimensions = useNodeDimensions({ nodeType: type as typeof 
NODE_TYPES.bkm, snapGrid, shape });
     const setName = useCallback<OnEditableNodeLabelChange>(
       (newName: string) => {
         dmnEditorStoreApi.setState((state) => {
@@ -658,7 +656,6 @@ export const KnowledgeSourceNode = React.memo(
       nodeType: type as typeof NODE_TYPES.knowledgeSource,
       snapGrid,
       shape,
-      isExternal,
     });
     const setName = useCallback<OnEditableNodeLabelChange>(
       (newName: string) => {
@@ -777,7 +774,6 @@ export const TextAnnotationNode = React.memo(
       nodeType: type as typeof NODE_TYPES.textAnnotation,
       snapGrid,
       shape,
-      isExternal,
     });
     const setText = useCallback(
       (newText: string) => {
@@ -898,7 +894,6 @@ export const DecisionServiceNode = React.memo(
       nodeType: type as typeof NODE_TYPES.decisionService,
       snapGrid,
       shape,
-      isExternal,
     });
     const setName = useCallback<OnEditableNodeLabelChange>(
       (newName: string) => {
@@ -943,8 +938,7 @@ export const DecisionServiceNode = React.memo(
 
     const dividerLineRef = useRef<SVGPathElement>(null);
 
-    // External Decision Service nodes are always collapsed.
-    const isCollapsed = isExternal || shape["@_isCollapsed"];
+    const isCollapsed = shape["@_isCollapsed"] ?? false;
 
     const onCreateDataType = useDataTypeCreationCallbackForNodes(index, 
decisionService["@_name"]);
 
@@ -965,7 +959,7 @@ export const DecisionServiceNode = React.memo(
             updateDecisionServiceDividerLine({
               definitions: state.dmn.model.definitions,
               drdIndex: state.diagram.drdIndex,
-              dmnShapesByHref: state.computed(state).indexes().dmnShapesByHref,
+              dmnShapesByHref: 
state.computed(state).indexedDrd().dmnShapesByHref,
               drgElementIndex: index,
               shapeIndex: shape.index,
               localYPosition: e.y,
@@ -1102,7 +1096,6 @@ export const GroupNode = React.memo(
       nodeType: type as typeof NODE_TYPES.group,
       snapGrid,
       shape,
-      isExternal,
     });
     const setName = useCallback<OnEditableNodeLabelChange>(
       (newName: string) => {
@@ -1233,7 +1226,6 @@ export const UnknownNode = React.memo(
       nodeType: type as typeof NODE_TYPES.unknown,
       snapGrid,
       shape,
-      isExternal,
     });
 
     return (
@@ -1334,21 +1326,20 @@ function useNodeResizing(id: string): boolean {
   return RF.useStore((s) => s.nodeInternals.get(id)?.resizing ?? false);
 }
 
-type NodeDimensionsProps = {
+type NodeDimensionsArgs = {
   snapGrid: SnapGrid;
   shape: DMNDI15__DMNShape;
-  isExternal: boolean;
 } & (
   | { nodeType: Extract<NodeType, typeof NODE_TYPES.inputData>; 
isAlternativeInputDataShape: boolean }
   | { nodeType: Exclude<NodeType, typeof NODE_TYPES.inputData> }
 );
 
-function useNodeDimensions(props: NodeDimensionsProps): RF.Dimensions {
-  const { nodeType, snapGrid, shape, isExternal } = props;
-  const isAlternativeInputDataShape = nodeType === NODE_TYPES.inputData ? 
props.isAlternativeInputDataShape : false;
+function useNodeDimensions(args: NodeDimensionsArgs): RF.Dimensions {
+  const { nodeType, snapGrid, shape } = args;
+  const isAlternativeInputDataShape = nodeType === NODE_TYPES.inputData ? 
args.isAlternativeInputDataShape : false;
 
   return useMemo(() => {
-    if (nodeType === NODE_TYPES.decisionService && (isExternal || 
shape["@_isCollapsed"])) {
+    if (nodeType === NODE_TYPES.decisionService && shape["@_isCollapsed"]) {
       return DECISION_SERVICE_COLLAPSED_DIMENSIONS;
     }
 
@@ -1366,7 +1357,7 @@ function useNodeDimensions(props: NodeDimensionsProps): 
RF.Dimensions {
       width: snapShapeDimensions(snapGrid, shape, minSizes).width,
       height: snapShapeDimensions(snapGrid, shape, minSizes).height,
     };
-  }, [isAlternativeInputDataShape, isExternal, shape, snapGrid, nodeType]);
+  }, [isAlternativeInputDataShape, shape, snapGrid, nodeType]);
 }
 
 function useHoveredNodeAlwaysOnTop(
diff --git a/packages/dmn-editor/src/externalNodes/ExternalNodesPanel.tsx 
b/packages/dmn-editor/src/externalNodes/ExternalNodesPanel.tsx
index 659d4f7b43f..f30a5c7b437 100644
--- a/packages/dmn-editor/src/externalNodes/ExternalNodesPanel.tsx
+++ b/packages/dmn-editor/src/externalNodes/ExternalNodesPanel.tsx
@@ -56,7 +56,7 @@ export function ExternalNodesPanel() {
   const externalDmnsByNamespace = useDmnEditorStore(
     (s) => 
s.computed(s).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns
   );
-  const dmnShapesByHref = useDmnEditorStore((s) => 
s.computed(s).indexes().dmnShapesByHref);
+  const dmnShapesByHref = useDmnEditorStore((s) => 
s.computed(s).indexedDrd().dmnShapesByHref);
   const { onRequestToResolvePath } = useDmnEditor();
 
   const onDragStart = useCallback((event: React.DragEvent, externalNode: 
ExternalNode) => {
diff --git 
a/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts 
b/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts
new file mode 100644
index 00000000000..9e6ae2f91a2
--- /dev/null
+++ b/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts
@@ -0,0 +1,248 @@
+/*
+ * 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 {
+  DMN15__tDecisionService,
+  DMN15__tDefinitions,
+} from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
+import { DECISION_SERVICE_COLLAPSED_DIMENSIONS } from 
"../diagram/nodes/DefaultSizes";
+import { NODE_TYPES } from "../diagram/nodes/NodeTypes";
+import { Computed } from "../store/Store";
+import { computeDecisionServiceHrefsByDecisionHrefs } from 
"../store/computed/computeDecisionServiceHrefsByDecisionHrefs";
+import { computeIndexedDrd } from "../store/computed/computeIndexes";
+import { xmlHrefToQName } from "../xml/xmlHrefToQName";
+import { buildXmlHref, parseXmlHref } from "../xml/xmlHrefs";
+import { addShape } from "./addShape";
+import { repositionNode } from "./repositionNode";
+
+/**
+ * When adding a Decision Service to a DRD, we need to bring all its 
encapsulated and output Decisions with it,
+ * copying their layout from other DRDs, or formatting with autolayout.
+ */
+export function addExistingDecisionServiceToDrd({
+  decisionServiceNamespace,
+  decisionService,
+  externalDmnsIndex,
+  thisDmnsNamespace,
+  thisDmnsDefinitions,
+  thisDmnsIndexedDrd,
+  drdIndex,
+  dropPoint,
+}: {
+  decisionServiceNamespace: string;
+  decisionService: DMN15__tDecisionService;
+  externalDmnsIndex: 
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
+  thisDmnsNamespace: string;
+  thisDmnsDefinitions: DMN15__tDefinitions;
+  thisDmnsIndexedDrd: ReturnType<Computed["indexedDrd"]>;
+  drdIndex: number;
+  dropPoint: { x: number; y: number };
+}) {
+  const decisionServiceDmnDefinitions =
+    !decisionServiceNamespace || decisionServiceNamespace === thisDmnsNamespace
+      ? thisDmnsDefinitions
+      : externalDmnsIndex.get(decisionServiceNamespace)?.model.definitions;
+  if (!decisionServiceDmnDefinitions) {
+    throw new Error(`DMN MUTATION: Can't find definitions for model with 
namespace ${decisionServiceNamespace}`);
+  }
+  const { decisionServiceNamespaceForHref, 
containedDecisionHrefsRelativeToThisDmn } =
+    getDecisionServicePropertiesRelativeToThisDmn({
+      thisDmnsNamespace,
+      decisionServiceNamespace,
+      decisionService,
+    });
+
+  const decisionServiceHrefRelativeToThisDmn = buildXmlHref({
+    namespace: decisionServiceNamespaceForHref,
+    id: decisionService["@_id"]!,
+  });
+
+  const decisionServiceHrefsByDecisionHrefsRelativeToThisDmn = 
computeDecisionServiceHrefsByDecisionHrefs({
+    thisDmnsNamespace,
+    drgElementsNamespace: decisionServiceNamespace,
+    drgElements: decisionServiceDmnDefinitions.drgElement,
+  });
+
+  const doesThisDrdHaveConflictingDecisionService = 
containedDecisionHrefsRelativeToThisDmn.some((decisionHref) =>
+    (decisionServiceHrefsByDecisionHrefsRelativeToThisDmn.get(decisionHref) ?? 
[]).some((d) =>
+      thisDmnsIndexedDrd.dmnShapesByHref.has(d)
+    )
+  );
+
+  if (doesThisDrdHaveConflictingDecisionService) {
+    // There's already, in this DRD, a Decision Service in expanded form that 
contains a Decision that is contained by the Decision Service we're adding.
+    // As the DMN specification doesn't allow two copies of the same DRG 
element to be depicted in the same DRD, we can't add the Decision Service in 
expanded form.
+    // To not disallow depicting the Decision Service in this DRD, though, we 
add it in collpased form.
+    addShape({
+      definitions: thisDmnsDefinitions,
+      drdIndex,
+      nodeType: NODE_TYPES.decisionService,
+      shape: {
+        "@_dmnElementRef": 
xmlHrefToQName(decisionServiceHrefRelativeToThisDmn, thisDmnsDefinitions),
+        "@_isCollapsed": true,
+        "dc:Bounds": {
+          "@_x": dropPoint.x,
+          "@_y": dropPoint.y,
+          "@_width": DECISION_SERVICE_COLLAPSED_DIMENSIONS.width,
+          "@_height": DECISION_SERVICE_COLLAPSED_DIMENSIONS.height,
+        },
+      },
+    });
+    return;
+  }
+
+  const drds = 
decisionServiceDmnDefinitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"] ?? [];
+
+  let indexedDrd: ReturnType<Computed["indexedDrd"]> | undefined;
+  for (let i = 0; i < drds.length; i++) {
+    if (thisDmnsNamespace === decisionServiceNamespace && i === drdIndex) {
+      continue; // Skip the current DRD!
+    }
+
+    const _indexedDrd = computeIndexedDrd(thisDmnsNamespace, 
decisionServiceDmnDefinitions, i);
+    const dsShape = 
_indexedDrd.dmnShapesByHref.get(decisionServiceHrefRelativeToThisDmn);
+    const hasCompleteExpandedDepictionOfDecisionService =
+      dsShape &&
+      !(dsShape["@_isCollapsed"] ?? false) &&
+      containedDecisionHrefsRelativeToThisDmn.every((dHref) => 
_indexedDrd.dmnShapesByHref.has(dHref));
+
+    if (hasCompleteExpandedDepictionOfDecisionService) {
+      indexedDrd = _indexedDrd;
+      break; // Found a DRD with a complete expanded depiction of the Decision 
Service.
+    }
+  }
+
+  if (!indexedDrd) {
+    // There's no DRD which inclues a complete expanded depiction of the 
Decision Service. Let's proceed with auto-layout.
+    // TODO: Tiago
+  } else {
+    // Let's copy the expanded depiction of the Decision Service from `drd`.
+    // Adding or moving nodes that already exist in the current DRD to inside 
the Decision Service.
+    // The positions need all be relative to the Decision Service node, of 
course.
+    const dsShapeOnOtherDrd = 
indexedDrd.dmnShapesByHref.get(decisionServiceHrefRelativeToThisDmn);
+    if (
+      dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined ||
+      dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined
+    ) {
+      throw new Error(
+        `DMN MUTATION: Complete DMNShape for Decision Service with href 
${decisionServiceHrefRelativeToThisDmn} should've existed on the indexed DRD.`
+      );
+    }
+
+    addShape({
+      definitions: thisDmnsDefinitions,
+      drdIndex,
+      nodeType: NODE_TYPES.decisionService,
+      shape: {
+        "@_dmnElementRef": 
xmlHrefToQName(decisionServiceHrefRelativeToThisDmn, thisDmnsDefinitions),
+        "dc:Bounds": {
+          "@_x": dropPoint.x,
+          "@_y": dropPoint.y,
+          "@_width": dsShapeOnOtherDrd["dc:Bounds"]["@_width"],
+          "@_height": dsShapeOnOtherDrd["dc:Bounds"]["@_height"],
+        },
+      },
+    });
+
+    for (const decisionHref of containedDecisionHrefsRelativeToThisDmn) {
+      const decisionShapeOnOtherDrd = 
indexedDrd.dmnShapesByHref.get(decisionHref);
+      if (
+        decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined ||
+        decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined ||
+        decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_width"] === undefined ||
+        decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_height"] === undefined
+      ) {
+        throw new Error(
+          `DMN MUTATION: Complete DMNShape for Decision with href 
${decisionHref} should've existed on the indexed DRD.`
+        );
+      }
+
+      const x = dropPoint.x + (decisionShapeOnOtherDrd["dc:Bounds"]["@_x"] - 
dsShapeOnOtherDrd["dc:Bounds"]["@_x"]);
+      const y = dropPoint.y + (decisionShapeOnOtherDrd["dc:Bounds"]["@_y"] - 
dsShapeOnOtherDrd["dc:Bounds"]["@_y"]);
+
+      const existingDecisionShape = 
thisDmnsIndexedDrd.dmnShapesByHref.get(decisionHref);
+      if (existingDecisionShape) {
+        repositionNode({
+          definitions: thisDmnsDefinitions,
+          drdIndex,
+          controlWaypointsByEdge: new Map(),
+          change: {
+            nodeType: NODE_TYPES.decision,
+            type: "absolute",
+            position: { x, y },
+            shapeIndex: existingDecisionShape.index,
+            selectedEdges: [],
+            sourceEdgeIndexes: [],
+            targetEdgeIndexes: [],
+          },
+        });
+      } else {
+        const decisionNs = parseXmlHref(decisionHref).namespace;
+        const decisionDmnDefinitions =
+          !decisionNs || decisionNs === thisDmnsNamespace
+            ? thisDmnsDefinitions
+            : externalDmnsIndex.get(decisionNs)?.model.definitions;
+        if (!decisionDmnDefinitions) {
+          throw new Error(`DMN MUTATION: Can't find definitions for model with 
namespace ${decisionServiceNamespace}`);
+        }
+
+        addShape({
+          definitions: thisDmnsDefinitions,
+          drdIndex,
+          nodeType: NODE_TYPES.decision,
+          shape: {
+            "@_dmnElementRef": xmlHrefToQName(decisionHref, 
thisDmnsDefinitions),
+            "dc:Bounds": {
+              "@_x": x,
+              "@_y": y,
+              "@_width": decisionShapeOnOtherDrd["dc:Bounds"]["@_width"],
+              "@_height": decisionShapeOnOtherDrd["dc:Bounds"]["@_height"],
+            },
+          },
+        });
+      }
+    }
+  }
+}
+
+export function getDecisionServicePropertiesRelativeToThisDmn({
+  thisDmnsNamespace,
+  decisionServiceNamespace,
+  decisionService,
+}: {
+  thisDmnsNamespace: string;
+  decisionServiceNamespace: string;
+  decisionService: DMN15__tDecisionService;
+}) {
+  const decisionServiceNamespaceForHref =
+    decisionServiceNamespace === thisDmnsNamespace ? "" : 
decisionServiceNamespace;
+
+  const containedDecisionHrefsRelativeToThisDmn = [
+    ...(decisionService.outputDecision ?? []),
+    ...(decisionService.encapsulatedDecision ?? []),
+  ].map((d) => {
+    const parsedHref = parseXmlHref(d["@_href"]);
+    return buildXmlHref({
+      namespace: !parsedHref.namespace ? decisionServiceNamespaceForHref : 
parsedHref.namespace,
+      id: parsedHref.id,
+    });
+  });
+
+  return { decisionServiceNamespaceForHref, 
containedDecisionHrefsRelativeToThisDmn };
+}
diff --git a/packages/dmn-editor/src/mutations/addOrGetDrd.ts 
b/packages/dmn-editor/src/mutations/addOrGetDrd.ts
index a1ff36b1681..2200bfaf35c 100644
--- a/packages/dmn-editor/src/mutations/addOrGetDrd.ts
+++ b/packages/dmn-editor/src/mutations/addOrGetDrd.ts
@@ -30,7 +30,7 @@ export function addOrGetDrd({ definitions, drdIndex }: { 
definitions: DMN15__tDe
   // diagram
   definitions["dmndi:DMNDI"] ??= {};
   definitions["dmndi:DMNDI"]["dmndi:DMNDiagram"] ??= [];
-  definitions["dmndi:DMNDI"]["dmndi:DMNDiagram"][drdIndex] ??= {};
+  definitions["dmndi:DMNDI"]["dmndi:DMNDiagram"][drdIndex] ??= { "@_id": 
generateUuid() };
 
   const defaultDiagram = 
definitions["dmndi:DMNDI"]["dmndi:DMNDiagram"][drdIndex];
   defaultDiagram["@_id"] ??= generateUuid();
diff --git a/packages/dmn-editor/src/mutations/deleteImport.ts 
b/packages/dmn-editor/src/mutations/deleteImport.ts
index 1f9c8f1ba12..b934cb61e04 100644
--- a/packages/dmn-editor/src/mutations/deleteImport.ts
+++ b/packages/dmn-editor/src/mutations/deleteImport.ts
@@ -25,7 +25,7 @@ export function deleteImport({ definitions, index }: { 
definitions: DMN15__tDefi
   const [deleted] = definitions.import.splice(index, 1);
 
   const namespaceName = getXmlNamespaceDeclarationName({
-    model: definitions,
+    rootElement: definitions,
     namespace: deleted["@_namespace"],
   });
   if (namespaceName) {
diff --git a/packages/dmn-editor/src/mutations/deleteNode.ts 
b/packages/dmn-editor/src/mutations/deleteNode.ts
index 894e9c0b043..74ff8349827 100644
--- a/packages/dmn-editor/src/mutations/deleteNode.ts
+++ b/packages/dmn-editor/src/mutations/deleteNode.ts
@@ -27,6 +27,9 @@ import { buildXmlHref } from "../xml/xmlHrefs";
 import { Unpacked } from "../tsExt/tsExt";
 import { DrgEdge } from "../diagram/graph/graph";
 import { EdgeDeletionMode, deleteEdge } from "./deleteEdge";
+import { Computed } from "../store/Store";
+import { computeDecisionServiceHrefsByDecisionHrefs } from 
"../store/computed/computeDecisionServiceHrefsByDecisionHrefs";
+import { xmlHrefToQName } from "../xml/xmlHrefToQName";
 
 export enum NodeDeletionMode {
   FROM_DRG_AND_ALL_DRDS,
@@ -41,13 +44,15 @@ export function deleteNode({
   dmnObjectId,
   dmnObjectQName,
   dmnObjectNamespace,
+  externalDmnsIndex,
   mode,
 }: {
   definitions: DMN15__tDefinitions;
   drgEdges: DrgEdge[];
   drdIndex: number;
   nodeNature: NodeNature;
-  dmnObjectNamespace: string | undefined;
+  externalDmnsIndex: 
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
+  dmnObjectNamespace: string;
   dmnObjectId: string | undefined;
   dmnObjectQName: XmlQName;
   mode: NodeDeletionMode;
@@ -62,6 +67,7 @@ export function deleteNode({
       drdIndex,
       dmnObjectNamespace,
       dmnObjectId,
+      externalDmnsIndex,
     })
   ) {
     console.warn("DMN MUTATION: Cannot hide a Decision that's contained by a 
Decision Service from a DRD.");
@@ -176,31 +182,41 @@ export function canRemoveNodeFromDrdOnly({
   drdIndex,
   dmnObjectNamespace,
   dmnObjectId,
+  externalDmnsIndex,
 }: {
-  dmnObjectNamespace: string | undefined;
+  dmnObjectNamespace: string;
   dmnObjectId: string | undefined;
   definitions: DMN15__tDefinitions;
   drdIndex: number;
+  externalDmnsIndex: 
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
 }) {
   const { diagramElements } = addOrGetDrd({ definitions, drdIndex });
 
-  const dmnObjectHref = buildXmlHref({ namespace: dmnObjectNamespace, id: 
dmnObjectId! });
+  const dmnObjectHref = buildXmlHref({
+    namespace: dmnObjectNamespace === definitions["@_namespace"] ? "" : 
dmnObjectNamespace,
+    id: dmnObjectId!,
+  });
 
-  // FIXME: Tiago --> A Decision can be contained by more than one Decision 
Service.
-  const containingDecisionService = definitions.drgElement?.find(
-    (drgElement) =>
-      drgElement.__$$element === "decisionService" &&
-      [...(drgElement.encapsulatedDecision ?? []), 
...(drgElement.outputDecision ?? [])].some(
-        (dd) => dd["@_href"] === dmnObjectHref
-      )
-  );
+  const drgElements =
+    definitions["@_namespace"] === dmnObjectNamespace
+      ? definitions.drgElement ?? []
+      : 
externalDmnsIndex.get(dmnObjectNamespace)?.model.definitions.drgElement ?? [];
+
+  const decisionServiceHrefsByDecisionHrefs = 
computeDecisionServiceHrefsByDecisionHrefs({
+    thisDmnsNamespace: definitions["@_namespace"],
+    drgElementsNamespace: dmnObjectNamespace,
+    drgElements,
+  });
+
+  const containingDecisionServiceHrefs = 
decisionServiceHrefsByDecisionHrefs.get(dmnObjectHref) ?? [];
+
+  const isContainedByDecisionService = containingDecisionServiceHrefs.length > 
0;
 
-  const isContainingDecisionServicePresentInTheDrd =
-    containingDecisionService &&
+  const isContainingDecisionServicePresentInTheDrd = 
containingDecisionServiceHrefs.some((dsHref) =>
     diagramElements.some(
-      (s) =>
-        s.__$$element === "dmndi:DMNShape" &&
-        s["@_dmnElementRef"] === buildXmlQName({ type: "xml-qname", localPart: 
containingDecisionService["@_id"]! })
-    );
-  return !containingDecisionService || 
!isContainingDecisionServicePresentInTheDrd;
+      (e) => e.__$$element === "dmndi:DMNShape" && e["@_dmnElementRef"] === 
xmlHrefToQName(dsHref, definitions)
+    )
+  );
+
+  return !isContainedByDecisionService || 
!isContainingDecisionServicePresentInTheDrd;
 }
diff --git a/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx 
b/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx
index 7e0a9118291..ac113a6c48c 100644
--- a/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx
+++ b/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx
@@ -68,7 +68,7 @@ export function FontOptions({ startExpanded, nodeIds }: { 
startExpanded: boolean
   const dmnEditorStoreApi = useDmnEditorStoreApi();
 
   const shapeStyles = useDmnEditorStore((s) =>
-    nodeIds.map((nodeId) => 
s.computed(s).indexes().dmnShapesByHref.get(nodeId)?.["di:Style"])
+    nodeIds.map((nodeId) => 
s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId)?.["di:Style"])
   );
 
   const fontFamily = useMemo(() => shapeStyles[0]?.["@_fontFamily"], 
[shapeStyles]);
@@ -92,7 +92,7 @@ export function FontOptions({ startExpanded, nodeIds }: { 
startExpanded: boolean
         const { diagramElements } = addOrGetDrd({ definitions: 
s.dmn.model.definitions, drdIndex: s.diagram.drdIndex });
 
         const shapes = nodeIds.map((nodeId) => {
-          const shape = s.computed(s).indexes().dmnShapesByHref.get(nodeId);
+          const shape = s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId);
           if (!shape) {
             throw new Error(`DMN Shape for '${nodeId}' does not exist.`);
           }
diff --git a/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx 
b/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx
index 472efc48fe0..6e60ab02adc 100644
--- a/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx
+++ b/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx
@@ -51,7 +51,9 @@ export function ShapeOptions({
 }) {
   const dmnEditorStoreApi = useDmnEditorStoreApi();
 
-  const shapes = useDmnEditorStore((s) => nodeIds.map((nodeId) => 
s.computed(s).indexes().dmnShapesByHref.get(nodeId)));
+  const shapes = useDmnEditorStore((s) =>
+    nodeIds.map((nodeId) => 
s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId))
+  );
   const shapeStyles = useMemo(() => shapes.map((shape) => 
shape?.["di:Style"]), [shapes]);
 
   // For when a single node is selected.
@@ -82,7 +84,7 @@ export function ShapeOptions({
       dmnEditorStoreApi.setState((s) => {
         const { diagramElements } = addOrGetDrd({ definitions: 
s.dmn.model.definitions, drdIndex: s.diagram.drdIndex });
 
-        const index = nodeIds.map((nodeId) => 
s.computed(s).indexes().dmnShapesByHref.get(nodeId))[0]?.index ?? -1;
+        const index = nodeIds.map((nodeId) => 
s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId))[0]?.index ?? -1;
         if (index < 0) {
           throw new Error(`DMN Shape for '${nodeIds[0]}' does not exist.`);
         }
@@ -143,7 +145,7 @@ export function ShapeOptions({
         const { diagramElements } = addOrGetDrd({ definitions: 
s.dmn.model.definitions, drdIndex: s.diagram.drdIndex });
 
         const shapes = nodeIds.map((nodeId) => {
-          const shape = s.computed(s).indexes().dmnShapesByHref.get(nodeId);
+          const shape = s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId);
           if (!shape) {
             throw new Error(`DMN Shape for '${nodeId}' does not exist.`);
           }
diff --git a/packages/dmn-editor/src/store/Store.ts 
b/packages/dmn-editor/src/store/Store.ts
index feb47b5d0d1..61e42213672 100644
--- a/packages/dmn-editor/src/store/Store.ts
+++ b/packages/dmn-editor/src/store/Store.ts
@@ -31,7 +31,7 @@ import { computeDataTypes } from 
"./computed/computeDataTypes";
 import { computeDiagramData } from "./computed/computeDiagramData";
 import { computeExternalModelsByType } from 
"./computed/computeExternalModelsByType";
 import { computeImportsByNamespace } from 
"./computed/computeImportsByNamespace";
-import { computeIndexes } from "./computed/computeIndexes";
+import { computeIndexedDrd } from "./computed/computeIndexes";
 import { computeIsDropTargetNodeValidForSelection } from 
"./computed/computeIsDropTargetNodeValidForSelection";
 
 enableMapSet(); // Necessary because `Computed` has a lot of Maps and Sets.
@@ -123,7 +123,7 @@ export type Computed = {
 
   importsByNamespace(): Map<string, DMN15__tImport>;
 
-  indexes(): ReturnType<typeof computeIndexes>;
+  indexedDrd(): ReturnType<typeof computeIndexedDrd>;
 
   getDiagramData(e: ExternalModelsIndex | undefined): ReturnType<typeof 
computeDiagramData>;
 
@@ -329,8 +329,12 @@ export function createDmnEditorStore(model: 
State["dmn"]["model"], computedCache
             );
           },
 
-          indexes: () => {
-            return computedCache.cached("indexes", computeIndexes, 
[s.dmn.model.definitions, s.diagram.drdIndex]);
+          indexedDrd: () => {
+            return computedCache.cached("indexedDrd", computeIndexedDrd, [
+              s.dmn.model.definitions["@_namespace"],
+              s.dmn.model.definitions,
+              s.diagram.drdIndex,
+            ]);
           },
 
           importsByNamespace: () => {
@@ -377,7 +381,7 @@ export function createDmnEditorStore(model: 
State["dmn"]["model"], computedCache
               s.diagram,
               s.dmn.model.definitions,
               
s.computed(s).getExternalModelTypesByNamespace(externalModelsByNamespace),
-              s.computed(s).indexes(),
+              s.computed(s).indexedDrd(),
               s.computed(s).isAlternativeInputDataShape(),
             ]),
         };
diff --git 
a/packages/dmn-editor/src/store/computed/computeDecisionServiceHrefsByDecisionHrefs.ts
 
b/packages/dmn-editor/src/store/computed/computeDecisionServiceHrefsByDecisionHrefs.ts
new file mode 100644
index 00000000000..03bb1f530e7
--- /dev/null
+++ 
b/packages/dmn-editor/src/store/computed/computeDecisionServiceHrefsByDecisionHrefs.ts
@@ -0,0 +1,69 @@
+/*
+ * 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 { getDecisionServicePropertiesRelativeToThisDmn } from 
"../../mutations/addExistingDecisionServiceToDrd";
+import { buildXmlHref } from "../../xml/xmlHrefs";
+import { State } from "../Store";
+
+export function computeDecisionServiceHrefsByDecisionHrefs({
+  thisDmnsNamespace,
+  drgElementsNamespace,
+  drgElements,
+}: {
+  thisDmnsNamespace: string;
+  drgElementsNamespace: string;
+  drgElements: State["dmn"]["model"]["definitions"]["drgElement"];
+}) {
+  drgElements ??= [];
+  const decisionServiecHrefsByDecisionHrefs = new Map<string, string[]>();
+
+  for (const drgElement of drgElements) {
+    const drgElementHref = buildXmlHref({
+      namespace: drgElementsNamespace === thisDmnsNamespace ? "" : 
drgElementsNamespace,
+      id: drgElement["@_id"]!,
+    });
+
+    // Decision
+    if (drgElement.__$$element === "decision") {
+      decisionServiecHrefsByDecisionHrefs.set(
+        drgElementHref,
+        decisionServiecHrefsByDecisionHrefs.get(drgElementHref) ?? []
+      );
+    }
+    // DS
+    else if (drgElement.__$$element === "decisionService") {
+      const { containedDecisionHrefsRelativeToThisDmn } = 
getDecisionServicePropertiesRelativeToThisDmn({
+        thisDmnsNamespace,
+        decisionServiceNamespace: drgElementsNamespace,
+        decisionService: drgElement,
+      });
+
+      for (const containedDecisionHref of 
containedDecisionHrefsRelativeToThisDmn) {
+        decisionServiecHrefsByDecisionHrefs.set(containedDecisionHref, [
+          ...(decisionServiecHrefsByDecisionHrefs.get(containedDecisionHref) 
?? []),
+          drgElementHref,
+        ]);
+      }
+    } else {
+      // Ignore other elements
+    }
+  }
+
+  return decisionServiecHrefsByDecisionHrefs;
+}
diff --git a/packages/dmn-editor/src/store/computed/computeDiagramData.ts 
b/packages/dmn-editor/src/store/computed/computeDiagramData.ts
index e64bebb35e9..58ac1568323 100644
--- a/packages/dmn-editor/src/store/computed/computeDiagramData.ts
+++ b/packages/dmn-editor/src/store/computed/computeDiagramData.ts
@@ -35,6 +35,7 @@ import { Unpacked } from "../../tsExt/tsExt";
 import { buildXmlHref, parseXmlHref } from "../../xml/xmlHrefs";
 import { TypeOrReturnType } from "../ComputedStateCache";
 import { Computed, State } from "../Store";
+import { getDecisionServicePropertiesRelativeToThisDmn } from 
"../../mutations/addExistingDecisionServiceToDrd";
 
 export const NODE_LAYERS = {
   GROUP_NODE: 0,
@@ -61,7 +62,7 @@ export function computeDiagramData(
   diagram: State["diagram"],
   definitions: State["dmn"]["model"]["definitions"],
   externalModelTypesByNamespace: 
TypeOrReturnType<Computed["getExternalModelTypesByNamespace"]>,
-  indexes: TypeOrReturnType<Computed["indexes"]>,
+  indexedDrd: TypeOrReturnType<Computed["indexedDrd"]>,
   isAlternativeInputDataShape: boolean
 ) {
   // console.time("nodes");
@@ -93,9 +94,9 @@ export function computeDiagramData(
   const ackEdge: AckEdge = ({ id, type, dmnObject, source, target }) => {
     const data = {
       dmnObject,
-      dmnEdge: id ? indexes.dmnEdgesByDmnElementRef.get(id) : undefined,
-      dmnShapeSource: indexes.dmnShapesByHref.get(source),
-      dmnShapeTarget: indexes.dmnShapesByHref.get(target),
+      dmnEdge: id ? indexedDrd.dmnEdgesByDmnElementRef.get(id) : undefined,
+      dmnShapeSource: indexedDrd.dmnShapesByHref.get(source),
+      dmnShapeTarget: indexedDrd.dmnShapesByHref.get(target),
     };
 
     const edge: RF.Edge<DmnDiagramEdgeData> = {
@@ -165,7 +166,7 @@ export function computeDiagramData(
 
     const id = buildXmlHref({ namespace: dmnObjectNamespace, id: 
dmnObjectQName.localPart });
 
-    const _shape = indexes.dmnShapesByHref.get(id);
+    const _shape = indexedDrd.dmnShapesByHref.get(id);
     if (!_shape) {
       drgElementsWithoutVisualRepresentationOnCurrentDrd.push(id);
       return undefined;
@@ -204,11 +205,17 @@ export function computeDiagramData(
     };
 
     if (dmnObject?.__$$element === "decisionService") {
-      const containedDecisions = [...(dmnObject.outputDecision ?? []), 
...(dmnObject.encapsulatedDecision ?? [])];
-      for (let i = 0; i < containedDecisions.length; i++) {
-        parentIdsById.set(containedDecisions[i]["@_href"], data);
+      const { containedDecisionHrefsRelativeToThisDmn } = 
getDecisionServicePropertiesRelativeToThisDmn({
+        thisDmnsNamespace: definitions["@_namespace"],
+        decisionServiceNamespace: dmnObjectNamespace ?? 
definitions["@_namespace"],
+        decisionService: dmnObject,
+      });
+
+      for (let i = 0; i < containedDecisionHrefsRelativeToThisDmn.length; i++) 
{
+        parentIdsById.set(containedDecisionHrefsRelativeToThisDmn[i], data);
       }
-      if (shape["@_isCollapsed"] || !!dmnObjectNamespace) {
+
+      if (shape["@_isCollapsed"]) {
         newNode.style = {
           ...newNode.style,
           ...DECISION_SERVICE_COLLAPSED_DIMENSIONS,
@@ -239,24 +246,6 @@ export function computeDiagramData(
     }),
   ];
 
-  // Assign parents & z-index to NODES
-  for (let i = 0; i < localNodes.length; i++) {
-    const parent = parentIdsById.get(localNodes[i].id);
-    if (parent) {
-      localNodes[i].data.parentRfNode = nodesById.get(
-        buildXmlHref({ namespace: parent.dmnObjectNamespace, id: 
parent.dmnObjectQName.localPart })
-      );
-      localNodes[i].extent = undefined; // Allows the node to be dragged 
freely outside of parent's bounds.
-      localNodes[i].zIndex = NODE_LAYERS.NESTED_NODES;
-    }
-
-    if (localNodes[i].type === NODE_TYPES.group) {
-      localNodes[i].zIndex = NODE_LAYERS.GROUP_NODE;
-    } else if (localNodes[i].type === NODE_TYPES.decisionService) {
-      localNodes[i].zIndex = NODE_LAYERS.DECISION_SERVICE_NODE;
-    }
-  }
-
   const externalDrgElementsByIdByNamespace = 
[...externalModelTypesByNamespace.dmns.entries()].reduce(
     (acc, [namespace, externalDmn]) => {
       // Taking advantage of the loop to add the edges here...
@@ -278,12 +267,12 @@ export function computeDiagramData(
     new Map<string, Map<string, { index: number; element: 
Unpacked<DMN15__tDefinitions["drgElement"]> }>>()
   );
 
-  const externalNodes = [...indexes.dmnShapesByHref.entries()].flatMap(([href, 
shape]) => {
+  const externalNodes = 
[...indexedDrd.dmnShapesByHref.entries()].flatMap(([href, shape]) => {
     if (nodesById.get(href)) {
       return [];
     }
 
-    if (!nodesById.get(href) && 
!indexes.hrefsOfDmnElementRefsOfShapesPointingToExternalDmnObjects.has(href)) {
+    if (!nodesById.get(href) && 
!indexedDrd.hrefsOfDmnElementRefsOfShapesPointingToExternalDmnObjects.has(href))
 {
       // Unknown local node.
       console.warn(`DMN DIAGRAM: Found a shape that references a local DRG 
element that doesn't exist.`, shape);
       const newNode = ackNode(shape.dmnElementRefQName, null, -1);
@@ -346,6 +335,24 @@ export function computeDiagramData(
     assignClassesToHighlightedHierarchyNodes(diagram._selectedNodes, 
nodesById, edgesById, drgEdges);
   }
 
+  // Assign parents & z-index to NODES
+  for (let i = 0; i < sortedNodes.length; i++) {
+    const parentNodeData = parentIdsById.get(sortedNodes[i].id);
+    if (parentNodeData) {
+      sortedNodes[i].data.parentRfNode = nodesById.get(
+        buildXmlHref({ namespace: parentNodeData.dmnObjectNamespace, id: 
parentNodeData.dmnObjectQName.localPart })
+      );
+      sortedNodes[i].extent = undefined; // Allows the node to be dragged 
freely outside of parent's bounds.
+      sortedNodes[i].zIndex = NODE_LAYERS.NESTED_NODES;
+    }
+
+    if (sortedNodes[i].type === NODE_TYPES.group) {
+      sortedNodes[i].zIndex = NODE_LAYERS.GROUP_NODE;
+    } else if (sortedNodes[i].type === NODE_TYPES.decisionService) {
+      sortedNodes[i].zIndex = NODE_LAYERS.DECISION_SERVICE_NODE;
+    }
+  }
+
   return {
     drgEdges,
     drgAdjacencyList,
diff --git a/packages/dmn-editor/src/store/computed/computeIndexes.ts 
b/packages/dmn-editor/src/store/computed/computeIndexes.ts
index 9e5cd35e677..62bb6a68faf 100644
--- a/packages/dmn-editor/src/store/computed/computeIndexes.ts
+++ b/packages/dmn-editor/src/store/computed/computeIndexes.ts
@@ -23,7 +23,8 @@ import { KIE_DMN_UNKNOWN_NAMESPACE } from "../../Dmn15Spec";
 import { buildXmlHref } from "../../xml/xmlHrefs";
 import { State } from "../Store";
 
-export function computeIndexes(
+export function computeIndexedDrd(
+  thisDmnsNamespace: string,
   definitions: State["dmn"]["model"]["definitions"],
   drdIndex: State["diagram"]["drdIndex"]
 ) {
@@ -52,7 +53,10 @@ export function computeIndexes(
         href = buildXmlHref({ namespace, id: dmnElementRefQName.localPart });
         hrefsOfDmnElementRefsOfShapesPointingToExternalDmnObjects.add(href);
       } else {
-        href = buildXmlHref({ id: dmnElementRefQName.localPart });
+        href = buildXmlHref({
+          namespace: definitions["@_namespace"] === thisDmnsNamespace ? "" : 
definitions["@_namespace"],
+          id: dmnElementRefQName.localPart,
+        });
       }
 
       dmnShapesByHref.set(href, { ...e, index: i, dmnElementRefQName });
diff --git a/packages/dmn-editor/src/store/computed/initial.ts 
b/packages/dmn-editor/src/store/computed/initial.ts
index 12d509df42b..89c8d98bead 100644
--- a/packages/dmn-editor/src/store/computed/initial.ts
+++ b/packages/dmn-editor/src/store/computed/initial.ts
@@ -29,7 +29,7 @@ export const INITIAL_COMPUTED_CACHE: Cache<Computed> = {
     value: undefined,
     dependencies: [],
   },
-  indexes: {
+  indexedDrd: {
     value: undefined,
     dependencies: [],
   },
diff --git a/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts 
b/packages/dmn-editor/src/xml/xmlHrefToQName.ts
similarity index 56%
copy from packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
copy to packages/dmn-editor/src/xml/xmlHrefToQName.ts
index d6d67a22da6..cda3d3b46fa 100644
--- a/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
+++ b/packages/dmn-editor/src/xml/xmlHrefToQName.ts
@@ -17,26 +17,25 @@
  * under the License.
  */
 
+import { buildXmlQName } from "@kie-tools/xml-parser-ts/dist/qNames";
+import { parseXmlHref } from "./xmlHrefs";
+import { getXmlNamespaceDeclarationName } from "./xmlNamespaceDeclarations";
 import { XmlParserTsRootElementBaseType } from "@kie-tools/xml-parser-ts";
-import { parseXmlQName } from "@kie-tools/xml-parser-ts/dist/qNames";
 
-export function getXmlNamespaceDeclarationName({
-  model,
-  namespace,
-}: {
-  model: XmlParserTsRootElementBaseType | undefined;
-  namespace: string;
-}) {
-  const xmlnsEntry = Object.entries(model ?? {}).find(
-    ([k, v]) => v === namespace && (k === "@_xmlns" || 
k.startsWith("@_xmlns:"))
-  );
-  if (!xmlnsEntry) {
-    return undefined;
-  }
+export function xmlHrefToQName(hrefString: string, rootElement: 
XmlParserTsRootElementBaseType | undefined) {
+  const href = parseXmlHref(hrefString);
+
+  const qNamePrefix = href.namespace
+    ? getXmlNamespaceDeclarationName({ rootElement, namespace: href.namespace 
})
+    : undefined;
 
-  if (xmlnsEntry[0] === "@_xmlns") {
-    return undefined;
+  if (href.namespace && !qNamePrefix) {
+    throw new Error(`Can't find namespace declaration for namespace 
'${href.namespace}'`);
   }
 
-  return parseXmlQName(xmlnsEntry[0]).localPart;
+  return buildXmlQName({
+    type: "xml-qname",
+    localPart: href.id,
+    prefix: qNamePrefix,
+  });
 }
diff --git a/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts 
b/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
index d6d67a22da6..34c7147a123 100644
--- a/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
+++ b/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
@@ -21,13 +21,13 @@ import { XmlParserTsRootElementBaseType } from 
"@kie-tools/xml-parser-ts";
 import { parseXmlQName } from "@kie-tools/xml-parser-ts/dist/qNames";
 
 export function getXmlNamespaceDeclarationName({
-  model,
+  rootElement,
   namespace,
 }: {
-  model: XmlParserTsRootElementBaseType | undefined;
+  rootElement: XmlParserTsRootElementBaseType | undefined;
   namespace: string;
 }) {
-  const xmlnsEntry = Object.entries(model ?? {}).find(
+  const xmlnsEntry = Object.entries(rootElement ?? {}).find(
     ([k, v]) => v === namespace && (k === "@_xmlns" || 
k.startsWith("@_xmlns:"))
   );
   if (!xmlnsEntry) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to