Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kie-issues#970: Test Scenario Editor: Integrate the @kie-tools/scesim-editor component with the DMN Marshaller #2887

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 31 additions & 24 deletions packages/scesim-editor-envelope/src/TestScenarioEditorRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import * as TestScenarioEditor from "@kie-tools/scesim-editor/dist/TestScenarioE
import { getMarshaller, SceSimMarshaller, SceSimModel } from "@kie-tools/scesim-marshaller";
import { EMPTY_ONE_EIGHT } from "@kie-tools/scesim-editor/dist/resources/EmptyScesimFile";

export const DMN_MODELS_SEARCH_GLOB_PATTERN = "**/*.{dmn}";
export const DMN_MODELS_SEARCH_GLOB_PATTERN = "**/*.dmn";

export type TestScenarioEditorRootProps = {
exposing: (s: TestScenarioEditorRoot) => void;
Expand All @@ -58,7 +58,7 @@ export type TestScenarioEditorRootState = {
keyboardShortcutsRegistred: boolean;
keyboardShortcutsRegisterIds: number[];
marshaller: SceSimMarshaller | undefined;
openFilenormalizedPosixPathRelativeToTheWorkspaceRoot: string | undefined;
openFileNormalizedPosixPathRelativeToTheWorkspaceRoot: string | undefined;
pointer: number;
stack: SceSimModel[];
};
Expand All @@ -74,13 +74,13 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
this.testScenarioEditorRef = React.createRef();
this.state = {
error: undefined,
externalModelsByNamespace: {},
externalModelsByNamespace: new Map(),
externalModelsManagerDoneBootstraping: false,
isReadOnly: props.isReadOnly,
keyboardShortcutsRegisterIds: [],
keyboardShortcutsRegistred: false,
marshaller: undefined,
openFilenormalizedPosixPathRelativeToTheWorkspaceRoot: undefined,
openFileNormalizedPosixPathRelativeToTheWorkspaceRoot: undefined,
pointer: -1,
stack: [],
};
Expand Down Expand Up @@ -111,7 +111,7 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
}

public async setContent(
openFilenormalizedPosixPathRelativeToTheWorkspaceRoot: string,
openFileNormalizedPosixPathRelativeToTheWorkspaceRoot: string,
content: string
): Promise<void> {
const marshaller = this.getMarshaller(content);
Expand All @@ -124,7 +124,7 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
savedStackPointer = [...prev.stack];
return {
stack: [marshaller.parser.parse()],
openFilenormalizedPosixPathRelativeToTheWorkspaceRoot,
openFileNormalizedPosixPathRelativeToTheWorkspaceRoot,
pointer: 0,
};
});
Expand All @@ -136,14 +136,14 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
this.setState((prev) => {
// External change to the same file.
if (
prev.openFilenormalizedPosixPathRelativeToTheWorkspaceRoot ===
openFilenormalizedPosixPathRelativeToTheWorkspaceRoot
prev.openFileNormalizedPosixPathRelativeToTheWorkspaceRoot ===
openFileNormalizedPosixPathRelativeToTheWorkspaceRoot
) {
const newStack = savedStackPointer.slice(0, prev.pointer + 1);
return {
externalModelsManagerDoneBootstraping: true,
isReadOnly: prev.isReadOnly,
openFilenormalizedPosixPathRelativeToTheWorkspaceRoot,
openFileNormalizedPosixPathRelativeToTheWorkspaceRoot,
marshaller,
pointer: newStack.length,
stack: [...newStack, marshaller.parser.parse()],
Expand All @@ -156,7 +156,7 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
externalModelsManagerDoneBootstraping: true,
isReadOnly: prev.isReadOnly,
marshaller,
openFilenormalizedPosixPathRelativeToTheWorkspaceRoot,
openFileNormalizedPosixPathRelativeToTheWorkspaceRoot,
pointer: 0,
stack: [marshaller.parser.parse()],
};
Expand Down Expand Up @@ -198,14 +198,14 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
},
() =>
this.props.onNewEdit({
id: `${this.state.openFilenormalizedPosixPathRelativeToTheWorkspaceRoot}__${generateUuid()}`,
id: `${this.state.openFileNormalizedPosixPathRelativeToTheWorkspaceRoot}__${generateUuid()}`,
})
);
};

private onRequestExternalModelsAvailableToInclude: TestScenarioEditor.OnRequestExternalModelsAvailableToInclude =
async () => {
if (!this.state.openFilenormalizedPosixPathRelativeToTheWorkspaceRoot) {
if (!this.state.openFileNormalizedPosixPathRelativeToTheWorkspaceRoot) {
return [];
}

Expand All @@ -215,7 +215,7 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
});

return list.normalizedPosixPathsRelativeToTheWorkspaceRoot.flatMap((p) =>
__path.relative(__path.dirname(this.state.openFilenormalizedPosixPathRelativeToTheWorkspaceRoot!), p)
__path.relative(__path.dirname(this.state.openFileNormalizedPosixPathRelativeToTheWorkspaceRoot!), p)
);
};

Expand All @@ -224,7 +224,7 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
) => {
const normalizedPosixPathRelativeToTheWorkspaceRoot = __path
.resolve(
__path.dirname(this.state.openFilenormalizedPosixPathRelativeToTheWorkspaceRoot!),
__path.dirname(this.state.openFileNormalizedPosixPathRelativeToTheWorkspaceRoot!),
normalizedPosixPathRelativeToTheOpenFile
)
.substring(1); // Remove leading slash.
Expand Down Expand Up @@ -264,7 +264,7 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
};

private onOpenFileFromPathRelativeToTheOpenFile = (normalizedPosixPathRelativeToTheOpenFile: string) => {
if (!this.state.openFilenormalizedPosixPathRelativeToTheWorkspaceRoot) {
if (!this.state.openFileNormalizedPosixPathRelativeToTheWorkspaceRoot) {
return;
}

Expand Down Expand Up @@ -318,6 +318,7 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
<>
<TestScenarioEditor.TestScenarioEditor
ref={this.testScenarioEditorRef}
externalModelsByNamespace={this.state.externalModelsByNamespace}
issueTrackerHref={""}
model={this.model}
onModelChange={this.onModelChange}
Expand All @@ -326,14 +327,14 @@ export class TestScenarioEditorRoot extends React.Component<TestScenarioEditorRo
onRequestToJumpToPath={this.onOpenFileFromPathRelativeToTheOpenFile}
onRequestToResolvePath={this.onRequestToResolvePathRelativeToTheOpenFile}
openFileNormalizedPosixPathRelativeToTheWorkspaceRoot={
this.state.openFilenormalizedPosixPathRelativeToTheWorkspaceRoot
this.state.openFileNormalizedPosixPathRelativeToTheWorkspaceRoot
}
/>
{
<ExternalModelsManager
workspaceRootAbsolutePosixPath={this.props.workspaceRootAbsolutePosixPath}
thisScesimNormalizedPosixPathRelativeToTheWorkspaceRoot={
this.state.openFilenormalizedPosixPathRelativeToTheWorkspaceRoot
this.state.openFileNormalizedPosixPathRelativeToTheWorkspaceRoot
}
model={this.model}
onChange={this.setExternalModelsByNamespace}
Expand Down Expand Up @@ -371,7 +372,7 @@ function ExternalModelsManager({
return null;
}
if (model.ScenarioSimulationModel.settings.dmnNamespace?.__$$text) {
return model.ScenarioSimulationModel.settings.dmnNamespace?.__$$text.toUpperCase();
return model.ScenarioSimulationModel.settings.dmnNamespace?.__$$text;
}
return null;
}, [model.ScenarioSimulationModel.settings]);
Expand Down Expand Up @@ -437,7 +438,7 @@ function ExternalModelsManager({
return Promise.all(resources);
})
.then((resources) => {
const externalModelsIndex: TestScenarioEditor.ExternalDmnsIndex = {};
const externalModelsByNamespace: TestScenarioEditor.ExternalDmnsIndex = new Map();

for (let i = 0; i < resources.length; i++) {
const resource = resources[i];
Expand All @@ -457,29 +458,35 @@ function ExternalModelsManager({
const namespace = domParser.getDomDocument(content).documentElement.getAttribute("namespace");
if (targetNamespace && namespace === targetNamespace) {
// Check for multiplicity of namespaces on DMN models
if (externalModelsIndex[namespace]) {
if (externalModelsByNamespace.has(namespace)) {
console.warn(
`TEST SCENARIO EDITOR ROOT: Multiple DMN models encountered with the same namespace '${namespace}': '${
resource.normalizedPosixPathRelativeToTheWorkspaceRoot
}' and '${
externalModelsIndex[namespace]!.normalizedPosixPathRelativeToTheOpenFile
externalModelsByNamespace.get(namespace)!.normalizedPosixPathRelativeToTheOpenFile
}'. The latter will be considered.`
);
}

externalModelsIndex[namespace] = {
externalModelsByNamespace.set(namespace, {
normalizedPosixPathRelativeToTheOpenFile,
model: normalize(getDmnMarshaller(content, { upgradeTo: "latest" }).parser.parse()),
svg: "",
};
});
}
} else {
throw new Error(`Unknown extension '${ext}'.`);
}
}

/* If the DMN file with the targetNamespace is not found, it populates the returning externalModelsByNamespace Map
/* adding the targetNamespace key with an undefined model, to be propagated to the internal Test Scenario component */
if (targetNamespace && !externalModelsByNamespace.has(targetNamespace)) {
externalModelsByNamespace.set(targetNamespace, undefined);
}

if (!canceled) {
onChange(externalModelsIndex);
onChange(externalModelsByNamespace);
}
externalModelsManagerDoneBootstraping.resolve();
});
Expand Down
13 changes: 13 additions & 0 deletions packages/scesim-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@
"description": "The Test Scenario editor, a powerful tool to create, edit and manage *.scesim files, to test DMN assets",
"license": "Apache-2.0",
"keywords": [],
"homepage": "https://github.com/apache/incubator-kie-tools",
"repository": {
"type": "git",
"url": "https://github.com/apache/incubator-kie-tools.git"
},
"bugs": {
"url": "https://github.com/apache/incubator-kie-tools/issues"
},
"files": [
"dist",
"src"
],
"scripts": {
"build:dev": "rimraf dist && pnpm copy:css && tsc -p tsconfig.json",
"build:prod": "rimraf dist && pnpm copy:css && pnpm lint && tsc -p tsconfig.json && pnpm test-e2e",
Expand All @@ -21,6 +33,7 @@
"dependencies": {
"@kie-tools-core/i18n": "workspace:*",
"@kie-tools-core/patternfly-base": "workspace:*",
"@kie-tools-core/react-hooks": "workspace:*",
"@kie-tools/boxed-expression-component": "workspace:*",
"@kie-tools/dmn-marshaller": "workspace:*",
"@kie-tools/i18n-common-dictionary": "workspace:*",
Expand Down
30 changes: 22 additions & 8 deletions packages/scesim-editor/src/TestScenarioEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ import {
} from "./store/TestScenarioStoreContext";
import { TestScenarioEditorErrorFallback } from "./TestScenarioEditorErrorFallback";
import { TestScenarioEditorContextProvider, useTestScenarioEditor } from "./TestScenarioEditorContext";
import { TestScenarioEditorExternalModelsContextProvider } from "./externalModels/TestScenarioEditorDependenciesContext";
import {
TestScenarioEditorExternalModelsContextProvider,
useExternalModels,
} from "./externalModels/TestScenarioEditorDependenciesContext";
import { useEffectAfterFirstRender } from "./hook/useEffectAfterFirstRender";
import { INITIAL_COMPUTED_CACHE } from "./store/computed/initial";

Expand Down Expand Up @@ -95,7 +98,7 @@ export type OnSceSimModelChange = (model: SceSimModel) => void;
export type OnRequestExternalModelByPath = (
normalizedPosixPathRelativeToTheOpenFile: string
) => Promise<ExternalDmn | null>;
export type ExternalDmnsIndex = Record<string /** normalizedPosixPathRelativeToTheOpenFile */, ExternalDmn | undefined>;
export type ExternalDmnsIndex = Map<string, ExternalDmn | undefined>;

export type ExternalDmn = {
model: Normalized<DmnLatestModel>;
Expand Down Expand Up @@ -165,15 +168,26 @@ export type TestScenarioSelectedColumnMetaData = {
function TestScenarioMainPanel() {
const { i18n } = useTestScenarioEditorI18n();
const { commandsRef } = useCommands();
const { externalModelsByNamespace } = useExternalModels();
const testScenarioEditorStoreApi = useTestScenarioEditorStoreApi();
const navigation = useTestScenarioEditorStore((s) => s.navigation);
const scesimModel = useTestScenarioEditorStore((s) => s.scesim.model);
const isAlertEnabled = true; // Will be managed in kie-issue#970
const navigation = useTestScenarioEditorStore((state) => state.navigation);
const scesimModel = useTestScenarioEditorStore((state) => state.scesim.model);
const testScenarioDmnNamespace = scesimModel.ScenarioSimulationModel.settings.dmnNamespace?.__$$text;
const testScenarioType = scesimModel.ScenarioSimulationModel.settings.type?.__$$text.toUpperCase();

const scenarioTableScrollableElementRef = useRef<HTMLDivElement | null>(null);
const backgroundTableScrollableElementRef = useRef<HTMLDivElement | null>(null);

/* RULE-based Test Scenario are still not supported. The Notification will always be active in such a case */
const isMissingDataObjectsNotificationEnabled = useMemo(() => {
const isReferencedDMNFileMissing =
externalModelsByNamespace &&
externalModelsByNamespace.has(testScenarioDmnNamespace!) &&
!externalModelsByNamespace.get(testScenarioDmnNamespace!);

return testScenarioType === "RULE" || isReferencedDMNFileMissing;
}, [externalModelsByNamespace, testScenarioDmnNamespace, testScenarioType]);

const onTabChanged = useCallback(
(_event, tab) => {
testScenarioEditorStoreApi.setState((state) => {
Expand Down Expand Up @@ -211,10 +225,10 @@ function TestScenarioMainPanel() {
<Drawer isExpanded={navigation.dock.isOpen} isInline={true} position={"right"}>
<DrawerContent panelContent={<TestScenarioDrawerPanel onDrawerClose={() => showDockPanel(false)} />}>
<DrawerContentBody>
{isAlertEnabled && (
{isMissingDataObjectsNotificationEnabled && (
<div className="kie-scesim-editor--content-alert">
<Alert
variant={testScenarioType === "DMN" ? "warning" : "danger"}
variant={"danger"}
title={
testScenarioType === "DMN"
? i18n.alerts.dmnDataRetrievedFromScesim
Expand Down Expand Up @@ -328,7 +342,7 @@ export const TestScenarioEditorInternal = ({
reset: () => {
console.trace("[TestScenarioEditorInternal: Reset called!");
const state = testScenarioEditorStoreApi.getState();
state.dispatch(state).scesim.reset();
state.dispatch(state).navigation.reset();
},
getCommands: () => commandsRef.current,
getDiagramSvg: async () => undefined,
Expand Down
Loading
Loading