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 ce934119e33 NO-ISSUE: Use the DMN marshaller on the DMN Language 
Service and temporarily disable the Validation panel for DMN 1.3+ on KIE 
Sandbox (#2105)
ce934119e33 is described below

commit ce934119e331c0383a81e50678eb5242bf80d7e8
Author: Luiz João Motta <[email protected]>
AuthorDate: Fri Jan 5 17:57:18 2024 -0300

    NO-ISSUE: Use the DMN marshaller on the DMN Language Service and 
temporarily disable the Validation panel for DMN 1.3+ on KIE Sandbox (#2105)
    
    Co-authored-by: Tiago Bento <[email protected]>
---
 .../dmn-language-service/src/DmnLanguageService.ts |  250 ++++-
 .../tests/buildImportIndex.test.ts                 | 1124 ++++++++++++++++++++
 .../tests/fixtures/{nested.dmn => dmn12/a.dmn}     |    0
 .../{recursive.dmn => dmn12/bImportsA.dmn}         |    2 +-
 .../fixtures/{model.dmn => dmn12/cImportsB.dmn}    |    3 +-
 .../fixtures/{model.dmn => dmn12/dImportsAB.dmn}   |    4 +-
 .../fixtures/{model.dmn => dmn12/eImportsXB.dmn}   |    4 +-
 .../fixtures/{model.dmn => dmn12/xImportsY.dmn}    |    3 +-
 .../tests/fixtures/{model.dmn => dmn12/y.dmn}      |    2 -
 .../tests/fixtures/dmn15/aImportsDmn12C.dmn        |   47 +
 .../tests/fixtures/dmn15/bImportsDmn12D.dmn        |   47 +
 .../fixtures/immediateRecursion/aImportsB.dmn      |   25 +
 .../fixtures/immediateRecursion/bImportsA.dmn      |   25 +
 .../fixtures/threeLevelRecursion/aImportsB.dmn     |   25 +
 .../fixtures/threeLevelRecursion/bImportsC.dmn     |   25 +
 .../fixtures/threeLevelRecursion/cImportsA.dmn     |   25 +
 packages/dmn-language-service/tests/fs/fixtures.ts |   94 ++
 .../dmn-language-service/tests/fs/getModelXml.ts   |   34 +
 .../tests/getDmnDocumentData.test.ts               |   35 +
 .../tests/getSpecVersion.test.ts                   |   42 +
 packages/dmn-language-service/tests/index.test.ts  |  129 ---
 .../src/bpmn/envelope/BpmnEditorFactory.ts         |    2 +-
 .../src/dmnRunner/DmnRunnerContextProvider.tsx     |   32 +-
 packages/online-editor/src/editor/EditorPage.tsx   |   25 +-
 packages/online-editor/src/editor/Validation.tsx   |  254 +++--
 25 files changed, 1926 insertions(+), 332 deletions(-)

diff --git a/packages/dmn-language-service/src/DmnLanguageService.ts 
b/packages/dmn-language-service/src/DmnLanguageService.ts
index 5bdc3b32f3f..7f800d2fa8d 100644
--- a/packages/dmn-language-service/src/DmnLanguageService.ts
+++ b/packages/dmn-language-service/src/DmnLanguageService.ts
@@ -20,87 +20,231 @@
 import { DmnDocumentData } from "./DmnDocumentData";
 import { DmnDecision } from "./DmnDecision";
 import * as path from "path";
+import { getMarshaller } from "@kie-tools/dmn-marshaller";
+import { DMN15__tDefinitions } from 
"@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
 
-const IMPORT = "import";
 const INPUT_DATA = "inputData";
 const XML_MIME = "text/xml";
-const LOCATION_URI_ATTRIBUTE = "locationURI";
 const DECISION_NAME_ATTRIBUTE = "name";
 const NAMESPACE = "namespace";
 const DMN_NAME = "name";
 const DECISION = "decision";
 const DEFINITIONS = "definitions";
 
-export interface DmnLanguageServiceImportedModelResources {
+/**
+ * The normalized posix path relative to the workspace root is a string
+ * Example of paths: "myFolderInsideWorkspace/myFile.txt"
+ */
+type NormalizedPosixPathRelativeToWorkspaceRoot = string & {}; // Stops 
TypeScript of auto casting to string;
+
+export interface DmnLanguageServiceResource {
   content: string;
-  pathRelativeToWorkspaceRoot: string;
+  normalizedPosixPathRelativeToTheWorkspaceRoot: 
NormalizedPosixPathRelativeToWorkspaceRoot;
+}
+
+/**
+ * The hierarchy is a map of NormalizedPosixPathRelativeToWorkspaceRoot to 
`deep` and `immediate` sets
+ * The `deep` Set contains all direct and indirect imported DMNs of the given 
DMN
+ * The `immediate` Set contains all direct imported DMNs of the given DMN
+ */
+type ImportIndexHierarchy = Map<
+  NormalizedPosixPathRelativeToWorkspaceRoot,
+  {
+    deep: Set<NormalizedPosixPathRelativeToWorkspaceRoot>;
+    immediate: Set<NormalizedPosixPathRelativeToWorkspaceRoot>;
+  }
+>;
+
+/**
+ * The models is a map of NormalizedPosixPathRelativeToWorkspaceRoot to 
`definitions` and `xml`
+ * The `definitions` is the parsed definitions of the given DMN
+ * The `xml` is the plain text of the given DMN
+ */
+type ImportIndexModels = Map<
+  NormalizedPosixPathRelativeToWorkspaceRoot,
+  {
+    definitions: DMN15__tDefinitions;
+    xml: string;
+  }
+>;
+
+/**
+ * The ImportIndex collects the hierarchy and the models of all imported DMNs
+ */
+export interface ImportIndex {
+  hierarchy: ImportIndexHierarchy;
+  models: ImportIndexModels;
 }
 
 export class DmnLanguageService {
-  private readonly parser = new DOMParser();
-  private readonly importTagRegExp = new RegExp(`([a-z]*:)?(${IMPORT})`);
-  private readonly inputDataRegEx = new RegExp(`([a-z]*:)?(${INPUT_DATA})`);
-  private readonly decisionsTagRegExp = new RegExp(`([a-z]*:)?(${DECISION})`);
-  private readonly definitionsTagRegExp = new 
RegExp(`([a-z]*:)?(${DEFINITIONS})`);
+  private readonly parser = new DOMParser(); // TODO: Delete this when the new 
Marshaller is being used for everything.
+  private readonly inputDataRegEx = new RegExp(`([a-z]*:)?(${INPUT_DATA})`); 
// TODO: Delete this when the new Marshaller is being used for everything.
+  private readonly decisionsTagRegExp = new RegExp(`([a-z]*:)?(${DECISION})`); 
// TODO: Delete this when the new Marshaller is being used for everything.
+  private readonly definitionsTagRegExp = new 
RegExp(`([a-z]*:)?(${DEFINITIONS})`); // TODO: Delete this when the new 
Marshaller is being used for everything.
 
   constructor(
     private readonly args: {
-      getDmnImportedModelResource: (
-        importedModelPathRelativeToWorkspaceRoot: string
-      ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+      getModelXml: (args: { normalizedPosixPathRelativeToTheWorkspaceRoot: 
string }) => Promise<string>;
     }
   ) {}
 
-  private getImportedModelPathRelativeToWorkspaceRoot(
-    modelResources: DmnLanguageServiceImportedModelResources
-  ): string[] {
-    const xmlContent = this.parser.parseFromString(modelResources.content, 
XML_MIME);
-    const importTag = this.importTagRegExp.exec(modelResources.content);
-    const importedModels = xmlContent.getElementsByTagName(importTag?.[0] ?? 
IMPORT);
-    return Array.from(importedModels)
-      .map((importedModel) =>
-        path.posix.join(
-          path.dirname(modelResources.pathRelativeToWorkspaceRoot),
-          path.normalize(importedModel.getAttribute(LOCATION_URI_ATTRIBUTE) ?? 
"")
-        )
-      )
-      .filter((e) => e !== null) as string[];
+  private async 
buildImportIndexModel(normalizedPosixPathRelativeToTheWorkspaceRoot: string) {
+    const xml = await this.args.getModelXml({ 
normalizedPosixPathRelativeToTheWorkspaceRoot });
+    return {
+      definitions: getMarshaller(xml, { upgradeTo: "latest" 
}).parser.parse().definitions,
+      xml,
+    };
   }
 
-  public getImportedModelPathsRelativeToWorkspaceRoot(
-    modelResources: DmnLanguageServiceImportedModelResources[] | 
DmnLanguageServiceImportedModelResources
-  ): string[] {
-    if (Array.isArray(modelResources)) {
-      return modelResources.flatMap((modelResource) => 
this.getImportedModelPathRelativeToWorkspaceRoot(modelResource));
+  private async recusivelyPopulateImportIndex(
+    normalizedPosixPathRelativeToTheWorkspaceRoot: string,
+    importIndex: ImportIndex,
+    parents: string[],
+    depth: number
+  ): Promise<void> {
+    // Depth === -1 means we're going to recursve forever.
+    // Depth === 0 means we'll stop without including any imports in the index
+    // Depth > 0 means we'll keep going down one level of imports at a time 
and add them to the index, when it reaches zero, we stop.
+    if (depth === 0) {
+      return;
+    }
+
+    // Add the current model to the index if not present
+    const model =
+      importIndex.models.get(normalizedPosixPathRelativeToTheWorkspaceRoot) ??
+      importIndex.models
+        .set(
+          normalizedPosixPathRelativeToTheWorkspaceRoot,
+          await 
this.buildImportIndexModel(normalizedPosixPathRelativeToTheWorkspaceRoot)
+        )
+        .get(normalizedPosixPathRelativeToTheWorkspaceRoot)!;
+
+    // Ensure a hierarchy always exists
+    const hierarchy =
+      importIndex.hierarchy.get(normalizedPosixPathRelativeToTheWorkspaceRoot) 
??
+      importIndex.hierarchy
+        .set(normalizedPosixPathRelativeToTheWorkspaceRoot, { immediate: new 
Set(), deep: new Set() })
+        .get(normalizedPosixPathRelativeToTheWorkspaceRoot)!;
+
+    // Iterate over the imports
+    const basedir = 
path.dirname(normalizedPosixPathRelativeToTheWorkspaceRoot);
+    for (const i of model.definitions.import ?? []) {
+      const locationUri = i["@_locationURI"];
+      if (!locationUri) {
+        // TODO: Write a test for this case temporarily, while we still depend 
on `locationURI`s and not exclusively on the namespace.
+        // Can't determine import without a locationURI.
+        console.warn(`Ignoring import with namespace '${i["@_namespace"]}' 
because it doesn't have a locationURI.`);
+        continue;
+      }
+
+      /** Normalized POSIX path of the DMN import relative to the workspace 
root.*/
+      const p = path.posix.join(basedir, path.posix.normalize(locationUri));
+
+      // Prevent cycles by looking if already in immediate hierarchy
+      if (hierarchy.immediate.has(p)) {
+        // We're going to abort the recursion, but some parents might not have 
had this import's hierarchy added to their deep dependencies lists.
+        for (const parent of parents) {
+          const parentHierarchy = importIndex.hierarchy.get(parent);
+
+          parentHierarchy?.deep.add(p);
+
+          for (const i of importIndex.hierarchy.get(p)?.deep ?? []) {
+            parentHierarchy?.deep.add(i);
+          }
+        }
+
+        continue; // Abort recursion, as a cycle has been found.
+      }
+
+      // Proceed normally, first by adding `p` to the hierarchy and to the 
parents too. Then recursing one more level down.
+      hierarchy.immediate.add(p);
+      hierarchy.deep.add(p);
+      for (const parent of parents) {
+        importIndex.hierarchy.get(parent)?.deep.add(p);
+      }
+
+      parents.push(normalizedPosixPathRelativeToTheWorkspaceRoot);
+      await this.recusivelyPopulateImportIndex(p, importIndex, parents, depth 
- 1);
+      parents.pop();
     }
+  }
+
+  /**
+   * This method collects the hierarchy and the models of all imported DMNs 
from the given DMNs
+   *
+   * @param resources the given resources to be used to build the `ImportIndex`
+   * @param depth the recursion max depth level of the hierarchy and models.
+   *
+   * Example:
+   *
+   * `-1: total recursion`
+   *
+   * `0: one level of recursion`
+   *
+   * @returns an `ImportIndex` with the hierarchy and models of all DMNs from 
the given resources. It includes the given resources and it'll build based on 
the given depth.
+   */
+  public async buildImportIndex(
+    resources: DmnLanguageServiceResource[],
+    depth = -1 // By default, we recurse infinitely.
+  ): Promise<ImportIndex> {
+    try {
+      const importIndex: ImportIndex = {
+        hierarchy: new Map(),
+        models: new Map(
+          resources.map((r) => [
+            r.normalizedPosixPathRelativeToTheWorkspaceRoot,
+            {
+              xml: r.content,
+              definitions: getMarshaller(r.content, { upgradeTo: "latest" 
}).parser.parse().definitions,
+            },
+          ])
+        ),
+      };
+
+      for (const r of resources) {
+        await this.recusivelyPopulateImportIndex(
+          r.normalizedPosixPathRelativeToTheWorkspaceRoot,
+          importIndex, // will be modified
+          [], // parents stack
+          depth // unaltered initial depth
+        );
+      }
 
-    return this.getImportedModelPathRelativeToWorkspaceRoot(modelResources);
+      return importIndex;
+    } catch (error) {
+      throw new Error(`
+DMN LANGUAGE SERVICE - buildImportIndex: Error while getting imported models 
from model resources.
+Tried to use the following model resources: ${JSON.stringify(resources)}
+Error details: ${error}`);
+    }
   }
 
+  // TODO: Rewrite this using the new Marshaller.
   // Receive all contents, paths and a node ID and returns the model that 
contains the node.
-  public getPathFromNodeId(resourceContents: 
DmnLanguageServiceImportedModelResources[], nodeId: string): string {
-    for (const resourceContent of resourceContents) {
+  public getPathFromNodeId(resources: DmnLanguageServiceResource[], nodeId: 
string): string {
+    for (const resourceContent of resources) {
       const xmlContent = this.parser.parseFromString(resourceContent.content 
?? "", XML_MIME);
       const inputDataTag = this.inputDataRegEx.exec(resourceContent.content ?? 
"");
       const inputs = xmlContent.getElementsByTagName(inputDataTag?.[0] ?? 
INPUT_DATA);
       for (const input of Array.from(inputs)) {
         if (input.id === nodeId) {
-          return resourceContent.pathRelativeToWorkspaceRoot;
+          return resourceContent.normalizedPosixPathRelativeToTheWorkspaceRoot;
         }
       }
     }
     return "";
   }
 
-  public getDmnDocumentData(dmnContent: string): DmnDocumentData {
-    const xmlContent = this.parser.parseFromString(dmnContent, XML_MIME);
-    const definitionsTag = this.definitionsTagRegExp.exec(dmnContent);
+  // TODO: Rewrite this using the new Marshaller.
+  public getDmnDocumentData(xml: string): DmnDocumentData {
+    const xmlContent = this.parser.parseFromString(xml, XML_MIME);
+    const definitionsTag = this.definitionsTagRegExp.exec(xml);
     const definitions = xmlContent.getElementsByTagName(definitionsTag ? 
definitionsTag[0] : DEFINITIONS);
     const definition = definitions[0];
     const namespace = definition.getAttribute(NAMESPACE);
     const dmnModelName = definition.getAttribute(DMN_NAME);
 
-    const dmnDecisions = this.decisionsTagRegExp.exec(dmnContent);
+    const dmnDecisions = this.decisionsTagRegExp.exec(xml);
     const dmnDecisionsContent = xmlContent.getElementsByTagName(dmnDecisions ? 
dmnDecisions[0] : DECISION);
 
     const decisions = Array.from(dmnDecisionsContent)
@@ -109,27 +253,15 @@ export class DmnLanguageService {
     return new DmnDocumentData(namespace ?? "", dmnModelName ?? "", decisions);
   }
 
-  // recursively get imported models
-  public async getAllImportedModelsResources(
-    modelsResources: DmnLanguageServiceImportedModelResources[]
-  ): Promise<DmnLanguageServiceImportedModelResources[]> {
-    // get imported models resources
-    const importedModelsPathsRelativeToWorkspaceRoot =
-      this.getImportedModelPathsRelativeToWorkspaceRoot(modelsResources);
-    if (importedModelsPathsRelativeToWorkspaceRoot && 
importedModelsPathsRelativeToWorkspaceRoot.length > 0) {
-      const importedModelsResources = (
-        await Promise.all(
-          
importedModelsPathsRelativeToWorkspaceRoot.map((importedModelPathRelativeToOpenFile)
 =>
-            
this.args.getDmnImportedModelResource(importedModelPathRelativeToOpenFile)
-          )
-        )
-      ).filter((e) => e !== undefined) as 
DmnLanguageServiceImportedModelResources[];
+  public getSpecVersion(xml: string) {
+    if (xml === "") {
+      return;
+    }
 
-      if (importedModelsResources.length > 0) {
-        return [...importedModelsResources, ...(await 
this.getAllImportedModelsResources(importedModelsResources))];
-      }
-      return [...importedModelsResources];
+    try {
+      return getMarshaller(xml).originalVersion;
+    } catch (error) {
+      return;
     }
-    return [];
   }
 }
diff --git a/packages/dmn-language-service/tests/buildImportIndex.test.ts 
b/packages/dmn-language-service/tests/buildImportIndex.test.ts
new file mode 100644
index 00000000000..13808c0a8f8
--- /dev/null
+++ b/packages/dmn-language-service/tests/buildImportIndex.test.ts
@@ -0,0 +1,1124 @@
+/*
+ * 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 { getMarshaller } from "@kie-tools/dmn-marshaller";
+import { DMN15__tDefinitions } from 
"@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
+import * as __path from "path";
+import { DmnLanguageService } from "../src";
+import {
+  dmn12A,
+  dmn12B,
+  dmn12C,
+  dmn12D,
+  dmn12E,
+  dmn12X,
+  dmn12Y,
+  dmn15A,
+  dmn15B,
+  immediateRecursionA,
+  immediateRecursionB,
+  threeLevelRecursionA,
+  threeLevelRecursionB,
+  threeLevelRecursionC,
+} from "./fs/fixtures";
+import { asyncGetModelXmlForTestFixtures } from "./fs/getModelXml";
+
+class NoErrorThrownError extends Error {}
+
+const getError = async <E>(call: () => unknown): Promise<E> => {
+  try {
+    await call();
+    throw new NoErrorThrownError();
+  } catch (error: unknown) {
+    return error as E;
+  }
+};
+
+describe("invalid inputs", () => {
+  it("empty string", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const emptyResource = [
+      {
+        content: "",
+        normalizedPosixPathRelativeToTheWorkspaceRoot: "",
+      },
+    ];
+
+    const error: Error = await getError(async () => await 
dmnLs.buildImportIndex(emptyResource));
+
+    expect(error.message).toEqual(`
+DMN LANGUAGE SERVICE - buildImportIndex: Error while getting imported models 
from model resources.
+Tried to use the following model resources: ${JSON.stringify(emptyResource)}
+Error details: SyntaxError: about:blank:1:0: document must contain a root 
element.`);
+  });
+
+  it("invalid dmn", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const invalidResource = [
+      {
+        content: `<?xml version="1.0" encoding="UTF-8"?>
+<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_FF389036-1B31-408E-9721-3704AE54EBF6"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_2EFA2297-868F-4243-96C8-DCCD82F25F30";
 id= [...]
+<<invalid/>/>
+</dmn:definitions>
+`,
+        normalizedPosixPathRelativeToTheWorkspaceRoot: "",
+      },
+    ];
+
+    const error: Error = await getError(async () => await 
dmnLs.buildImportIndex(invalidResource));
+
+    expect(error.message).toEqual(`
+DMN LANGUAGE SERVICE - buildImportIndex: Error while getting imported models 
from model resources.
+Tried to use the following model resources: ${JSON.stringify(invalidResource)}
+Error details: SyntaxError: about:blank:3:2: disallowed character in tag 
name`);
+  });
+});
+
+describe("retrieve import index from single model resource", () => {
+  it("dmn 1.2 - dmn without imports - correctly populate the immediate and 
deep hierarchy", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: dmn12A(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: "fixtures/dmn12/a.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }]])
+    );
+  });
+
+  it("dmn 1.2 - correctly populate the immediate and deep hierarchy", async () 
=> {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: dmn12D(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/dImportsAB.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/dImportsAB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn12/dImportsAB.dmn", { xml: dmn12D(), definitions: 
getDefinitions(dmn12D()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+        ["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }],
+      ])
+    );
+  });
+
+  it("dmn 1.2 - correctly populate the immediate and deep hierarchy for depth 
= 1", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+    const depth = 1;
+
+    const importIndex = await dmnLs.buildImportIndex(
+      [
+        {
+          content: dmn12C(),
+          normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/cImportsB.dmn",
+        },
+      ],
+      depth
+    );
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/cImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn"]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([["fixtures/dmn12/cImportsB.dmn", { xml: dmn12C(), definitions: 
getDefinitions(dmn12C()) }]])
+    );
+  });
+
+  it("dmn 1.2 - correctly populate the immediate and deep hierarchy for depth 
= 2", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+    const depth = 2;
+
+    const importIndex = await dmnLs.buildImportIndex(
+      [
+        {
+          content: dmn12C(),
+          normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/cImportsB.dmn",
+        },
+      ],
+      depth
+    );
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/cImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn12/cImportsB.dmn", { xml: dmn12C(), definitions: 
getDefinitions(dmn12C()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+      ])
+    );
+  });
+
+  it("dmn 1.2 - correctly populate the immediate and deep hierarchy for depth 
= 3", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+    const depth = 3;
+
+    const importIndex = await dmnLs.buildImportIndex(
+      [
+        {
+          content: dmn12C(),
+          normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/cImportsB.dmn",
+        },
+      ],
+      depth
+    );
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/cImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn12/cImportsB.dmn", { xml: dmn12C(), definitions: 
getDefinitions(dmn12C()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+        ["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }],
+      ])
+    );
+  });
+
+  it("dmn 1.5 - correctly populate the immediate and deep hierarchy", async () 
=> {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: dmn15B(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/bImportsDmn12D.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn15/bImportsDmn12D.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/dImportsAB.dmn"]),
+            deep: new Set(["fixtures/dmn12/dImportsAB.dmn", 
"fixtures/dmn12/bImportsA.dmn", "fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/dImportsAB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn15/bImportsDmn12D.dmn", { xml: dmn15B(), definitions: 
getDefinitions(dmn15B()) }],
+        ["fixtures/dmn12/dImportsAB.dmn", { xml: dmn12D(), definitions: 
getDefinitions(dmn12D()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+        ["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }],
+      ])
+    );
+  });
+
+  it("dmn 1.5 - correctly populate the immediate and deep hierarchy for depth 
= 1", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+    const depth = 1;
+
+    const importIndex = await dmnLs.buildImportIndex(
+      [
+        {
+          content: dmn15A(),
+          normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/aImportsDmn12C.dmn",
+        },
+      ],
+      depth
+    );
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn15/aImportsDmn12C.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/cImportsB.dmn"]),
+            deep: new Set(["fixtures/dmn12/cImportsB.dmn"]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([["fixtures/dmn15/aImportsDmn12C.dmn", { xml: dmn15A(), 
definitions: getDefinitions(dmn15A()) }]])
+    );
+  });
+
+  it("dmn 1.5 - correctly populate the immediate and deep hierarchy for depth 
= 2", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+    const depth = 2;
+
+    const importIndex = await dmnLs.buildImportIndex(
+      [
+        {
+          content: dmn15A(),
+          normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/aImportsDmn12C.dmn",
+        },
+      ],
+      depth
+    );
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn15/aImportsDmn12C.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/cImportsB.dmn"]),
+            deep: new Set(["fixtures/dmn12/cImportsB.dmn", 
"fixtures/dmn12/bImportsA.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/cImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn"]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn15/aImportsDmn12C.dmn", { xml: dmn15A(), definitions: 
getDefinitions(dmn15A()) }],
+        ["fixtures/dmn12/cImportsB.dmn", { xml: dmn12C(), definitions: 
getDefinitions(dmn12C()) }],
+      ])
+    );
+  });
+
+  it("dmn 1.5 - correctly populate the immediate and deep hierarchy for depth 
= 2 for dmn15B", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+    const depth = 2;
+
+    const importIndex = await dmnLs.buildImportIndex(
+      [
+        {
+          content: dmn15B(),
+          normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/bImportsDmn12D.dmn",
+        },
+      ],
+      depth
+    );
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn15/bImportsDmn12D.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/dImportsAB.dmn"]),
+            deep: new Set(["fixtures/dmn12/dImportsAB.dmn", 
"fixtures/dmn12/bImportsA.dmn", "fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/dImportsAB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn15/bImportsDmn12D.dmn", { xml: dmn15B(), definitions: 
getDefinitions(dmn15B()) }],
+        ["fixtures/dmn12/dImportsAB.dmn", { xml: dmn12D(), definitions: 
getDefinitions(dmn12D()) }],
+      ])
+    );
+  });
+
+  it("immediate recursion", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: immediateRecursionA(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/immediateRecursion/aImportsB.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/immediateRecursion/aImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/immediateRecursion/bImportsA.dmn"]),
+            deep: new Set(["fixtures/immediateRecursion/bImportsA.dmn", 
"fixtures/immediateRecursion/aImportsB.dmn"]),
+          },
+        ],
+        [
+          "fixtures/immediateRecursion/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/immediateRecursion/aImportsB.dmn"]),
+            deep: new Set(["fixtures/immediateRecursion/aImportsB.dmn", 
"fixtures/immediateRecursion/bImportsA.dmn"]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        [
+          "fixtures/immediateRecursion/aImportsB.dmn",
+          { xml: immediateRecursionA(), definitions: 
getDefinitions(immediateRecursionA()) },
+        ],
+        [
+          "fixtures/immediateRecursion/bImportsA.dmn",
+          { xml: immediateRecursionB(), definitions: 
getDefinitions(immediateRecursionB()) },
+        ],
+      ])
+    );
+  });
+
+  it("three level recursion", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: threeLevelRecursionA(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/threeLevelRecursion/aImportsB.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/threeLevelRecursion/aImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/threeLevelRecursion/bImportsC.dmn"]),
+            deep: new Set([
+              "fixtures/threeLevelRecursion/bImportsC.dmn",
+              "fixtures/threeLevelRecursion/cImportsA.dmn",
+              "fixtures/threeLevelRecursion/aImportsB.dmn",
+            ]),
+          },
+        ],
+        [
+          "fixtures/threeLevelRecursion/bImportsC.dmn",
+          {
+            immediate: new Set(["fixtures/threeLevelRecursion/cImportsA.dmn"]),
+            deep: new Set([
+              "fixtures/threeLevelRecursion/bImportsC.dmn",
+              "fixtures/threeLevelRecursion/cImportsA.dmn",
+              "fixtures/threeLevelRecursion/aImportsB.dmn",
+            ]),
+          },
+        ],
+        [
+          "fixtures/threeLevelRecursion/cImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/threeLevelRecursion/aImportsB.dmn"]),
+            deep: new Set([
+              "fixtures/threeLevelRecursion/bImportsC.dmn",
+              "fixtures/threeLevelRecursion/cImportsA.dmn",
+              "fixtures/threeLevelRecursion/aImportsB.dmn",
+            ]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        [
+          "fixtures/threeLevelRecursion/aImportsB.dmn",
+          { xml: threeLevelRecursionA(), definitions: 
getDefinitions(threeLevelRecursionA()) },
+        ],
+        [
+          "fixtures/threeLevelRecursion/bImportsC.dmn",
+          { xml: threeLevelRecursionB(), definitions: 
getDefinitions(threeLevelRecursionB()) },
+        ],
+        [
+          "fixtures/threeLevelRecursion/cImportsA.dmn",
+          { xml: threeLevelRecursionC(), definitions: 
getDefinitions(threeLevelRecursionC()) },
+        ],
+      ])
+    );
+  });
+
+  it("dmn with two distincts import tree's", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: dmn12E(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/eImportsXB.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/eImportsXB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/xImportsY.dmn"]),
+            deep: new Set([
+              "fixtures/dmn12/bImportsA.dmn",
+              "fixtures/dmn12/a.dmn",
+              "fixtures/dmn12/xImportsY.dmn",
+              "fixtures/dmn12/y.dmn",
+            ]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+        [
+          "fixtures/dmn12/xImportsY.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/y.dmn"]),
+            deep: new Set(["fixtures/dmn12/y.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/y.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn12/eImportsXB.dmn", { xml: dmn12E(), definitions: 
getDefinitions(dmn12E()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+        ["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }],
+        ["fixtures/dmn12/xImportsY.dmn", { xml: dmn12X(), definitions: 
getDefinitions(dmn12X()) }],
+        ["fixtures/dmn12/y.dmn", { xml: dmn12Y(), definitions: 
getDefinitions(dmn12Y()) }],
+      ])
+    );
+  });
+});
+
+describe("retrieve import index from multi model resources", () => {
+  it("two dmns with commom deep imports - correctly populate the deep 
hierarchy", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: dmn12C(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/cImportsB.dmn",
+      },
+      {
+        content: dmn15B(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/bImportsDmn12D.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/cImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn15/bImportsDmn12D.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/dImportsAB.dmn"]),
+            deep: new Set(["fixtures/dmn12/dImportsAB.dmn", 
"fixtures/dmn12/bImportsA.dmn", "fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/dImportsAB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn12/dImportsAB.dmn", { xml: dmn12D(), definitions: 
getDefinitions(dmn12D()) }],
+        ["fixtures/dmn12/cImportsB.dmn", { xml: dmn12C(), definitions: 
getDefinitions(dmn12C()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+        ["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }],
+        ["fixtures/dmn15/bImportsDmn12D.dmn", { xml: dmn15B(), definitions: 
getDefinitions(dmn15B()) }],
+      ])
+    );
+  });
+
+  it("two dmns with commom deep imports - correctly populate the deep 
hierarchy - immediate different of deep", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: dmn12C(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/cImportsB.dmn",
+      },
+      {
+        content: dmn15A(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/aImportsDmn12C.dmn",
+      },
+    ]);
+
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/cImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn15/aImportsDmn12C.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/cImportsB.dmn"]),
+            deep: new Set(["fixtures/dmn12/cImportsB.dmn", 
"fixtures/dmn12/bImportsA.dmn", "fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn15/aImportsDmn12C.dmn", { xml: dmn15A(), definitions: 
getDefinitions(dmn15A()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+        ["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }],
+        ["fixtures/dmn12/cImportsB.dmn", { xml: dmn12C(), definitions: 
getDefinitions(dmn12C()) }],
+      ])
+    );
+  });
+
+  it("two dmns with commom deep imports - correctly populate the deep 
hierarchy - immediate equal to deep", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: dmn12D(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/dImportsAB.dmn",
+      },
+      {
+        content: dmn15B(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/bImportsDmn12D.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/dImportsAB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn15/bImportsDmn12D.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/dImportsAB.dmn"]),
+            deep: new Set(["fixtures/dmn12/dImportsAB.dmn", 
"fixtures/dmn12/bImportsA.dmn", "fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn12/dImportsAB.dmn", { xml: dmn12D(), definitions: 
getDefinitions(dmn12D()) }],
+        ["fixtures/dmn15/bImportsDmn12D.dmn", { xml: dmn15B(), definitions: 
getDefinitions(dmn15B()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+        ["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }],
+      ])
+    );
+  });
+
+  it("two dmns with commom deep imports - correctly populate the deep 
hierarchy - immediate equal to deep - change order", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: dmn15B(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/bImportsDmn12D.dmn",
+      },
+      {
+        content: dmn12D(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/dImportsAB.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/dImportsAB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn15/bImportsDmn12D.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/dImportsAB.dmn"]),
+            deep: new Set(["fixtures/dmn12/dImportsAB.dmn", 
"fixtures/dmn12/bImportsA.dmn", "fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn12/dImportsAB.dmn", { xml: dmn12D(), definitions: 
getDefinitions(dmn12D()) }],
+        ["fixtures/dmn15/bImportsDmn12D.dmn", { xml: dmn15B(), definitions: 
getDefinitions(dmn15B()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+        ["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }],
+      ])
+    );
+  });
+
+  it("two equal dmns", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: dmn15B(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/bImportsDmn12D.dmn",
+      },
+      {
+        content: dmn15B(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/bImportsDmn12D.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/dImportsAB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn15/bImportsDmn12D.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/dImportsAB.dmn"]),
+            deep: new Set(["fixtures/dmn12/dImportsAB.dmn", 
"fixtures/dmn12/bImportsA.dmn", "fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn12/dImportsAB.dmn", { xml: dmn12D(), definitions: 
getDefinitions(dmn12D()) }],
+        ["fixtures/dmn15/bImportsDmn12D.dmn", { xml: dmn15B(), definitions: 
getDefinitions(dmn15B()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+        ["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }],
+      ])
+    );
+  });
+
+  it("five dmns - correctly populate the deep hierarchy - immediate equal to 
deep", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: dmn12B(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/bImportsA.dmn",
+      },
+      {
+        content: dmn15B(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/bImportsDmn12D.dmn",
+      },
+      {
+        content: dmn12D(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/dImportsAB.dmn",
+      },
+      {
+        content: dmn15A(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/aImportsDmn12C.dmn",
+      },
+      {
+        content: dmn12C(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn12/cImportsB.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/dmn12/dImportsAB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn15/bImportsDmn12D.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/dImportsAB.dmn"]),
+            deep: new Set(["fixtures/dmn12/dImportsAB.dmn", 
"fixtures/dmn12/bImportsA.dmn", "fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/cImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/bImportsA.dmn"]),
+            deep: new Set(["fixtures/dmn12/bImportsA.dmn", 
"fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn15/aImportsDmn12C.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/cImportsB.dmn"]),
+            deep: new Set(["fixtures/dmn12/cImportsB.dmn", 
"fixtures/dmn12/bImportsA.dmn", "fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/dmn12/a.dmn"]),
+            deep: new Set(["fixtures/dmn12/a.dmn"]),
+          },
+        ],
+        [
+          "fixtures/dmn12/a.dmn",
+          {
+            immediate: new Set([]),
+            deep: new Set([]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        ["fixtures/dmn12/cImportsB.dmn", { xml: dmn12C(), definitions: 
getDefinitions(dmn12C()) }],
+        ["fixtures/dmn12/dImportsAB.dmn", { xml: dmn12D(), definitions: 
getDefinitions(dmn12D()) }],
+        ["fixtures/dmn15/bImportsDmn12D.dmn", { xml: dmn15B(), definitions: 
getDefinitions(dmn15B()) }],
+        ["fixtures/dmn15/aImportsDmn12C.dmn", { xml: dmn15A(), definitions: 
getDefinitions(dmn15A()) }],
+        ["fixtures/dmn12/bImportsA.dmn", { xml: dmn12B(), definitions: 
getDefinitions(dmn12B()) }],
+        ["fixtures/dmn12/a.dmn", { xml: dmn12A(), definitions: 
getDefinitions(dmn12A()) }],
+      ])
+    );
+  });
+
+  it("two dmns that import each other - immediate recursion", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: immediateRecursionA(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/immediateRecursion/aImportsB.dmn",
+      },
+      {
+        content: immediateRecursionB(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/immediateRecursion/bImportsA.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/immediateRecursion/aImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/immediateRecursion/bImportsA.dmn"]),
+            deep: new Set(["fixtures/immediateRecursion/bImportsA.dmn", 
"fixtures/immediateRecursion/aImportsB.dmn"]),
+          },
+        ],
+        [
+          "fixtures/immediateRecursion/bImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/immediateRecursion/aImportsB.dmn"]),
+            deep: new Set(["fixtures/immediateRecursion/aImportsB.dmn", 
"fixtures/immediateRecursion/bImportsA.dmn"]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        [
+          "fixtures/immediateRecursion/aImportsB.dmn",
+          { xml: immediateRecursionA(), definitions: 
getDefinitions(immediateRecursionA()) },
+        ],
+        [
+          "fixtures/immediateRecursion/bImportsA.dmn",
+          { xml: immediateRecursionB(), definitions: 
getDefinitions(immediateRecursionB()) },
+        ],
+      ])
+    );
+  });
+
+  it("three dmns that import each other - three level recursion", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const importIndex = await dmnLs.buildImportIndex([
+      {
+        content: threeLevelRecursionA(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/threeLevelRecursion/aImportsB.dmn",
+      },
+      {
+        content: threeLevelRecursionB(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/threeLevelRecursion/bImportsC.dmn",
+      },
+      {
+        content: threeLevelRecursionC(),
+        normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/threeLevelRecursion/cImportsA.dmn",
+      },
+    ]);
+    expect(importIndex.hierarchy).toEqual(
+      new Map([
+        [
+          "fixtures/threeLevelRecursion/aImportsB.dmn",
+          {
+            immediate: new Set(["fixtures/threeLevelRecursion/bImportsC.dmn"]),
+            deep: new Set([
+              "fixtures/threeLevelRecursion/bImportsC.dmn",
+              "fixtures/threeLevelRecursion/cImportsA.dmn",
+              "fixtures/threeLevelRecursion/aImportsB.dmn",
+            ]),
+          },
+        ],
+        [
+          "fixtures/threeLevelRecursion/bImportsC.dmn",
+          {
+            immediate: new Set(["fixtures/threeLevelRecursion/cImportsA.dmn"]),
+            deep: new Set([
+              "fixtures/threeLevelRecursion/bImportsC.dmn",
+              "fixtures/threeLevelRecursion/cImportsA.dmn",
+              "fixtures/threeLevelRecursion/aImportsB.dmn",
+            ]),
+          },
+        ],
+        [
+          "fixtures/threeLevelRecursion/cImportsA.dmn",
+          {
+            immediate: new Set(["fixtures/threeLevelRecursion/aImportsB.dmn"]),
+            deep: new Set([
+              "fixtures/threeLevelRecursion/bImportsC.dmn",
+              "fixtures/threeLevelRecursion/cImportsA.dmn",
+              "fixtures/threeLevelRecursion/aImportsB.dmn",
+            ]),
+          },
+        ],
+      ])
+    );
+    expect(importIndex.models).toEqual(
+      new Map([
+        [
+          "fixtures/threeLevelRecursion/aImportsB.dmn",
+          { xml: threeLevelRecursionA(), definitions: 
getDefinitions(threeLevelRecursionA()) },
+        ],
+        [
+          "fixtures/threeLevelRecursion/bImportsC.dmn",
+          { xml: threeLevelRecursionB(), definitions: 
getDefinitions(threeLevelRecursionB()) },
+        ],
+        [
+          "fixtures/threeLevelRecursion/cImportsA.dmn",
+          { xml: threeLevelRecursionC(), definitions: 
getDefinitions(threeLevelRecursionC()) },
+        ],
+      ])
+    );
+  });
+});
+
+describe("invalid model resources", () => {
+  it("content", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const dmnModelResources = [{ content: "aaa", 
normalizedPosixPathRelativeToTheWorkspaceRoot: "fixtures/model.dmn" }];
+
+    const error: Error = await getError(async () => await 
dmnLs.buildImportIndex(dmnModelResources));
+
+    expect(error).not.toBeInstanceOf(NoErrorThrownError);
+    expect(error.message).toEqual(`
+DMN LANGUAGE SERVICE - buildImportIndex: Error while getting imported models 
from model resources.
+Tried to use the following model resources: 
${JSON.stringify(dmnModelResources)}
+Error details: SyntaxError: about:blank:1:3: text data outside of root node.`);
+  });
+
+  it("normalizedPosixPathRelativeToTheWorkspaceRoot", async () => {
+    const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+    const dmnModelResources = [{ content: dmn12D(), 
normalizedPosixPathRelativeToTheWorkspaceRoot: "aaa" }];
+    const fsAbsoluteModelPath = __path.resolve(__dirname, 
"a.dmn".split("/").join(__path.sep));
+
+    const error: Error = await getError(async () => await 
dmnLs.buildImportIndex(dmnModelResources));
+    expect(error).not.toBeInstanceOf(NoErrorThrownError);
+    expect(error.message).toEqual(`
+DMN LANGUAGE SERVICE - buildImportIndex: Error while getting imported models 
from model resources.
+Tried to use the following model resources: 
${JSON.stringify(dmnModelResources)}
+Error details: Error: ENOENT: no such file or directory, open 
'${fsAbsoluteModelPath}'`);
+  });
+});
+
+function getDefinitions(content: string): DMN15__tDefinitions {
+  return getMarshaller(content, { upgradeTo: "latest" 
}).parser.parse().definitions;
+}
diff --git a/packages/dmn-language-service/tests/fixtures/nested.dmn 
b/packages/dmn-language-service/tests/fixtures/dmn12/a.dmn
similarity index 100%
rename from packages/dmn-language-service/tests/fixtures/nested.dmn
rename to packages/dmn-language-service/tests/fixtures/dmn12/a.dmn
diff --git a/packages/dmn-language-service/tests/fixtures/recursive.dmn 
b/packages/dmn-language-service/tests/fixtures/dmn12/bImportsA.dmn
similarity index 93%
rename from packages/dmn-language-service/tests/fixtures/recursive.dmn
rename to packages/dmn-language-service/tests/fixtures/dmn12/bImportsA.dmn
index dfeff6aa6ae..db495395959 100644
--- a/packages/dmn-language-service/tests/fixtures/recursive.dmn
+++ b/packages/dmn-language-service/tests/fixtures/dmn12/bImportsA.dmn
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_235F665F-D019-40F9-8350-566FBDE21490";
 id="_30 [...]
   <extensionElements/>
-  <import id="_16BC8F9B-54E6-4ECA-8A63-D593F8B1C607" name="nested" 
namespace="https://kiegroup.org/dmn/_235F665F-D019-40F9-8350-566FBDE21490"; 
locationURI="nested.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <import id="_16BC8F9B-54E6-4ECA-8A63-D593F8B1C607" name="nested" 
namespace="https://kiegroup.org/dmn/_235F665F-D019-40F9-8350-566FBDE21490"; 
locationURI="a.dmn" importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
   <inputData id="_4B366BE7-DEDF-4CF1-AE91-4E10BEC1B450" name="test2">
     <extensionElements/>
     <variable id="_A2324F46-2BE1-4AAD-B7F0-893F5004723E" name="test2"/>
diff --git a/packages/dmn-language-service/tests/fixtures/model.dmn 
b/packages/dmn-language-service/tests/fixtures/dmn12/cImportsB.dmn
similarity index 81%
copy from packages/dmn-language-service/tests/fixtures/model.dmn
copy to packages/dmn-language-service/tests/fixtures/dmn12/cImportsB.dmn
index e879516641f..21c5d20821b 100644
--- a/packages/dmn-language-service/tests/fixtures/model.dmn
+++ b/packages/dmn-language-service/tests/fixtures/dmn12/cImportsB.dmn
@@ -1,8 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_3FC31AA3-F0BA-4A0C-AEA5-7C0B5E4E6DB7"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E";
 xml [...]
   <dmn:extensionElements/>
-  <dmn:import id="_737AB6A6-3393-4760-A13B-9C71CADAB88E" name="recursive" 
namespace="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E"; 
locationURI="recursive.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
-  <dmn:import id="_7719EBFD-6E29-4B96-A28E-F5B903CA3E57" name="nested" 
namespace="https://kiegroup.org/dmn/_235F665F-D019-40F9-8350-566FBDE21490"; 
locationURI="nested.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:import id="_737AB6A6-3393-4760-A13B-9C71CADAB88E" name="re" 
namespace="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E"; 
locationURI="bImportsA.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
   <dmn:inputData id="_7EDE8C61-D569-4270-AD4A-6E5633F92E97" name="test">
     <dmn:extensionElements/>
     <dmn:variable id="_EF760EAA-7663-4000-BB0B-1712F6B19BD5" name="test"/>
diff --git a/packages/dmn-language-service/tests/fixtures/model.dmn 
b/packages/dmn-language-service/tests/fixtures/dmn12/dImportsAB.dmn
similarity index 83%
copy from packages/dmn-language-service/tests/fixtures/model.dmn
copy to packages/dmn-language-service/tests/fixtures/dmn12/dImportsAB.dmn
index e879516641f..f5fe8f92aa3 100644
--- a/packages/dmn-language-service/tests/fixtures/model.dmn
+++ b/packages/dmn-language-service/tests/fixtures/dmn12/dImportsAB.dmn
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_3FC31AA3-F0BA-4A0C-AEA5-7C0B5E4E6DB7"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E";
 xml [...]
   <dmn:extensionElements/>
-  <dmn:import id="_737AB6A6-3393-4760-A13B-9C71CADAB88E" name="recursive" 
namespace="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E"; 
locationURI="recursive.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
-  <dmn:import id="_7719EBFD-6E29-4B96-A28E-F5B903CA3E57" name="nested" 
namespace="https://kiegroup.org/dmn/_235F665F-D019-40F9-8350-566FBDE21490"; 
locationURI="nested.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:import id="_737AB6A6-3393-4760-A13B-9C71CADAB88E" name="re" 
namespace="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E"; 
locationURI="a.dmn" importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:import id="_7719EBFD-6E29-4B96-A28E-F5B903CA3E57" name="ne" 
namespace="https://kiegroup.org/dmn/_235F665F-D019-40F9-8350-566FBDE21490"; 
locationURI="bImportsA.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
   <dmn:inputData id="_7EDE8C61-D569-4270-AD4A-6E5633F92E97" name="test">
     <dmn:extensionElements/>
     <dmn:variable id="_EF760EAA-7663-4000-BB0B-1712F6B19BD5" name="test"/>
diff --git a/packages/dmn-language-service/tests/fixtures/model.dmn 
b/packages/dmn-language-service/tests/fixtures/dmn12/eImportsXB.dmn
similarity index 83%
copy from packages/dmn-language-service/tests/fixtures/model.dmn
copy to packages/dmn-language-service/tests/fixtures/dmn12/eImportsXB.dmn
index e879516641f..099a00c1669 100644
--- a/packages/dmn-language-service/tests/fixtures/model.dmn
+++ b/packages/dmn-language-service/tests/fixtures/dmn12/eImportsXB.dmn
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_3FC31AA3-F0BA-4A0C-AEA5-7C0B5E4E6DB7"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E";
 xml [...]
   <dmn:extensionElements/>
-  <dmn:import id="_737AB6A6-3393-4760-A13B-9C71CADAB88E" name="recursive" 
namespace="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E"; 
locationURI="recursive.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
-  <dmn:import id="_7719EBFD-6E29-4B96-A28E-F5B903CA3E57" name="nested" 
namespace="https://kiegroup.org/dmn/_235F665F-D019-40F9-8350-566FBDE21490"; 
locationURI="nested.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:import id="_737AB6A6-3393-4760-A13B-9C71CADAB88E" name="re" 
namespace="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E"; 
locationURI="bImportsA.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:import id="_7719EBFD-6E29-4B96-A28E-F5B903CA3E57" name="ne" 
namespace="https://kiegroup.org/dmn/_235F665F-D019-40F9-8350-566FBDE21490"; 
locationURI="xImportsY.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
   <dmn:inputData id="_7EDE8C61-D569-4270-AD4A-6E5633F92E97" name="test">
     <dmn:extensionElements/>
     <dmn:variable id="_EF760EAA-7663-4000-BB0B-1712F6B19BD5" name="test"/>
diff --git a/packages/dmn-language-service/tests/fixtures/model.dmn 
b/packages/dmn-language-service/tests/fixtures/dmn12/xImportsY.dmn
similarity index 81%
copy from packages/dmn-language-service/tests/fixtures/model.dmn
copy to packages/dmn-language-service/tests/fixtures/dmn12/xImportsY.dmn
index e879516641f..a10f7cf81a9 100644
--- a/packages/dmn-language-service/tests/fixtures/model.dmn
+++ b/packages/dmn-language-service/tests/fixtures/dmn12/xImportsY.dmn
@@ -1,8 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_3FC31AA3-F0BA-4A0C-AEA5-7C0B5E4E6DB7"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E";
 xml [...]
   <dmn:extensionElements/>
-  <dmn:import id="_737AB6A6-3393-4760-A13B-9C71CADAB88E" name="recursive" 
namespace="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E"; 
locationURI="recursive.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
-  <dmn:import id="_7719EBFD-6E29-4B96-A28E-F5B903CA3E57" name="nested" 
namespace="https://kiegroup.org/dmn/_235F665F-D019-40F9-8350-566FBDE21490"; 
locationURI="nested.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:import id="_737AB6A6-3393-4760-A13B-9C71CADAB88E" name="re" 
namespace="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E"; 
locationURI="y.dmn" importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
   <dmn:inputData id="_7EDE8C61-D569-4270-AD4A-6E5633F92E97" name="test">
     <dmn:extensionElements/>
     <dmn:variable id="_EF760EAA-7663-4000-BB0B-1712F6B19BD5" name="test"/>
diff --git a/packages/dmn-language-service/tests/fixtures/model.dmn 
b/packages/dmn-language-service/tests/fixtures/dmn12/y.dmn
similarity index 78%
rename from packages/dmn-language-service/tests/fixtures/model.dmn
rename to packages/dmn-language-service/tests/fixtures/dmn12/y.dmn
index e879516641f..803468f667e 100644
--- a/packages/dmn-language-service/tests/fixtures/model.dmn
+++ b/packages/dmn-language-service/tests/fixtures/dmn12/y.dmn
@@ -1,8 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_3FC31AA3-F0BA-4A0C-AEA5-7C0B5E4E6DB7"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E";
 xml [...]
   <dmn:extensionElements/>
-  <dmn:import id="_737AB6A6-3393-4760-A13B-9C71CADAB88E" name="recursive" 
namespace="https://kiegroup.org/dmn/_92BDAC3A-440F-490F-A673-9F5E18007A7E"; 
locationURI="recursive.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
-  <dmn:import id="_7719EBFD-6E29-4B96-A28E-F5B903CA3E57" name="nested" 
namespace="https://kiegroup.org/dmn/_235F665F-D019-40F9-8350-566FBDE21490"; 
locationURI="nested.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
   <dmn:inputData id="_7EDE8C61-D569-4270-AD4A-6E5633F92E97" name="test">
     <dmn:extensionElements/>
     <dmn:variable id="_EF760EAA-7663-4000-BB0B-1712F6B19BD5" name="test"/>
diff --git 
a/packages/dmn-language-service/tests/fixtures/dmn15/aImportsDmn12C.dmn 
b/packages/dmn-language-service/tests/fixtures/dmn15/aImportsDmn12C.dmn
new file mode 100644
index 00000000000..a151592d6fc
--- /dev/null
+++ b/packages/dmn-language-service/tests/fixtures/dmn15/aImportsDmn12C.dmn
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<definitions xmlns="https://www.omg.org/spec/DMN/20230324/MODEL/"; 
xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:kie="https://kie.org/dmn/extensions/1.0"; 
expressionLanguage="https://www.omg.org/spec/DMN/20230324/FEEL/"; 
namespace="https://kie.org/dmn/_6A074663-D6AF-4CF3-8422-832E13B293FF"; 
id="_DC83BCEA-14D5-4486-B53A-23439C02D0CB" 
name="DMN_17FA2016-8A66-4AC5-BD09-C4A7 [...]
+  <inputData name="MyInput" id="_B28F0927-E9E6-4FFF-8D6D-4301DAE7613A">
+    <variable name="MyInput" id="_72C3430E-A48B-4012-891C-14B479256DCF" 
typeRef="&lt;Undefined&gt;" />
+  </inputData>
+  <decision name="MyDecision" id="_558A8034-134C-470A-990D-7638A295FA47">
+    <informationRequirement id="_1EF5FE37-D4B6-4F2F-A949-81CC65FA9D81">
+      <requiredInput href="#_B28F0927-E9E6-4FFF-8D6D-4301DAE7613A" />
+    </informationRequirement>
+    <variable id="_8D1BBCBE-8734-45DE-94C9-7DB554D39662" 
typeRef="&lt;Undefined&gt;" name="MyDecision" />
+    <literalExpression id="_D787D7AC-84D4-4069-B3B1-5E927BCAAB3E" 
label="MyDecision" typeRef="&lt;Undefined&gt;">
+      <text>MyInput</text>
+    </literalExpression>
+  </decision>
+  <dmndi:DMNDI>
+    <dmndi:DMNDiagram name="Default DRD" useAlternativeInputDataShape="false">
+      <dmndi:DMNShape id="_AFD7506D-8882-4856-A0CD-468DE1DB6953" 
dmnElementRef="_B28F0927-E9E6-4FFF-8D6D-4301DAE7613A" isCollapsed="false" 
isListedInputData="false">
+        <dc:Bounds x="180" y="380" width="160" height="80" />
+        <dmndi:DMNStyle>
+          <dmndi:FontColor blue="0" green="0" red="0" />
+          <dmndi:StrokeColor blue="0" green="0" red="0" />
+          <dmndi:FillColor blue="255" green="255" red="255" />
+        </dmndi:DMNStyle>
+      </dmndi:DMNShape>
+      <dmndi:DMNShape id="_36571799-0640-4468-AB13-6E0BDC3EEA07" 
dmnElementRef="_558A8034-134C-470A-990D-7638A295FA47" isCollapsed="false" 
isListedInputData="false">
+        <dc:Bounds x="180" y="180" width="160" height="80" />
+        <dmndi:DMNStyle>
+          <dmndi:FontColor blue="0" green="0" red="0" />
+          <dmndi:StrokeColor blue="0" green="0" red="0" />
+          <dmndi:FillColor blue="255" green="255" red="255" />
+        </dmndi:DMNStyle>
+      </dmndi:DMNShape>
+      <dmndi:DMNEdge id="_EAEB3851-7035-4292-9903-453916B43606-AUTO-TARGET" 
dmnElementRef="_1EF5FE37-D4B6-4F2F-A949-81CC65FA9D81" 
sourceElement="_AFD7506D-8882-4856-A0CD-468DE1DB6953" 
targetElement="_36571799-0640-4468-AB13-6E0BDC3EEA07">
+        <di:waypoint x="260" y="420" />
+        <di:waypoint x="260" y="220" />
+      </dmndi:DMNEdge>
+      <di:extension>
+        <kie:ComponentsWidthsExtension>
+          <kie:ComponentWidths 
dmnElementRef="_D787D7AC-84D4-4069-B3B1-5E927BCAAB3E">
+            <kie:width>190</kie:width>
+          </kie:ComponentWidths>
+        </kie:ComponentsWidthsExtension>
+      </di:extension>
+    </dmndi:DMNDiagram>
+  </dmndi:DMNDI>
+  <import id="_512DB531-51F7-4EA2-B4BA-F2E769660B64" name="mo" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
namespace="https://kiegroup.org/dmn/_3FC31AA3-F0BA-4A0C-AEA5-7C0B5E4E6DB7"; 
locationURI="../dmn12/cImportsB.dmn" />
+</definitions>
diff --git 
a/packages/dmn-language-service/tests/fixtures/dmn15/bImportsDmn12D.dmn 
b/packages/dmn-language-service/tests/fixtures/dmn15/bImportsDmn12D.dmn
new file mode 100644
index 00000000000..9840903ce14
--- /dev/null
+++ b/packages/dmn-language-service/tests/fixtures/dmn15/bImportsDmn12D.dmn
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<definitions xmlns="https://www.omg.org/spec/DMN/20230324/MODEL/"; 
xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:kie="https://kie.org/dmn/extensions/1.0"; 
expressionLanguage="https://www.omg.org/spec/DMN/20230324/FEEL/"; 
namespace="https://kie.org/dmn/_6A074663-D6AF-4CF3-8422-832E13B293FF"; 
id="_DC83BCEA-14D5-4486-B53A-23439C02D0CB" 
name="DMN_17FA2016-8A66-4AC5-BD09-C4A7 [...]
+  <inputData name="MyInput" id="_B28F0927-E9E6-4FFF-8D6D-4301DAE7613A">
+    <variable name="MyInput" id="_72C3430E-A48B-4012-891C-14B479256DCF" 
typeRef="&lt;Undefined&gt;" />
+  </inputData>
+  <decision name="MyDecision" id="_558A8034-134C-470A-990D-7638A295FA47">
+    <informationRequirement id="_1EF5FE37-D4B6-4F2F-A949-81CC65FA9D81">
+      <requiredInput href="#_B28F0927-E9E6-4FFF-8D6D-4301DAE7613A" />
+    </informationRequirement>
+    <variable id="_8D1BBCBE-8734-45DE-94C9-7DB554D39662" 
typeRef="&lt;Undefined&gt;" name="MyDecision" />
+    <literalExpression id="_D787D7AC-84D4-4069-B3B1-5E927BCAAB3E" 
label="MyDecision" typeRef="&lt;Undefined&gt;">
+      <text>MyInput</text>
+    </literalExpression>
+  </decision>
+  <dmndi:DMNDI>
+    <dmndi:DMNDiagram name="Default DRD" useAlternativeInputDataShape="false">
+      <dmndi:DMNShape id="_AFD7506D-8882-4856-A0CD-468DE1DB6953" 
dmnElementRef="_B28F0927-E9E6-4FFF-8D6D-4301DAE7613A" isCollapsed="false" 
isListedInputData="false">
+        <dc:Bounds x="180" y="380" width="160" height="80" />
+        <dmndi:DMNStyle>
+          <dmndi:FontColor blue="0" green="0" red="0" />
+          <dmndi:StrokeColor blue="0" green="0" red="0" />
+          <dmndi:FillColor blue="255" green="255" red="255" />
+        </dmndi:DMNStyle>
+      </dmndi:DMNShape>
+      <dmndi:DMNShape id="_36571799-0640-4468-AB13-6E0BDC3EEA07" 
dmnElementRef="_558A8034-134C-470A-990D-7638A295FA47" isCollapsed="false" 
isListedInputData="false">
+        <dc:Bounds x="180" y="180" width="160" height="80" />
+        <dmndi:DMNStyle>
+          <dmndi:FontColor blue="0" green="0" red="0" />
+          <dmndi:StrokeColor blue="0" green="0" red="0" />
+          <dmndi:FillColor blue="255" green="255" red="255" />
+        </dmndi:DMNStyle>
+      </dmndi:DMNShape>
+      <dmndi:DMNEdge id="_EAEB3851-7035-4292-9903-453916B43606-AUTO-TARGET" 
dmnElementRef="_1EF5FE37-D4B6-4F2F-A949-81CC65FA9D81" 
sourceElement="_AFD7506D-8882-4856-A0CD-468DE1DB6953" 
targetElement="_36571799-0640-4468-AB13-6E0BDC3EEA07">
+        <di:waypoint x="260" y="420" />
+        <di:waypoint x="260" y="220" />
+      </dmndi:DMNEdge>
+      <di:extension>
+        <kie:ComponentsWidthsExtension>
+          <kie:ComponentWidths 
dmnElementRef="_D787D7AC-84D4-4069-B3B1-5E927BCAAB3E">
+            <kie:width>190</kie:width>
+          </kie:ComponentWidths>
+        </kie:ComponentsWidthsExtension>
+      </di:extension>
+    </dmndi:DMNDiagram>
+  </dmndi:DMNDI>
+  <import id="_512DB531-51F7-4EA2-B4BA-F2E769660B64" name="mo" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
namespace="https://kiegroup.org/dmn/_3FC31AA3-F0BA-4A0C-AEA5-7C0B5E4E6DB7"; 
locationURI="../dmn12/dImportsAB.dmn" />
+</definitions>
diff --git 
a/packages/dmn-language-service/tests/fixtures/immediateRecursion/aImportsB.dmn 
b/packages/dmn-language-service/tests/fixtures/immediateRecursion/aImportsB.dmn
new file mode 100644
index 00000000000..d5022373fcb
--- /dev/null
+++ 
b/packages/dmn-language-service/tests/fixtures/immediateRecursion/aImportsB.dmn
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_2EFA2297-868F-4243-96C8-DCCD82F25F30"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_FF389036-1B31-408E-9721-3704AE54EBF6";
 id= [...]
+  <dmn:extensionElements/>
+  <dmn:import id="_14E75CBB-A857-4B73-8939-AC8A8B295DB1" name="e2" 
namespace="https://kiegroup.org/dmn/_FF389036-1B31-408E-9721-3704AE54EBF6"; 
locationURI="bImportsA.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:inputData id="_DE0481C1-EEB6-4E2E-97E5-ECC412C7CE0F" name="input">
+    <dmn:extensionElements/>
+    <dmn:variable id="_D83E2D1E-3939-4299-9F45-9C0B0E612E54" name="input"/>
+  </dmn:inputData>
+  <dmndi:DMNDI>
+    <dmndi:DMNDiagram id="_D38E0ED4-1DDF-42BA-B6A1-947B50FCC588" name="DRG">
+      <di:extension>
+        <kie:ComponentsWidthsExtension/>
+      </di:extension>
+      <dmndi:DMNShape id="dmnshape-drg-_DE0481C1-EEB6-4E2E-97E5-ECC412C7CE0F" 
dmnElementRef="_DE0481C1-EEB6-4E2E-97E5-ECC412C7CE0F" isCollapsed="false">
+        <dmndi:DMNStyle>
+          <dmndi:FillColor red="255" green="255" blue="255"/>
+          <dmndi:StrokeColor red="0" green="0" blue="0"/>
+          <dmndi:FontColor red="0" green="0" blue="0"/>
+        </dmndi:DMNStyle>
+        <dc:Bounds x="388" y="212" width="100" height="50"/>
+        <dmndi:DMNLabel/>
+      </dmndi:DMNShape>
+    </dmndi:DMNDiagram>
+  </dmndi:DMNDI>
+</dmn:definitions>
\ No newline at end of file
diff --git 
a/packages/dmn-language-service/tests/fixtures/immediateRecursion/bImportsA.dmn 
b/packages/dmn-language-service/tests/fixtures/immediateRecursion/bImportsA.dmn
new file mode 100644
index 00000000000..ff268927e0f
--- /dev/null
+++ 
b/packages/dmn-language-service/tests/fixtures/immediateRecursion/bImportsA.dmn
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_FF389036-1B31-408E-9721-3704AE54EBF6"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_2EFA2297-868F-4243-96C8-DCCD82F25F30";
 id= [...]
+  <dmn:extensionElements/>
+  <dmn:import id="_6B7F7D2C-09CA-4D3C-AD8D-81127ECA074B" name="e1" 
namespace="https://kiegroup.org/dmn/_2EFA2297-868F-4243-96C8-DCCD82F25F30"; 
locationURI="aImportsB.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:inputData id="_EE9108C1-4D95-468A-99CC-8570853FA17D" name="input2">
+    <dmn:extensionElements/>
+    <dmn:variable id="_5EAB7054-03D0-42DA-B8E1-98558AAFE588" name="input2"/>
+  </dmn:inputData>
+  <dmndi:DMNDI>
+    <dmndi:DMNDiagram id="_EFF39877-49E7-4CED-8EEF-E05613D851DB" name="DRG">
+      <di:extension>
+        <kie:ComponentsWidthsExtension/>
+      </di:extension>
+      <dmndi:DMNShape id="dmnshape-drg-_EE9108C1-4D95-468A-99CC-8570853FA17D" 
dmnElementRef="_EE9108C1-4D95-468A-99CC-8570853FA17D" isCollapsed="false">
+        <dmndi:DMNStyle>
+          <dmndi:FillColor red="255" green="255" blue="255"/>
+          <dmndi:StrokeColor red="0" green="0" blue="0"/>
+          <dmndi:FontColor red="0" green="0" blue="0"/>
+        </dmndi:DMNStyle>
+        <dc:Bounds x="479" y="223" width="100" height="50"/>
+        <dmndi:DMNLabel/>
+      </dmndi:DMNShape>
+    </dmndi:DMNDiagram>
+  </dmndi:DMNDI>
+</dmn:definitions>
\ No newline at end of file
diff --git 
a/packages/dmn-language-service/tests/fixtures/threeLevelRecursion/aImportsB.dmn
 
b/packages/dmn-language-service/tests/fixtures/threeLevelRecursion/aImportsB.dmn
new file mode 100644
index 00000000000..7d1a250a919
--- /dev/null
+++ 
b/packages/dmn-language-service/tests/fixtures/threeLevelRecursion/aImportsB.dmn
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_FF389036-1B31-408E-9721-3704AE54EBF6"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_2EFA2297-868F-4243-96C8-DCCD82F25F30";
 id= [...]
+  <dmn:extensionElements/>
+  <dmn:import id="_6B7F7D2C-09CA-4D3C-AD8D-81127ECA074B" name="e1" 
namespace="https://kiegroup.org/dmn/_2EFA2297-868F-4243-96C8-DCCD82F25F30"; 
locationURI="bImportsC.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:inputData id="_EE9108C1-4D95-468A-99CC-8570853FA17D" name="input2">
+    <dmn:extensionElements/>
+    <dmn:variable id="_5EAB7054-03D0-42DA-B8E1-98558AAFE588" name="input2"/>
+  </dmn:inputData>
+  <dmndi:DMNDI>
+    <dmndi:DMNDiagram id="_EFF39877-49E7-4CED-8EEF-E05613D851DB" name="DRG">
+      <di:extension>
+        <kie:ComponentsWidthsExtension/>
+      </di:extension>
+      <dmndi:DMNShape id="dmnshape-drg-_EE9108C1-4D95-468A-99CC-8570853FA17D" 
dmnElementRef="_EE9108C1-4D95-468A-99CC-8570853FA17D" isCollapsed="false">
+        <dmndi:DMNStyle>
+          <dmndi:FillColor red="255" green="255" blue="255"/>
+          <dmndi:StrokeColor red="0" green="0" blue="0"/>
+          <dmndi:FontColor red="0" green="0" blue="0"/>
+        </dmndi:DMNStyle>
+        <dc:Bounds x="479" y="223" width="100" height="50"/>
+        <dmndi:DMNLabel/>
+      </dmndi:DMNShape>
+    </dmndi:DMNDiagram>
+  </dmndi:DMNDI>
+</dmn:definitions>
\ No newline at end of file
diff --git 
a/packages/dmn-language-service/tests/fixtures/threeLevelRecursion/bImportsC.dmn
 
b/packages/dmn-language-service/tests/fixtures/threeLevelRecursion/bImportsC.dmn
new file mode 100644
index 00000000000..818e2baad5d
--- /dev/null
+++ 
b/packages/dmn-language-service/tests/fixtures/threeLevelRecursion/bImportsC.dmn
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_FF389036-1B31-408E-9721-3704AE54EBF6"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_2EFA2297-868F-4243-96C8-DCCD82F25F30";
 id= [...]
+  <dmn:extensionElements/>
+  <dmn:import id="_6B7F7D2C-09CA-4D3C-AD8D-81127ECA074B" name="e1" 
namespace="https://kiegroup.org/dmn/_2EFA2297-868F-4243-96C8-DCCD82F25F30"; 
locationURI="cImportsA.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:inputData id="_EE9108C1-4D95-468A-99CC-8570853FA17D" name="input2">
+    <dmn:extensionElements/>
+    <dmn:variable id="_5EAB7054-03D0-42DA-B8E1-98558AAFE588" name="input2"/>
+  </dmn:inputData>
+  <dmndi:DMNDI>
+    <dmndi:DMNDiagram id="_EFF39877-49E7-4CED-8EEF-E05613D851DB" name="DRG">
+      <di:extension>
+        <kie:ComponentsWidthsExtension/>
+      </di:extension>
+      <dmndi:DMNShape id="dmnshape-drg-_EE9108C1-4D95-468A-99CC-8570853FA17D" 
dmnElementRef="_EE9108C1-4D95-468A-99CC-8570853FA17D" isCollapsed="false">
+        <dmndi:DMNStyle>
+          <dmndi:FillColor red="255" green="255" blue="255"/>
+          <dmndi:StrokeColor red="0" green="0" blue="0"/>
+          <dmndi:FontColor red="0" green="0" blue="0"/>
+        </dmndi:DMNStyle>
+        <dc:Bounds x="479" y="223" width="100" height="50"/>
+        <dmndi:DMNLabel/>
+      </dmndi:DMNShape>
+    </dmndi:DMNDiagram>
+  </dmndi:DMNDI>
+</dmn:definitions>
\ No newline at end of file
diff --git 
a/packages/dmn-language-service/tests/fixtures/threeLevelRecursion/cImportsA.dmn
 
b/packages/dmn-language-service/tests/fixtures/threeLevelRecursion/cImportsA.dmn
new file mode 100644
index 00000000000..ff268927e0f
--- /dev/null
+++ 
b/packages/dmn-language-service/tests/fixtures/threeLevelRecursion/cImportsA.dmn
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"; 
xmlns="https://kiegroup.org/dmn/_FF389036-1B31-408E-9721-3704AE54EBF6"; 
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"; 
xmlns:kie="http://www.drools.org/kie/dmn/1.2"; 
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:included1="https://kiegroup.org/dmn/_2EFA2297-868F-4243-96C8-DCCD82F25F30";
 id= [...]
+  <dmn:extensionElements/>
+  <dmn:import id="_6B7F7D2C-09CA-4D3C-AD8D-81127ECA074B" name="e1" 
namespace="https://kiegroup.org/dmn/_2EFA2297-868F-4243-96C8-DCCD82F25F30"; 
locationURI="aImportsB.dmn" 
importType="http://www.omg.org/spec/DMN/20180521/MODEL/"/>
+  <dmn:inputData id="_EE9108C1-4D95-468A-99CC-8570853FA17D" name="input2">
+    <dmn:extensionElements/>
+    <dmn:variable id="_5EAB7054-03D0-42DA-B8E1-98558AAFE588" name="input2"/>
+  </dmn:inputData>
+  <dmndi:DMNDI>
+    <dmndi:DMNDiagram id="_EFF39877-49E7-4CED-8EEF-E05613D851DB" name="DRG">
+      <di:extension>
+        <kie:ComponentsWidthsExtension/>
+      </di:extension>
+      <dmndi:DMNShape id="dmnshape-drg-_EE9108C1-4D95-468A-99CC-8570853FA17D" 
dmnElementRef="_EE9108C1-4D95-468A-99CC-8570853FA17D" isCollapsed="false">
+        <dmndi:DMNStyle>
+          <dmndi:FillColor red="255" green="255" blue="255"/>
+          <dmndi:StrokeColor red="0" green="0" blue="0"/>
+          <dmndi:FontColor red="0" green="0" blue="0"/>
+        </dmndi:DMNStyle>
+        <dc:Bounds x="479" y="223" width="100" height="50"/>
+        <dmndi:DMNLabel/>
+      </dmndi:DMNShape>
+    </dmndi:DMNDiagram>
+  </dmndi:DMNDI>
+</dmn:definitions>
\ No newline at end of file
diff --git a/packages/dmn-language-service/tests/fs/fixtures.ts 
b/packages/dmn-language-service/tests/fs/fixtures.ts
new file mode 100644
index 00000000000..b2f265c5fb9
--- /dev/null
+++ b/packages/dmn-language-service/tests/fs/fixtures.ts
@@ -0,0 +1,94 @@
+/*
+ * 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 { getModelXmlForTestFixtures } from "./getModelXml";
+
+export const dmn12A = () => {
+  return getModelXmlForTestFixtures({ 
normalizedPosixPathRelativeToTheWorkspaceRoot: "fixtures/dmn12/a.dmn" });
+};
+
+export const dmn12B = () => {
+  return getModelXmlForTestFixtures({ 
normalizedPosixPathRelativeToTheWorkspaceRoot: "fixtures/dmn12/bImportsA.dmn" 
});
+};
+
+export const dmn12C = () => {
+  return getModelXmlForTestFixtures({ 
normalizedPosixPathRelativeToTheWorkspaceRoot: "fixtures/dmn12/cImportsB.dmn" 
});
+};
+
+export const dmn12D = () => {
+  return getModelXmlForTestFixtures({ 
normalizedPosixPathRelativeToTheWorkspaceRoot: "fixtures/dmn12/dImportsAB.dmn" 
});
+};
+
+export const dmn12E = () => {
+  return getModelXmlForTestFixtures({ 
normalizedPosixPathRelativeToTheWorkspaceRoot: "fixtures/dmn12/eImportsXB.dmn" 
});
+};
+
+export const dmn12X = () => {
+  return getModelXmlForTestFixtures({ 
normalizedPosixPathRelativeToTheWorkspaceRoot: "fixtures/dmn12/xImportsY.dmn" 
});
+};
+
+export const dmn12Y = () => {
+  return getModelXmlForTestFixtures({ 
normalizedPosixPathRelativeToTheWorkspaceRoot: "fixtures/dmn12/y.dmn" });
+};
+
+export const dmn15A = () => {
+  return getModelXmlForTestFixtures({
+    normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/aImportsDmn12C.dmn",
+  });
+};
+
+export const dmn15B = () => {
+  return getModelXmlForTestFixtures({
+    normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/dmn15/bImportsDmn12D.dmn",
+  });
+};
+
+export const immediateRecursionA = () => {
+  return getModelXmlForTestFixtures({
+    normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/immediateRecursion/aImportsB.dmn",
+  });
+};
+
+export const immediateRecursionB = () => {
+  return getModelXmlForTestFixtures({
+    normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/immediateRecursion/bImportsA.dmn",
+  });
+};
+
+export const threeLevelRecursionA = () => {
+  return getModelXmlForTestFixtures({
+    normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/threeLevelRecursion/aImportsB.dmn",
+  });
+};
+
+export const threeLevelRecursionB = () => {
+  return getModelXmlForTestFixtures({
+    normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/threeLevelRecursion/bImportsC.dmn",
+  });
+};
+
+export const threeLevelRecursionC = () => {
+  return getModelXmlForTestFixtures({
+    normalizedPosixPathRelativeToTheWorkspaceRoot: 
"fixtures/threeLevelRecursion/cImportsA.dmn",
+  });
+};
+
+export const decisions = () => {
+  return getModelXmlForTestFixtures({ 
normalizedPosixPathRelativeToTheWorkspaceRoot: "fixtures/decisions.dmn" });
+};
diff --git a/packages/dmn-language-service/tests/fs/getModelXml.ts 
b/packages/dmn-language-service/tests/fs/getModelXml.ts
new file mode 100644
index 00000000000..a839ad75399
--- /dev/null
+++ b/packages/dmn-language-service/tests/fs/getModelXml.ts
@@ -0,0 +1,34 @@
+/*
+ * 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 { readFileSync } from "fs";
+import * as __path from "path";
+
+export function getModelXmlForTestFixtures(args: { 
normalizedPosixPathRelativeToTheWorkspaceRoot: string }): string {
+  return readFileSync(
+    __path.resolve(__dirname, "..", 
args.normalizedPosixPathRelativeToTheWorkspaceRoot.split("/").join(__path.sep)),
 // TODO: Use `toFsPath` from `@kie-tools-core/operating-system.
+    "utf8"
+  );
+}
+
+export function asyncGetModelXmlForTestFixtures(args: {
+  normalizedPosixPathRelativeToTheWorkspaceRoot: string;
+}): Promise<string> {
+  return Promise.resolve(getModelXmlForTestFixtures(args));
+}
diff --git a/packages/dmn-language-service/tests/getDmnDocumentData.test.ts 
b/packages/dmn-language-service/tests/getDmnDocumentData.test.ts
new file mode 100644
index 00000000000..abf28a5a9fe
--- /dev/null
+++ b/packages/dmn-language-service/tests/getDmnDocumentData.test.ts
@@ -0,0 +1,35 @@
+/*
+ * 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 { DmnDocumentData, DmnLanguageService } from "../src";
+import { DmnDecision } from "../src/DmnDecision";
+import { decisions } from "./fs/fixtures";
+import { asyncGetModelXmlForTestFixtures } from "./fs/getModelXml";
+
+it("get decisions", () => {
+  const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+
+  expect(dmnLs.getDmnDocumentData(decisions())).toEqual(
+    new 
DmnDocumentData("https://kiegroup.org/dmn/_57B8BED3-0077-4154-8435-30E57EA6F02E";,
 "My Model Name", [
+      new DmnDecision("Decision-1"),
+      new DmnDecision("Decision-2"),
+      new DmnDecision("Decision-3"),
+    ])
+  );
+});
diff --git a/packages/dmn-language-service/tests/getSpecVersion.test.ts 
b/packages/dmn-language-service/tests/getSpecVersion.test.ts
new file mode 100644
index 00000000000..a013fe90d17
--- /dev/null
+++ b/packages/dmn-language-service/tests/getSpecVersion.test.ts
@@ -0,0 +1,42 @@
+/*
+ * 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 { DmnLanguageService } from "../src";
+import { dmn12D, dmn15B } from "./fs/fixtures";
+import { asyncGetModelXmlForTestFixtures } from "./fs/getModelXml";
+
+it("empty", () => {
+  const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+  expect(dmnLs.getSpecVersion("")).toEqual(undefined);
+});
+
+it("invalid", () => {
+  const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+  expect(dmnLs.getSpecVersion("aa")).toEqual(undefined);
+});
+
+it("1.2", () => {
+  const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+  expect(dmnLs.getSpecVersion(dmn12D())).toEqual("1.2");
+});
+
+it("1.5", () => {
+  const dmnLs = new DmnLanguageService({ getModelXml: 
asyncGetModelXmlForTestFixtures });
+  expect(dmnLs.getSpecVersion(dmn15B())).toEqual("1.5");
+});
diff --git a/packages/dmn-language-service/tests/index.test.ts 
b/packages/dmn-language-service/tests/index.test.ts
deleted file mode 100644
index 901988777f5..00000000000
--- a/packages/dmn-language-service/tests/index.test.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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 { DmnDocumentData, DmnLanguageService } from "../src";
-import { readFile } from "fs/promises";
-import { readFileSync } from "fs";
-import { resolve } from "path";
-import { DmnDecision } from "../src/DmnDecision";
-
-const tests = [
-  { pathRelativeToWorkspaceRoot: "./fixtures/model.dmn", expected: 
["fixtures/recursive.dmn", "fixtures/nested.dmn"] },
-  { pathRelativeToWorkspaceRoot: "./fixtures/recursive.dmn", expected: 
["fixtures/nested.dmn"] },
-  { pathRelativeToWorkspaceRoot: "./fixtures/nested.dmn", expected: [] },
-];
-
-describe("DmnLanguageService", () => {
-  const service = new DmnLanguageService({
-    getDmnImportedModelResource: () =>
-      new Promise((res) =>
-        res({
-          pathRelativeToWorkspaceRoot: "",
-          content: "",
-        })
-      ),
-  });
-
-  it("getImportedModelPathsRelativeToWorkspaceRoot - empty string", () => {
-    expect(
-      service.getImportedModelPathsRelativeToWorkspaceRoot([{ content: "", 
pathRelativeToWorkspaceRoot: "" }])
-    ).toEqual([]);
-  });
-
-  it("getImportedModelPathsRelativeToWorkspaceRoot - single file", () => {
-    tests.forEach(({ pathRelativeToWorkspaceRoot, expected }) => {
-      const content = readFileSync(resolve(__dirname, 
pathRelativeToWorkspaceRoot), "utf8");
-      expect(service.getImportedModelPathsRelativeToWorkspaceRoot({ content, 
pathRelativeToWorkspaceRoot })).toEqual(
-        expected
-      );
-    });
-  });
-
-  it("getImportedModelPathsRelativeToWorkspaceRoot - multiple files", async () 
=> {
-    const importedModelResources = (
-      await Promise.all(
-        tests.map(({ pathRelativeToWorkspaceRoot }) => 
readFile(resolve(__dirname, pathRelativeToWorkspaceRoot)))
-      )
-    ).map((e, i) => ({
-      content: e.toString("utf8"),
-      pathRelativeToWorkspaceRoot: tests[i].pathRelativeToWorkspaceRoot,
-    }));
-
-    
expect(service.getImportedModelPathsRelativeToWorkspaceRoot(importedModelResources)).toEqual(
-      tests.flatMap((e) => e.expected)
-    );
-  });
-
-  it("getAllImportedModelsResources - empty", async () => {
-    const service = new DmnLanguageService({
-      getDmnImportedModelResource: () =>
-        new Promise((res) =>
-          res({
-            pathRelativeToWorkspaceRoot: "",
-            content: "",
-          })
-        ),
-    });
-
-    expect(await service.getAllImportedModelsResources([])).toEqual([]);
-  });
-
-  it("getAllImportedModelsResources - get resources", async () => {
-    const absolutePathOfModelWithNestedIncludedModel = resolve(__dirname, 
"./fixtures/recursive.dmn");
-    const modelContent = 
readFileSync(absolutePathOfModelWithNestedIncludedModel, "utf8");
-
-    const absolutePathOfIncludedModel = resolve(__dirname, 
"./fixtures/nested.dmn");
-    const includedModelContent = readFileSync(absolutePathOfIncludedModel, 
"utf8");
-
-    const expected = {
-      pathRelativeToWorkspaceRoot: absolutePathOfIncludedModel,
-      content: includedModelContent,
-    };
-
-    const service = new DmnLanguageService({ getDmnImportedModelResource: () 
=> new Promise((res) => res(expected)) });
-
-    expect(
-      await service.getAllImportedModelsResources([
-        { content: modelContent, pathRelativeToWorkspaceRoot: 
absolutePathOfModelWithNestedIncludedModel },
-      ])
-    ).toEqual([expected]);
-  });
-
-  it("getDmnDocumentData - get decisions", async () => {
-    const absolutePathOfModelWithNestedIncludedModel = resolve(__dirname, 
"./fixtures/decisions.dmn");
-    const modelContent = 
readFileSync(absolutePathOfModelWithNestedIncludedModel, "utf8");
-
-    const absolutePathOfIncludedModel = resolve(__dirname, 
"./fixtures/nested.dmn");
-    const includedModelContent = readFileSync(absolutePathOfIncludedModel, 
"utf8");
-
-    const expected = {
-      pathRelativeToWorkspaceRoot: absolutePathOfIncludedModel,
-      content: includedModelContent,
-    };
-
-    const dmnDocumentData: DmnDocumentData = new DmnDocumentData(
-      "https://kiegroup.org/dmn/_57B8BED3-0077-4154-8435-30E57EA6F02E";,
-      "My Model Name",
-      [new DmnDecision("Decision-1"), new DmnDecision("Decision-2"), new 
DmnDecision("Decision-3")]
-    );
-    const service = new DmnLanguageService({ getDmnImportedModelResource: () 
=> new Promise((res) => res(expected)) });
-
-    expect(service.getDmnDocumentData(modelContent)).toEqual(dmnDocumentData);
-  });
-});
diff --git a/packages/kie-bc-editors/src/bpmn/envelope/BpmnEditorFactory.ts 
b/packages/kie-bc-editors/src/bpmn/envelope/BpmnEditorFactory.ts
index 0477a0a7261..ebdb3db9179 100644
--- a/packages/kie-bc-editors/src/bpmn/envelope/BpmnEditorFactory.ts
+++ b/packages/kie-bc-editors/src/bpmn/envelope/BpmnEditorFactory.ts
@@ -41,7 +41,7 @@ export class BpmnEditorFactory implements 
EditorFactory<BpmnEditor, BpmnEditorCh
   ): Promise<BpmnEditor> {
     const dmnLs = new DmnLanguageService({
       // currently does not need to implement it since we don't need a way to 
read other Dmn files.
-      getDmnImportedModelResource: () => Promise.resolve({ 
pathRelativeToWorkspaceRoot: "", content: "" }),
+      getModelXml: () => Promise.resolve(""),
     });
 
     const exposedInteropApi: CustomWindow["envelope"] = {
diff --git a/packages/online-editor/src/dmnRunner/DmnRunnerContextProvider.tsx 
b/packages/online-editor/src/dmnRunner/DmnRunnerContextProvider.tsx
index e054000a30b..4d19ff8ae2f 100644
--- a/packages/online-editor/src/dmnRunner/DmnRunnerContextProvider.tsx
+++ b/packages/online-editor/src/dmnRunner/DmnRunnerContextProvider.tsx
@@ -207,31 +207,31 @@ export function DmnRunnerContextProvider(props: 
PropsWithChildren<Props>) {
     }
   }, [prevExtendedServicesStatus, extendedServices.status, 
props.workspaceFile.extension]);
 
-  const extendedServicesModelPayload = useCallback(
-    async (formInputs?: InputRow) => {
+  const extendedServicesModelPayload = useCallback<(formInputs?: InputRow) => 
Promise<ExtendedServicesModelPayload>>(
+    async (formInputs) => {
       const fileContent = await workspaces.getFileContent({
         workspaceId: props.workspaceFile.workspaceId,
         relativePath: props.workspaceFile.relativePath,
       });
 
       const decodedFileContent = decoder.decode(fileContent);
-      const importedModelsResources =
-        (await props.dmnLanguageService?.getAllImportedModelsResources([
-          { content: decodedFileContent, pathRelativeToWorkspaceRoot: 
props.workspaceFile.relativePath },
-        ])) ?? [];
-      const dmnResources = [
-        { content: decodedFileContent, pathRelativeToWorkspaceRoot: 
props.workspaceFile.relativePath },
-        ...importedModelsResources,
-      ].map((resource) => ({
-        URI: resource.pathRelativeToWorkspaceRoot,
-        content: resource.content ?? "",
-      }));
+      const importIndex = await props.dmnLanguageService?.buildImportIndex([
+        {
+          content: decodedFileContent,
+          normalizedPosixPathRelativeToTheWorkspaceRoot: 
props.workspaceFile.relativePath,
+        },
+      ]);
 
       return {
-        mainURI: props.workspaceFile.relativePath,
-        resources: dmnResources,
         context: formInputs,
-      } as ExtendedServicesModelPayload;
+        mainURI: props.workspaceFile.relativePath,
+        resources: [...(importIndex?.models.entries() ?? [])].map(
+          ([normalizedPosixPathRelativeToTheWorkspaceRoot, model]) => ({
+            content: model.xml,
+            URI: normalizedPosixPathRelativeToTheWorkspaceRoot,
+          })
+        ),
+      };
     },
     [props.dmnLanguageService, props.workspaceFile.relativePath, 
props.workspaceFile.workspaceId, workspaces]
   );
diff --git a/packages/online-editor/src/editor/EditorPage.tsx 
b/packages/online-editor/src/editor/EditorPage.tsx
index 58bcc13e31a..9d4628ad572 100644
--- a/packages/online-editor/src/editor/EditorPage.tsx
+++ b/packages/online-editor/src/editor/EditorPage.tsx
@@ -341,24 +341,23 @@ export function EditorPage(props: Props) {
     }
 
     return new DmnLanguageService({
-      getDmnImportedModelResource: async 
(importedModelPathRelativeToWorkspaceRoot: string) => {
+      getModelXml: async (args) => {
         try {
-          const fileContent = await workspaces.getFileContent({
-            workspaceId: workspaceFilePromise.data?.workspaceFile.workspaceId,
-            relativePath: importedModelPathRelativeToWorkspaceRoot,
-          });
-
-          return {
-            content: decoder.decode(fileContent),
-            pathRelativeToWorkspaceRoot: 
importedModelPathRelativeToWorkspaceRoot,
-          };
+          return decoder.decode(
+            await workspaces.getFileContent({
+              workspaceId: 
workspaceFilePromise.data?.workspaceFile.workspaceId,
+              relativePath: args.normalizedPosixPathRelativeToTheWorkspaceRoot,
+            })
+          );
         } catch (err) {
-          console.debug("ERROR: DmnLanguageService.getImportedModel: ", err);
-          return undefined;
+          throw new Error(`
+KIE SANDBOX - DmnLanguageService - getModelXml: Error on getFileContent.
+Tried to open path: ${args.normalizedPosixPathRelativeToTheWorkspaceRoot}
+Error details: ${err}`);
         }
       },
     });
-  }, [workspaces, workspaceFilePromise.data?.workspaceFile]);
+  }, [workspaceFilePromise.data?.workspaceFile, workspaces]);
 
   const onKeyDown = useCallback(
     (ke: React.KeyboardEvent) => {
diff --git a/packages/online-editor/src/editor/Validation.tsx 
b/packages/online-editor/src/editor/Validation.tsx
index d7e2e7a71ce..d8952984394 100644
--- a/packages/online-editor/src/editor/Validation.tsx
+++ b/packages/online-editor/src/editor/Validation.tsx
@@ -20,13 +20,14 @@
 import { Notification } from "@kie-tools-core/notifications/dist/api";
 import { WorkspaceFile } from 
"@kie-tools-core/workspaces-git-fs/dist/context/WorkspacesContext";
 import { decoder } from 
"@kie-tools-core/workspaces-git-fs/dist/encoderdecoder/EncoderDecoder";
-import { useEffect } from "react";
+import { useCallback } from "react";
 import { useOnlineI18n } from "../i18n";
 import { ExtendedServicesModelPayload } from 
"@kie-tools/extended-services-api";
 import { useExtendedServices } from 
"../extendedServices/ExtendedServicesContext";
 import { ExtendedServicesStatus } from 
"../extendedServices/ExtendedServicesStatus";
-import { DmnLanguageService, DmnLanguageServiceImportedModelResources } from 
"@kie-tools/dmn-language-service";
+import { DmnLanguageService } from "@kie-tools/dmn-language-service";
 import { WorkspacesContextType } from 
"@kie-tools-core/workspaces-git-fs/dist/context/WorkspacesContext";
+import { useCancelableEffect } from 
"@kie-tools-core/react-hooks/dist/useCancelableEffect";
 
 export function useFileValidation(
   workspaces: WorkspacesContextType,
@@ -38,116 +39,163 @@ export function useFileValidation(
   const extendedServices = useExtendedServices();
 
   // BPMN validation
-  useEffect(() => {
-    if (!workspaceFile) {
-      return;
-    }
-    if (
-      workspaceFile.extension.toLocaleLowerCase() !== "bpmn" &&
-      workspaceFile.extension.toLocaleLowerCase() !== "bpmn2"
-    ) {
-      return;
-    }
+  useCancelableEffect(
+    useCallback(
+      ({ canceled }) => {
+        if (!workspaceFile) {
+          return;
+        }
+        if (
+          workspaceFile.extension.toLocaleLowerCase() !== "bpmn" &&
+          workspaceFile.extension.toLocaleLowerCase() !== "bpmn2"
+        ) {
+          return;
+        }
 
-    if (extendedServices.status !== ExtendedServicesStatus.RUNNING) {
-      setNotifications(i18n.terms.validation, "", []);
-      return;
-    }
+        if (extendedServices.status !== ExtendedServicesStatus.RUNNING) {
+          setNotifications(i18n.terms.validation, "", []);
+          return;
+        }
 
-    workspaceFile
-      .getFileContents()
-      .then((fileContents) => {
-        const payload: ExtendedServicesModelPayload = {
-          mainURI: workspaceFile.relativePath,
-          resources: [
-            {
-              URI: workspaceFile.relativePath,
-              content: decoder.decode(fileContents),
-            },
-          ],
-        };
+        workspaceFile
+          .getFileContents()
+          .then((fileContents) => {
+            if (canceled.get()) {
+              return;
+            }
+            const payload: ExtendedServicesModelPayload = {
+              mainURI: workspaceFile.relativePath,
+              resources: [
+                {
+                  URI: workspaceFile.relativePath,
+                  content: decoder.decode(fileContents),
+                },
+              ],
+            };
 
-        extendedServices.client.validateBpmn(payload).then((validationResults) 
=> {
-          const notifications: Notification[] = 
validationResults.map((validationResult: any) => ({
-            type: "PROBLEM",
-            path: "",
-            severity: "ERROR",
-            message: validationResult,
-          }));
-          setNotifications(i18n.terms.validation, "", notifications);
-        });
-      })
-      .catch((err) => console.error(err));
-  }, [workspaceFile, setNotifications, extendedServices.status, 
extendedServices.client, i18n.terms.validation]);
+            
extendedServices.client.validateBpmn(payload).then((validationResults) => {
+              if (canceled.get()) {
+                return;
+              }
+              const notifications: Notification[] = 
validationResults.map((validationResult: any) => ({
+                type: "PROBLEM",
+                path: "",
+                severity: "ERROR",
+                message: validationResult,
+              }));
+              setNotifications(i18n.terms.validation, "", notifications);
+            });
+          })
+          .catch((err) => console.error(err));
+      },
+      [workspaceFile, setNotifications, extendedServices.status, 
extendedServices.client, i18n.terms.validation]
+    )
+  );
 
   // DMN validation
-  useEffect(() => {
-    if (!workspaceFile) {
-      return;
-    }
-    if (workspaceFile.extension.toLocaleLowerCase() !== "dmn") {
-      return;
-    }
+  useCancelableEffect(
+    useCallback(
+      ({ canceled }) => {
+        if (!workspaceFile) {
+          return;
+        }
+        if (workspaceFile.extension.toLocaleLowerCase() !== "dmn") {
+          return;
+        }
 
-    if (extendedServices.status !== ExtendedServicesStatus.RUNNING) {
-      setNotifications(i18n.terms.validation, "", []);
-      return;
-    }
+        if (extendedServices.status !== ExtendedServicesStatus.RUNNING) {
+          setNotifications(i18n.terms.validation, "", []);
+          return;
+        }
 
-    workspaces
-      .getFileContent({
-        workspaceId: workspaceFile.workspaceId,
-        relativePath: workspaceFile.relativePath,
-      })
-      .then((fileContent) => {
-        const decodedFileContent = decoder.decode(fileContent);
-        dmnLanguageService
-          ?.getAllImportedModelsResources([
-            { content: decodedFileContent, pathRelativeToWorkspaceRoot: 
workspaceFile.relativePath },
-          ])
-          .then((importedModelsResources: 
DmnLanguageServiceImportedModelResources[]) => {
-            const resources = [
-              { content: decodedFileContent, pathRelativeToWorkspaceRoot: 
workspaceFile.relativePath },
-              ...importedModelsResources,
-            ];
-            const payload: ExtendedServicesModelPayload = {
-              mainURI: workspaceFile.relativePath,
-              resources: resources.map((resource) => ({
-                URI: resource.pathRelativeToWorkspaceRoot,
-                content: resource.content ?? "",
-              })),
-            };
+        workspaces
+          .getFileContent({
+            workspaceId: workspaceFile.workspaceId,
+            relativePath: workspaceFile.relativePath,
+          })
+          .then((fileContent) => {
+            if (canceled.get()) {
+              return;
+            }
+
+            const decodedFileContent = decoder.decode(fileContent);
+            const dmnSpecVersion = 
dmnLanguageService?.getSpecVersion(decodedFileContent);
+            if (!dmnSpecVersion || (dmnSpecVersion !== "1.0" && dmnSpecVersion 
!== "1.1" && dmnSpecVersion !== "1.2")) {
+              setNotifications(i18n.terms.validation, "", [
+                {
+                  type: "ALERT",
+                  path: "",
+                  severity: "WARNING",
+                  message:
+                    "Validation checks are temporarily supported only on DMN 
1.2 or below. For full access to this feature, use the Legacy DMN Editor.",
+                },
+              ]);
+              return;
+            }
 
-            
extendedServices.client.validateDmn(payload).then((validationResults) => {
-              const notifications: Notification[] = 
validationResults.map((validationResult) => {
-                let path = payload.resources.length > 1 ? 
validationResult.path : "";
-                if (
-                  validationResult.severity === "ERROR" &&
-                  validationResult.sourceId === null &&
-                  validationResult.messageType === "REQ_NOT_FOUND"
-                ) {
-                  const nodeId = validationResult.message.split("'")[1] ?? "";
-                  path = dmnLanguageService.getPathFromNodeId(resources, 
nodeId);
+            dmnLanguageService
+              ?.buildImportIndex([
+                {
+                  content: decodedFileContent,
+                  normalizedPosixPathRelativeToTheWorkspaceRoot: 
workspaceFile.relativePath,
+                },
+              ])
+              .then((importIndex) => {
+                if (canceled.get()) {
+                  return;
                 }
-                return {
-                  type: "PROBLEM",
-                  path,
-                  severity: validationResult.severity,
-                  message: `${validationResult.messageType}: 
${validationResult.message}`,
+
+                const allImportedModelResources = 
[...importIndex.models.entries()].map(
+                  ([normalizedPosixPathRelativeToTheWorkspaceRoot, model]) => 
({
+                    content: model.xml,
+                    normalizedPosixPathRelativeToTheWorkspaceRoot,
+                  })
+                );
+
+                const payload: ExtendedServicesModelPayload = {
+                  mainURI: workspaceFile.relativePath,
+                  resources: allImportedModelResources.map((resource) => ({
+                    URI: 
resource.normalizedPosixPathRelativeToTheWorkspaceRoot,
+                    content: resource.content,
+                  })),
                 };
+
+                
extendedServices.client.validateDmn(payload).then((validationResults) => {
+                  if (canceled.get()) {
+                    return;
+                  }
+                  const notifications: Notification[] = 
validationResults.map((validationResult) => {
+                    let path = payload.resources.length > 1 ? 
validationResult.path : "";
+                    if (
+                      validationResult.severity === "ERROR" &&
+                      validationResult.sourceId === null &&
+                      validationResult.messageType === "REQ_NOT_FOUND"
+                    ) {
+                      const nodeId = validationResult.message.split("'")[1] ?? 
"";
+                      path = 
dmnLanguageService.getPathFromNodeId(allImportedModelResources, nodeId);
+                    }
+                    return {
+                      type: "PROBLEM",
+                      path,
+                      severity: validationResult.severity,
+                      message: `${validationResult.messageType}: 
${validationResult.message}`,
+                    };
+                  });
+                  setNotifications(i18n.terms.validation, "", notifications);
+                });
               });
-              setNotifications(i18n.terms.validation, "", notifications);
-            });
-          });
-      })
-      .catch((err) => console.error(err));
-  }, [
-    workspaces,
-    workspaceFile,
-    setNotifications,
-    dmnLanguageService,
-    extendedServices.status,
-    extendedServices.client,
-    i18n.terms.validation,
-  ]);
+          })
+          .catch((err) => console.error(err));
+      },
+      [
+        workspaces,
+        workspaceFile,
+        setNotifications,
+        dmnLanguageService,
+        extendedServices.status,
+        extendedServices.client,
+        i18n.terms.validation,
+      ]
+    )
+  );
 }


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

Reply via email to