tiagobento commented on code in PR #2105:
URL:
https://github.com/apache/incubator-kie-tools/pull/2105#discussion_r1439887109
##########
packages/online-editor/src/editor/EditorPage.tsx:
##########
@@ -331,34 +331,48 @@ export function EditorPage(props: Props) {
setContentErrorAlert.show();
}, [setContentErrorAlert]);
- const dmnLanguageService = useMemo(() => {
- if (!workspaceFilePromise.data?.workspaceFile) {
- return;
- }
+ const [dmnLanguageService, setDmnLanguageService] =
useState<DmnLanguageService | undefined>();
+ useCancelableEffect(
+ useCallback(
+ ({ canceled }) => {
+ if (!workspaceFilePromise.data?.workspaceFile) {
+ return;
+ }
- if (workspaceFilePromise.data?.workspaceFile.extension.toLocaleLowerCase()
!== "dmn") {
- return;
- }
+ if
(workspaceFilePromise.data?.workspaceFile.extension.toLocaleLowerCase() !==
"dmn") {
+ return;
+ }
- return new DmnLanguageService({
- getDmnImportedModelResource: async
(importedModelPathRelativeToWorkspaceRoot: string) => {
- try {
- const fileContent = await workspaces.getFileContent({
- workspaceId: workspaceFilePromise.data?.workspaceFile.workspaceId,
- relativePath: importedModelPathRelativeToWorkspaceRoot,
- });
+
workspaceFilePromise.data?.workspaceFile.getFileContentsAsString().then((modelContent)
=> {
+ if (canceled.get()) {
+ return;
+ }
- return {
- content: decoder.decode(fileContent),
- pathRelativeToWorkspaceRoot:
importedModelPathRelativeToWorkspaceRoot,
- };
- } catch (err) {
- console.debug("ERROR: DmnLanguageService.getImportedModel: ", err);
- return undefined;
- }
+ setDmnLanguageService(
+ new DmnLanguageService({
+ getModelContent: async (args: {
normalizedPosixPathRelativeToWorkspaceRoot: string }) => {
+ try {
+ const fileContent = await workspaces.getFileContent({
+ workspaceId:
workspaceFilePromise.data?.workspaceFile.workspaceId,
+ relativePath:
args.normalizedPosixPathRelativeToWorkspaceRoot,
+ });
+
+ return {
+ content: decoder.decode(fileContent),
+ normalizedPosixPathRelativeToWorkspaceRoot:
args.normalizedPosixPathRelativeToWorkspaceRoot,
+ };
+ } catch (err) {
+ console.debug("ERROR: DmnLanguageService.getDmnModelContent:
", err);
+ return undefined;
+ }
+ },
+ })
+ );
+ });
},
- });
- }, [workspaces, workspaceFilePromise.data?.workspaceFile]);
+ [workspaces, workspaceFilePromise.data?.workspaceFile]
+ )
+ );
Review Comment:
I don't understand why this conversion to state + effect was necessary. It
doesn't look like you're using `modelContent`....
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
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})`);
constructor(
private readonly args: {
- getDmnImportedModelResource: (
- importedModelPathRelativeToWorkspaceRoot: string
- ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+ getModelContent: (args: {
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+ }) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
}
) {}
- private getImportedModelPathRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources
+ private getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
Review Comment:
I know we kind of came up with this name together, but it's really not
readable. Let's change it to something more explicit like "visited" or "seen".
It stands out enough, and since its scope is really narrow, it's okay to leave
out the format of the path, IMHO. There's very little indirection here, and
more than that, you can add a nice TSDoc entry here. Let's leave the big names
for variables that are part of the domain, not part of some programming
mechanism we need (in this cache, a cycle prevention set)
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
Review Comment:
I know this was already like that, but since you touched it... :)
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
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})`);
constructor(
private readonly args: {
- getDmnImportedModelResource: (
- importedModelPathRelativeToWorkspaceRoot: string
- ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+ getModelContent: (args: {
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+ }) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
}
) {}
- private getImportedModelPathRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources
+ private getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): 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) ??
"")
- )
- )
+ if (!modelResources.content) {
+ return [];
+ }
+
+ const marshaller: DmnMarshaller = getMarshaller(modelResources.content);
+ const definitions = marshaller.parser.parse()?.definitions;
+
+ if (!definitions) {
+ return [];
+ }
+ if (!definitions.import) {
+ return [];
+ }
+
+ return definitions.import
+ .flatMap((importedModel) => {
+ const importedModelNormalizedPosixPathRelativeToWorkspaceRoot =
path.posix.normalize(
+ path.posix.join(
+
path.dirname(modelResources.normalizedPosixPathRelativeToWorkspaceRoot),
+ path.normalize(importedModel["@_locationURI"] ?? "")
+ )
+ );
+
+ // Can't import a model that already is imported
+ if (
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.has(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ )
+ ) {
+ return [];
+ }
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.add(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ return importedModelNormalizedPosixPathRelativeToWorkspaceRoot;
+ })
.filter((e) => e !== null) as string[];
Review Comment:
This filter became useless after the change, no? I mean, we're not returning
any `null` elements on `flatMap`, right?
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
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})`);
constructor(
private readonly args: {
- getDmnImportedModelResource: (
- importedModelPathRelativeToWorkspaceRoot: string
- ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+ getModelContent: (args: {
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+ }) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
}
) {}
- private getImportedModelPathRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources
+ private getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): 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) ??
"")
- )
- )
+ if (!modelResources.content) {
+ return [];
+ }
+
+ const marshaller: DmnMarshaller = getMarshaller(modelResources.content);
+ const definitions = marshaller.parser.parse()?.definitions;
+
+ if (!definitions) {
+ return [];
+ }
+ if (!definitions.import) {
+ return [];
+ }
Review Comment:
Isn't it the same as
```suggestion
if (!definitions?.import) {
return [];
}
```
?
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
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})`);
constructor(
private readonly args: {
- getDmnImportedModelResource: (
- importedModelPathRelativeToWorkspaceRoot: string
- ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+ getModelContent: (args: {
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+ }) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
}
) {}
- private getImportedModelPathRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources
+ private getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): 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) ??
"")
- )
- )
+ if (!modelResources.content) {
+ return [];
+ }
+
+ const marshaller: DmnMarshaller = getMarshaller(modelResources.content);
+ const definitions = marshaller.parser.parse()?.definitions;
+
+ if (!definitions) {
+ return [];
+ }
+ if (!definitions.import) {
+ return [];
+ }
+
+ return definitions.import
+ .flatMap((importedModel) => {
+ const importedModelNormalizedPosixPathRelativeToWorkspaceRoot =
path.posix.normalize(
+ path.posix.join(
+
path.dirname(modelResources.normalizedPosixPathRelativeToWorkspaceRoot),
+ path.normalize(importedModel["@_locationURI"] ?? "")
+ )
+ );
+
+ // Can't import a model that already is imported
+ if (
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.has(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ )
+ ) {
+ return [];
+ }
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.add(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ return importedModelNormalizedPosixPathRelativeToWorkspaceRoot;
+ })
.filter((e) => e !== null) as string[];
}
- public getImportedModelPathsRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources[] |
DmnLanguageServiceImportedModelResources
+ private getImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources[] |
DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): string[] {
if (Array.isArray(modelResources)) {
- return modelResources.flatMap((modelResource) =>
this.getImportedModelPathRelativeToWorkspaceRoot(modelResource));
+ return modelResources.flatMap((modelResource) =>
+ this.getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResource,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ )
+ );
}
- return this.getImportedModelPathRelativeToWorkspaceRoot(modelResources);
+ return this.getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ }
+
+ private async internalGetImportedModelsByModelResources(
+ modelResources: DmnLanguageServiceImportedModelResources[],
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
+ ): Promise<DmnLanguageServiceImportedModelResources[]> {
+ // get imported models resources
+ const importedModelsPathsRelativeToWorkspaceRoot =
+ this.getImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ if (importedModelsPathsRelativeToWorkspaceRoot &&
importedModelsPathsRelativeToWorkspaceRoot.length > 0) {
+ const importedModelsResources = (
+ await Promise.all(
+
importedModelsPathsRelativeToWorkspaceRoot.map((normalizedPosixPathRelativeToWorkspaceRoot)
=>
+ this.args.getModelContent({
normalizedPosixPathRelativeToWorkspaceRoot })
+ )
+ )
+ ).filter((e) => e !== undefined) as
DmnLanguageServiceImportedModelResources[];
+
+ if (importedModelsResources.length > 0) {
+ return [
+ ...importedModelsResources,
+ ...(await this.internalGetImportedModelsByModelResources(
+ importedModelsResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ )),
+ ];
+ }
+ return [...importedModelsResources];
+ }
+ return [];
+ }
+
+ private async getImportedModelsByModelResources(
+ modelsResources: DmnLanguageServiceImportedModelResources[]
+ ): Promise<DmnLanguageServiceImportedModelResources[]> {
+ return this.internalGetImportedModelsByModelResources(modelsResources, new
Set<string>());
+ }
+
+ public async getImportedModels(args: ImportedModelsParams) {
+ if (args.modelResources) {
+ return this.getImportedModelsByModelResources(args.modelResources);
+ }
Review Comment:
From the type, this is not optional nor nullable.
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
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})`);
constructor(
private readonly args: {
- getDmnImportedModelResource: (
- importedModelPathRelativeToWorkspaceRoot: string
- ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+ getModelContent: (args: {
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+ }) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
}
) {}
- private getImportedModelPathRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources
+ private getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): 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) ??
"")
- )
- )
+ if (!modelResources.content) {
+ return [];
+ }
+
+ const marshaller: DmnMarshaller = getMarshaller(modelResources.content);
+ const definitions = marshaller.parser.parse()?.definitions;
+
+ if (!definitions) {
+ return [];
+ }
+ if (!definitions.import) {
+ return [];
+ }
+
+ return definitions.import
+ .flatMap((importedModel) => {
+ const importedModelNormalizedPosixPathRelativeToWorkspaceRoot =
path.posix.normalize(
+ path.posix.join(
+
path.dirname(modelResources.normalizedPosixPathRelativeToWorkspaceRoot),
+ path.normalize(importedModel["@_locationURI"] ?? "")
+ )
+ );
+
+ // Can't import a model that already is imported
+ if (
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.has(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ )
+ ) {
+ return [];
+ }
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.add(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ return importedModelNormalizedPosixPathRelativeToWorkspaceRoot;
+ })
.filter((e) => e !== null) as string[];
}
- public getImportedModelPathsRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources[] |
DmnLanguageServiceImportedModelResources
+ private getImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources[] |
DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
Review Comment:
Same here.
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -109,27 +213,14 @@ 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[];
-
- if (importedModelsResources.length > 0) {
- return [...importedModelsResources, ...(await
this.getAllImportedModelsResources(importedModelsResources))];
- }
- return [...importedModelsResources];
+ public getDmnSpecVersion(modelContent: string) {
+ if (modelContent === "") {
+ return;
+ }
+ try {
+ return getMarshaller(modelContent)?.originalVersion;
Review Comment:
`getMarshaller` never returns null or undefined...
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
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})`);
constructor(
private readonly args: {
- getDmnImportedModelResource: (
- importedModelPathRelativeToWorkspaceRoot: string
- ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+ getModelContent: (args: {
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+ }) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
}
) {}
- private getImportedModelPathRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources
+ private getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): 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) ??
"")
- )
- )
+ if (!modelResources.content) {
+ return [];
+ }
+
+ const marshaller: DmnMarshaller = getMarshaller(modelResources.content);
Review Comment:
Unnecessary explicit type.
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
Review Comment:
These types are really confusing.. and it looks like they're not being
reused. Giving names to types can be really hard, especially when they are
single-purposed. Please consider inlining them or changing some functions'
signatures... it's really hard to understand why you'd need an XOR between a
list of models and a path...
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
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})`);
constructor(
private readonly args: {
- getDmnImportedModelResource: (
- importedModelPathRelativeToWorkspaceRoot: string
- ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+ getModelContent: (args: {
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+ }) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
}
) {}
- private getImportedModelPathRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources
+ private getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): 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) ??
"")
- )
- )
+ if (!modelResources.content) {
+ return [];
+ }
+
+ const marshaller: DmnMarshaller = getMarshaller(modelResources.content);
+ const definitions = marshaller.parser.parse()?.definitions;
+
+ if (!definitions) {
+ return [];
+ }
+ if (!definitions.import) {
+ return [];
+ }
+
+ return definitions.import
+ .flatMap((importedModel) => {
+ const importedModelNormalizedPosixPathRelativeToWorkspaceRoot =
path.posix.normalize(
+ path.posix.join(
+
path.dirname(modelResources.normalizedPosixPathRelativeToWorkspaceRoot),
+ path.normalize(importedModel["@_locationURI"] ?? "")
+ )
+ );
+
+ // Can't import a model that already is imported
+ if (
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.has(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ )
+ ) {
+ return [];
+ }
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.add(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ return importedModelNormalizedPosixPathRelativeToWorkspaceRoot;
+ })
.filter((e) => e !== null) as string[];
}
- public getImportedModelPathsRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources[] |
DmnLanguageServiceImportedModelResources
+ private getImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources[] |
DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): string[] {
if (Array.isArray(modelResources)) {
- return modelResources.flatMap((modelResource) =>
this.getImportedModelPathRelativeToWorkspaceRoot(modelResource));
+ return modelResources.flatMap((modelResource) =>
+ this.getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResource,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ )
+ );
}
- return this.getImportedModelPathRelativeToWorkspaceRoot(modelResources);
+ return this.getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ }
+
+ private async internalGetImportedModelsByModelResources(
+ modelResources: DmnLanguageServiceImportedModelResources[],
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
+ ): Promise<DmnLanguageServiceImportedModelResources[]> {
+ // get imported models resources
+ const importedModelsPathsRelativeToWorkspaceRoot =
+ this.getImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ if (importedModelsPathsRelativeToWorkspaceRoot &&
importedModelsPathsRelativeToWorkspaceRoot.length > 0) {
+ const importedModelsResources = (
+ await Promise.all(
+
importedModelsPathsRelativeToWorkspaceRoot.map((normalizedPosixPathRelativeToWorkspaceRoot)
=>
+ this.args.getModelContent({
normalizedPosixPathRelativeToWorkspaceRoot })
+ )
+ )
+ ).filter((e) => e !== undefined) as
DmnLanguageServiceImportedModelResources[];
+
+ if (importedModelsResources.length > 0) {
+ return [
+ ...importedModelsResources,
+ ...(await this.internalGetImportedModelsByModelResources(
+ importedModelsResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ )),
+ ];
+ }
+ return [...importedModelsResources];
+ }
+ return [];
+ }
+
+ private async getImportedModelsByModelResources(
+ modelsResources: DmnLanguageServiceImportedModelResources[]
+ ): Promise<DmnLanguageServiceImportedModelResources[]> {
+ return this.internalGetImportedModelsByModelResources(modelsResources, new
Set<string>());
+ }
+
+ public async getImportedModels(args: ImportedModelsParams) {
+ if (args.modelResources) {
+ return this.getImportedModelsByModelResources(args.modelResources);
+ }
+
+ const importedModelsNormalizedPosixPathRelativeToWorkspaceRoot = new
Set<string>();
+
importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.add(args.normalizedPosixPathRelativeToWorkspaceRoot);
+
+ const modelResource = [
+ (await this.args.getModelContent({
+ normalizedPosixPathRelativeToWorkspaceRoot:
args.normalizedPosixPathRelativeToWorkspaceRoot,
+ })) ?? ([] as DmnLanguageServiceImportedModelResources[]),
+ ].flatMap((e) => e);
Review Comment:
Singular name for an array variable. You can inline this one. It will allow
you to remove the casting too, as type inference will kick in.
##########
packages/online-editor/src/dmnRunner/DmnRunnerContextProvider.tsx:
##########
@@ -216,14 +216,14 @@ export function DmnRunnerContextProvider(props:
PropsWithChildren<Props>) {
const decodedFileContent = decoder.decode(fileContent);
const importedModelsResources =
- (await props.dmnLanguageService?.getAllImportedModelsResources([
- { content: decodedFileContent, pathRelativeToWorkspaceRoot:
props.workspaceFile.relativePath },
- ])) ?? [];
+ (await props.dmnLanguageService?.getImportedModels({
+ normalizedPosixPathRelativeToWorkspaceRoot:
props.workspaceFile.relativePath,
+ })) ?? [];
Review Comment:
I feel like this has an explicit `any` maybe?
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
Review Comment:
Plural name for an interface that represents a single unit of resource.
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
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})`);
constructor(
private readonly args: {
- getDmnImportedModelResource: (
- importedModelPathRelativeToWorkspaceRoot: string
- ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+ getModelContent: (args: {
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+ }) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
}
) {}
- private getImportedModelPathRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources
+ private getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): 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) ??
"")
- )
- )
+ if (!modelResources.content) {
+ return [];
+ }
+
+ const marshaller: DmnMarshaller = getMarshaller(modelResources.content);
+ const definitions = marshaller.parser.parse()?.definitions;
+
+ if (!definitions) {
+ return [];
+ }
+ if (!definitions.import) {
+ return [];
+ }
+
+ return definitions.import
+ .flatMap((importedModel) => {
+ const importedModelNormalizedPosixPathRelativeToWorkspaceRoot =
path.posix.normalize(
+ path.posix.join(
+
path.dirname(modelResources.normalizedPosixPathRelativeToWorkspaceRoot),
+ path.normalize(importedModel["@_locationURI"] ?? "")
+ )
+ );
+
+ // Can't import a model that already is imported
+ if (
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.has(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ )
+ ) {
+ return [];
+ }
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.add(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ return importedModelNormalizedPosixPathRelativeToWorkspaceRoot;
+ })
.filter((e) => e !== null) as string[];
}
- public getImportedModelPathsRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources[] |
DmnLanguageServiceImportedModelResources
+ private getImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources[] |
DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): string[] {
if (Array.isArray(modelResources)) {
- return modelResources.flatMap((modelResource) =>
this.getImportedModelPathRelativeToWorkspaceRoot(modelResource));
+ return modelResources.flatMap((modelResource) =>
+ this.getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResource,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ )
+ );
}
- return this.getImportedModelPathRelativeToWorkspaceRoot(modelResources);
+ return this.getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ }
+
+ private async internalGetImportedModelsByModelResources(
+ modelResources: DmnLanguageServiceImportedModelResources[],
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
+ ): Promise<DmnLanguageServiceImportedModelResources[]> {
+ // get imported models resources
+ const importedModelsPathsRelativeToWorkspaceRoot =
+ this.getImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ if (importedModelsPathsRelativeToWorkspaceRoot &&
importedModelsPathsRelativeToWorkspaceRoot.length > 0) {
+ const importedModelsResources = (
+ await Promise.all(
+
importedModelsPathsRelativeToWorkspaceRoot.map((normalizedPosixPathRelativeToWorkspaceRoot)
=>
+ this.args.getModelContent({
normalizedPosixPathRelativeToWorkspaceRoot })
+ )
+ )
+ ).filter((e) => e !== undefined) as
DmnLanguageServiceImportedModelResources[];
+
+ if (importedModelsResources.length > 0) {
+ return [
+ ...importedModelsResources,
+ ...(await this.internalGetImportedModelsByModelResources(
+ importedModelsResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ )),
+ ];
+ }
+ return [...importedModelsResources];
+ }
+ return [];
+ }
+
+ private async getImportedModelsByModelResources(
+ modelsResources: DmnLanguageServiceImportedModelResources[]
+ ): Promise<DmnLanguageServiceImportedModelResources[]> {
+ return this.internalGetImportedModelsByModelResources(modelsResources, new
Set<string>());
+ }
+
+ public async getImportedModels(args: ImportedModelsParams) {
+ if (args.modelResources) {
+ return this.getImportedModelsByModelResources(args.modelResources);
+ }
+
+ const importedModelsNormalizedPosixPathRelativeToWorkspaceRoot = new
Set<string>();
Review Comment:
Same here for the big name. I think naming this `seen` or `visited` or even
`cyclePrevention` would be much better.
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
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})`);
constructor(
private readonly args: {
- getDmnImportedModelResource: (
- importedModelPathRelativeToWorkspaceRoot: string
- ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+ getModelContent: (args: {
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+ }) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
}
) {}
- private getImportedModelPathRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources
+ private getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): 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) ??
"")
- )
- )
+ if (!modelResources.content) {
+ return [];
+ }
+
+ const marshaller: DmnMarshaller = getMarshaller(modelResources.content);
+ const definitions = marshaller.parser.parse()?.definitions;
+
+ if (!definitions) {
+ return [];
+ }
+ if (!definitions.import) {
+ return [];
+ }
+
+ return definitions.import
+ .flatMap((importedModel) => {
+ const importedModelNormalizedPosixPathRelativeToWorkspaceRoot =
path.posix.normalize(
+ path.posix.join(
+
path.dirname(modelResources.normalizedPosixPathRelativeToWorkspaceRoot),
+ path.normalize(importedModel["@_locationURI"] ?? "")
+ )
+ );
+
+ // Can't import a model that already is imported
+ if (
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.has(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ )
+ ) {
+ return [];
+ }
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot.add(
+ importedModelNormalizedPosixPathRelativeToWorkspaceRoot
+ );
+ return importedModelNormalizedPosixPathRelativeToWorkspaceRoot;
+ })
.filter((e) => e !== null) as string[];
}
- public getImportedModelPathsRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources[] |
DmnLanguageServiceImportedModelResources
+ private getImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources[] |
DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): string[] {
if (Array.isArray(modelResources)) {
- return modelResources.flatMap((modelResource) =>
this.getImportedModelPathRelativeToWorkspaceRoot(modelResource));
+ return modelResources.flatMap((modelResource) =>
+ this.getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResource,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot
+ )
+ );
}
Review Comment:
Actually, I think we can always force an array here, right? I mean, there's
only one usage of this method, and it's private, and this usage always passed
an array as argument.
##########
packages/dmn-language-service/src/DmnLanguageService.ts:
##########
@@ -20,61 +20,165 @@
import { DmnDocumentData } from "./DmnDocumentData";
import { DmnDecision } from "./DmnDecision";
import * as path from "path";
+import { DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller";
-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";
+interface ImportedModelsByModelResources {
+ modelResources: DmnLanguageServiceImportedModelResources[];
+ normalizedPosixPathRelativeToWorkspaceRoot?: undefined;
+}
+
+interface ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot {
+ modelResources?: undefined;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+}
+
+type ImportedModelsParams = ImportedModelsByModelResources |
ImportedModelsByNormalizedPosixPathRelativeToWorkspaceRoot;
+
export interface DmnLanguageServiceImportedModelResources {
content: string;
- pathRelativeToWorkspaceRoot: string;
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
}
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})`);
constructor(
private readonly args: {
- getDmnImportedModelResource: (
- importedModelPathRelativeToWorkspaceRoot: string
- ) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
+ getModelContent: (args: {
+ normalizedPosixPathRelativeToWorkspaceRoot: string;
+ }) => Promise<DmnLanguageServiceImportedModelResources | undefined>;
}
) {}
- private getImportedModelPathRelativeToWorkspaceRoot(
- modelResources: DmnLanguageServiceImportedModelResources
+ private getImportedModelByNormalizedPosixPathRelativeToWorkspaceRoot(
+ modelResources: DmnLanguageServiceImportedModelResources,
+ importedModelsNormalizedPosixPathRelativeToWorkspaceRoot: Set<string>
): 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) ??
"")
- )
- )
+ if (!modelResources.content) {
+ return [];
+ }
+
+ const marshaller: DmnMarshaller = getMarshaller(modelResources.content);
+ const definitions = marshaller.parser.parse()?.definitions;
+
+ if (!definitions) {
+ return [];
+ }
+ if (!definitions.import) {
+ return [];
+ }
+
+ return definitions.import
+ .flatMap((importedModel) => {
+ const importedModelNormalizedPosixPathRelativeToWorkspaceRoot =
path.posix.normalize(
+ path.posix.join(
+
path.dirname(modelResources.normalizedPosixPathRelativeToWorkspaceRoot),
+ path.normalize(importedModel["@_locationURI"] ?? "")
+ )
+ );
Review Comment:
Mixing `path.posix` and `path` usages. I'm really not sure why we would use
`path.posix`, to be honest. Can you please double-check at the `Windows vs.
POSIX` section here? -->
https://nodejs.org/api/path.html#pathposix:~:text=COPY-,Windows%20vs.%20POSIX,-%23
##########
packages/online-editor/src/editor/EditorPage.tsx:
##########
@@ -331,34 +331,48 @@ export function EditorPage(props: Props) {
setContentErrorAlert.show();
}, [setContentErrorAlert]);
- const dmnLanguageService = useMemo(() => {
- if (!workspaceFilePromise.data?.workspaceFile) {
- return;
- }
+ const [dmnLanguageService, setDmnLanguageService] =
useState<DmnLanguageService | undefined>();
+ useCancelableEffect(
+ useCallback(
+ ({ canceled }) => {
+ if (!workspaceFilePromise.data?.workspaceFile) {
+ return;
+ }
- if (workspaceFilePromise.data?.workspaceFile.extension.toLocaleLowerCase()
!== "dmn") {
- return;
- }
+ if
(workspaceFilePromise.data?.workspaceFile.extension.toLocaleLowerCase() !==
"dmn") {
+ return;
+ }
- return new DmnLanguageService({
- getDmnImportedModelResource: async
(importedModelPathRelativeToWorkspaceRoot: string) => {
- try {
- const fileContent = await workspaces.getFileContent({
- workspaceId: workspaceFilePromise.data?.workspaceFile.workspaceId,
- relativePath: importedModelPathRelativeToWorkspaceRoot,
- });
+
workspaceFilePromise.data?.workspaceFile.getFileContentsAsString().then((modelContent)
=> {
+ if (canceled.get()) {
+ return;
+ }
- return {
- content: decoder.decode(fileContent),
- pathRelativeToWorkspaceRoot:
importedModelPathRelativeToWorkspaceRoot,
- };
- } catch (err) {
- console.debug("ERROR: DmnLanguageService.getImportedModel: ", err);
- return undefined;
- }
+ setDmnLanguageService(
+ new DmnLanguageService({
+ getModelContent: async (args: {
normalizedPosixPathRelativeToWorkspaceRoot: string }) => {
+ try {
+ const fileContent = await workspaces.getFileContent({
+ workspaceId:
workspaceFilePromise.data?.workspaceFile.workspaceId,
+ relativePath:
args.normalizedPosixPathRelativeToWorkspaceRoot,
+ });
+
+ return {
+ content: decoder.decode(fileContent),
+ normalizedPosixPathRelativeToWorkspaceRoot:
args.normalizedPosixPathRelativeToWorkspaceRoot,
+ };
+ } catch (err) {
+ console.debug("ERROR: DmnLanguageService.getDmnModelContent:
", err);
+ return undefined;
+ }
+ },
+ })
+ );
+ });
},
- });
- }, [workspaces, workspaceFilePromise.data?.workspaceFile]);
+ [workspaces, workspaceFilePromise.data?.workspaceFile]
+ )
+ );
Review Comment:
I think I remember you went through an iteration where the DMN LS instance
needed the file content... I think maybe you can revert this change?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]