-
Notifications
You must be signed in to change notification settings - Fork 185
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#1298: Decision Services & multiple DRDs: make it possible o add external Decisions to a Decision Service #2508
Changes from 9 commits
b690ebb
33de8ea
2db60f6
b55113d
24f1b15
f916099
99924ea
d64a04e
a078041
c9f88bf
411e61d
6cd536f
a44b7da
fad6167
3ee4271
a84bea2
90c03ff
c3c23dd
54b419c
40a0fdf
227ee5a
3d8abb5
9d7a04e
a5f3e71
8bbb1cf
81d260f
1bdc614
ffa1867
2921171
e22fcd1
3cb35a5
334cf35
bd5786c
0e90a76
a03e182
d9408e7
4759ae3
91a815f
702f44f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,25 +25,48 @@ import { SnapGrid } from "../store/Store"; | |
import { MIN_NODE_SIZES } from "../diagram/nodes/DefaultSizes"; | ||
import { NODE_TYPES } from "../diagram/nodes/NodeTypes"; | ||
import { Normalized } from "../normalization/normalize"; | ||
import { ExternalModelsIndex } from "../DmnEditor"; | ||
import { buildXmlHref, parseXmlHref } from "../xml/xmlHrefs"; | ||
import { DmnLatestModel } from "@kie-tools/dmn-marshaller/dist"; | ||
import { xmlHrefToQName } from "../xml/xmlHrefToQName"; | ||
|
||
export function addDecisionToDecisionService({ | ||
definitions, | ||
decisionId, | ||
decisionServiceId, | ||
drdIndex, | ||
snapGrid, | ||
externalModelsByNamespace, | ||
}: { | ||
definitions: Normalized<DMN15__tDefinitions>; | ||
decisionId: string; | ||
decisionServiceId: string; | ||
drdIndex: number; | ||
snapGrid: SnapGrid; | ||
externalModelsByNamespace: ExternalModelsIndex | undefined; | ||
}) { | ||
console.debug(`DMN MUTATION: Adding Decision '${decisionId}' to Decision Service '${decisionServiceId}'`); | ||
|
||
const decision = definitions.drgElement?.find((s) => s["@_id"] === decisionId); | ||
if (decision?.__$$element !== "decision") { | ||
throw new Error(`DMN MUTATION: DRG Element with id '${decisionId}' is either not a Decision or doesn't exist.`); | ||
const href = parseXmlHref(decisionId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So But, if it is an external node, we only be able to find it if the full id is passed: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I think we better rename There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...and the |
||
|
||
const externalModel = externalModelsByNamespace?.[href.namespace ?? ""]; | ||
if (href.namespace && !externalModel) { | ||
throw new Error(`DMN MUTATION: Namespace '${href.namespace}' not found.`); | ||
} | ||
|
||
if (externalModel && (externalModel.model as Normalized<DmnLatestModel>).definitions) { | ||
danielzhe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const externalDrgs = (externalModel.model as Normalized<DmnLatestModel>).definitions.drgElement; | ||
const decision = externalDrgs?.find((drgElement) => drgElement["@_id"] === href.id); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personal preference, but I always think we should avoid intermediate variables to avoid giving it a name. It's one more thing to hold onto my head when reading code... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a very good topic. Also, in this case, I'm giving "meaning" to what But the main point is that if someone new looks at the code it can easily get what |
||
if (decision?.__$$element !== "decision") { | ||
throw new Error( | ||
`DMN MUTATION: DRG Element with id '${href.id}' is either not a Decision or doesn't exist in the external model '${href.namespace}'` | ||
); | ||
} | ||
Comment on lines
+64
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The error message is targeting two different cases. Not having an element and the element not being a Decision are two distinguish scenarios. I think this should be break into two errors. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm following the existing error message patterns used in this mutation and in |
||
} else { | ||
const decision = definitions.drgElement?.find((s) => s["@_id"] === href.id); | ||
if (decision?.__$$element !== "decision") { | ||
throw new Error(`DMN MUTATION: DRG Element with id '${href.id}' is either not a Decision or doesn't exist.`); | ||
} | ||
} | ||
|
||
const decisionService = definitions.drgElement?.find((s) => s["@_id"] === decisionServiceId); | ||
|
@@ -54,8 +77,11 @@ export function addDecisionToDecisionService({ | |
} | ||
|
||
const diagram = addOrGetDrd({ definitions, drdIndex }); | ||
const hrefString = buildXmlHref({ namespace: href.namespace, id: href.id }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. The The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A I think this is a problem in our codebase, where we mix the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @danielzhe @tiagobento how are we going to proceed here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @danielzhe Please use |
||
const dmnElementRef = xmlHrefToQName(hrefString, definitions); | ||
|
||
const decisionShape = diagram.diagramElements.find( | ||
(s) => s["@_dmnElementRef"] === decisionId && s.__$$element === "dmndi:DMNShape" | ||
(s) => s["@_dmnElementRef"] === dmnElementRef && s.__$$element === "dmndi:DMNShape" | ||
) as Normalized<DMNDI15__DMNShape>; | ||
|
||
const decisionServiceShape = diagram.diagramElements.find( | ||
|
@@ -65,10 +91,10 @@ export function addDecisionToDecisionService({ | |
const section = getSectionForDecisionInsideDecisionService({ decisionShape, decisionServiceShape, snapGrid }); | ||
if (section === "encapsulated") { | ||
decisionService.encapsulatedDecision ??= []; | ||
decisionService.encapsulatedDecision.push({ "@_href": `#${decisionId}` }); | ||
decisionService.encapsulatedDecision.push({ "@_href": `${hrefString}` }); | ||
} else if (section === "output") { | ||
decisionService.outputDecision ??= []; | ||
decisionService.outputDecision.push({ "@_href": `#${decisionId}` }); | ||
decisionService.outputDecision.push({ "@_href": `${hrefString}` }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice |
||
} else { | ||
throw new Error(`DMN MUTATION: Invalid section to add decision to: '${section}' `); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,21 +20,43 @@ | |
import { DMN15__tDefinitions } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; | ||
import { repopulateInputDataAndDecisionsOnDecisionService } from "./repopulateInputDataAndDecisionsOnDecisionService"; | ||
import { Normalized } from "../normalization/normalize"; | ||
import { buildXmlHref, parseXmlHref } from "../xml/xmlHrefs"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unused import |
||
import { DmnLatestModel } from "@kie-tools/dmn-marshaller/dist"; | ||
import { ExternalModelsIndex } from "../DmnEditor"; | ||
|
||
export function deleteDecisionFromDecisionService({ | ||
definitions, | ||
decisionId, | ||
decisionServiceId, | ||
externalModelsByNamespace, | ||
}: { | ||
definitions: Normalized<DMN15__tDefinitions>; | ||
decisionId: string; | ||
decisionServiceId: string; | ||
externalModelsByNamespace: ExternalModelsIndex | undefined; | ||
}) { | ||
console.debug(`DMN MUTATION: Deleting Decision '${decisionId}' from Decision Service '${decisionServiceId}'`); | ||
|
||
const decision = definitions.drgElement?.find((s) => s["@_id"] === decisionId); | ||
if (decision?.__$$element !== "decision") { | ||
throw new Error(`DMN MUTATION: DRG Element with id '${decisionId}' is either not a Decision or doesn't exist.`); | ||
const href = parseXmlHref(decisionId); | ||
|
||
const externalModel = externalModelsByNamespace?.[href.namespace ?? ""]; | ||
if (href.namespace && !externalModel) { | ||
throw new Error(`DMN MUTATION: Namespace '${href.namespace}' not found.`); | ||
} | ||
|
||
if (externalModel && (externalModel.model as Normalized<DmnLatestModel>).definitions) { | ||
danielzhe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const externalDrgs = (externalModel.model as Normalized<DmnLatestModel>).definitions.drgElement; | ||
const decision = externalDrgs?.find((drgElement) => drgElement["@_id"] === href.id); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also same as above. |
||
if (decision?.__$$element !== "decision") { | ||
throw new Error( | ||
`DMN MUTATION: DRG Element with id '${href.id}' is either not a Decision or doesn't exist in the external model '${href.namespace}'` | ||
); | ||
} | ||
Comment on lines
+52
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same with this error. |
||
} else { | ||
const decision = definitions.drgElement?.find((s) => s["@_id"] === href.id); | ||
if (decision?.__$$element !== "decision") { | ||
throw new Error(`DMN MUTATION: DRG Element with id '${href.id}' is either not a Decision or doesn't exist.`); | ||
} | ||
} | ||
|
||
const decisionService = definitions.drgElement?.find((s) => s["@_id"] === decisionServiceId); | ||
|
@@ -44,11 +66,10 @@ export function deleteDecisionFromDecisionService({ | |
); | ||
} | ||
|
||
decisionService.outputDecision = (decisionService.outputDecision ?? []).filter( | ||
(s) => s["@_href"] !== `#${decisionId}` | ||
); | ||
const xmlHref = buildXmlHref({ namespace: href.namespace, id: href.id }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same answer as above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
decisionService.outputDecision = (decisionService.outputDecision ?? []).filter((s) => s["@_href"] !== `${xmlHref}`); | ||
decisionService.encapsulatedDecision = (decisionService.encapsulatedDecision ?? []).filter( | ||
(s) => s["@_href"] !== `#${decisionId}` | ||
(s) => s["@_href"] !== `${xmlHref}` | ||
); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice |
||
|
||
repopulateInputDataAndDecisionsOnDecisionService({ definitions, decisionService }); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,20 +18,19 @@ | |
*/ | ||
|
||
import * as React from "react"; | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import { useCallback, useMemo, useRef, useState } from "react"; | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those changes are made automatically by the Prettier There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Prettier doesn't change the imports... Maybe it's your VS Code "Organize imports"? |
||
import "@patternfly/react-core/dist/styles/base.css"; | ||
import { Flex, FlexItem } from "@patternfly/react-core/dist/js/layouts/Flex"; | ||
import { Page, PageSection } from "@patternfly/react-core/dist/js/components/Page"; | ||
import { DmnLatestModel, getMarshaller, DmnMarshaller } from "@kie-tools/dmn-marshaller"; | ||
import { Normalized, normalize } from "@kie-tools/dmn-editor/dist/normalization/normalize"; | ||
import { DmnLatestModel, DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller"; | ||
import { normalize, Normalized } from "@kie-tools/dmn-editor/dist/normalization/normalize"; | ||
import { availableModelsByPath, modelsByNamespace } from "./availableModelsToInclude"; | ||
import { generateEmptyDmn15 } from "../misc/empty/Empty.stories"; | ||
import { loanPreQualificationDmn } from "../useCases/loanPreQualification/LoanPreQualification.stories"; | ||
import { DmnEditorWrapper } from "../dmnEditorStoriesWrapper"; | ||
import { | ||
DmnEditorProps, | ||
DmnEditorRef, | ||
ExternalModelsIndex, | ||
OnDmnModelChange, | ||
OnRequestExternalModelByPath, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import EmptyWithAvailableExternalModelsStories from "./EmptyWithAvailableExternalModels.stories"; | ||
import { Meta } from "@storybook/blocks"; | ||
|
||
<Meta title="MDX/Misc/EmptyWithAvailableExternalModelsStories" of={EmptyWithAvailableExternalModelsStories} /> | ||
|
||
## Empty With Available External Models | ||
|
||
When user starts with an empty diagram, a wizard for quick content initialization is displayed. | ||
The wizard enables to create simple diagram with one input and one output or a diagram with one decision table expression. | ||
This model also have some external models available to be included. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
* 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 * as React from "react"; | ||
import { useCallback, useMemo, useState } from "react"; | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import { DmnLatestModel, DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller"; | ||
import { ns as dmn15ns } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/meta"; | ||
import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; | ||
import { DMN15_SPEC } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/Dmn15Spec"; | ||
import { | ||
DmnEditor, | ||
DmnEditorProps, | ||
ExternalModelsIndex, | ||
OnRequestExternalModelByPath, | ||
OnRequestExternalModelsAvailableToInclude, | ||
OnDmnModelChange, | ||
} from "@kie-tools/dmn-editor/dist/DmnEditor"; | ||
import { normalize, Normalized } from "@kie-tools/dmn-editor/dist/normalization/normalize"; | ||
|
||
import { DmnEditorWrapper, StorybookDmnEditorProps } from "../../dmnEditorStoriesWrapper"; | ||
|
||
import { availableModelsByPath, modelsByNamespace } from "./availableModelsToInclude"; | ||
|
||
export const generateEmptyDmn15 = () => `<?xml version="1.0" encoding="UTF-8"?> | ||
<definitions | ||
xmlns="${dmn15ns.get("")}" | ||
expressionLanguage="${DMN15_SPEC.expressionLanguage.default}" | ||
namespace="https://kie.apache.org/dmn/${generateUuid()}" | ||
id="${generateUuid()}" | ||
name="DMN${generateUuid()}"> | ||
</definitions>`; | ||
|
||
const initialModel = generateEmptyDmn15(); | ||
|
||
function EmptyStoryWithIncludedModels(args: DmnEditorProps) { | ||
const [state, setState] = useState<{ | ||
marshaller: DmnMarshaller; | ||
stack: Normalized<DmnLatestModel>[]; | ||
pointer: number; | ||
}>(() => { | ||
const initialDmnMarshaller = getMarshaller(initialModel, { upgradeTo: "latest" }); | ||
return { | ||
marshaller: initialDmnMarshaller, | ||
stack: [normalize(initialDmnMarshaller.parser.parse())], | ||
pointer: 0, | ||
}; | ||
}); | ||
|
||
const currentModel = state.stack[state.pointer]; | ||
|
||
const externalModelsByNamespace = useMemo<ExternalModelsIndex>(() => { | ||
return (currentModel.definitions.import ?? []).reduce((acc, i) => { | ||
acc[i["@_namespace"]] = modelsByNamespace[i["@_namespace"]]; | ||
return acc; | ||
}, {} as ExternalModelsIndex); | ||
}, [currentModel.definitions.import]); | ||
|
||
const onRequestExternalModelByPath = useCallback<OnRequestExternalModelByPath>(async (path) => { | ||
return availableModelsByPath[path] ?? null; | ||
}, []); | ||
|
||
const onRequestExternalModelsAvailableToInclude = useCallback<OnRequestExternalModelsAvailableToInclude>(async () => { | ||
return Object.keys(availableModelsByPath); | ||
}, []); | ||
|
||
const onModelChange = useCallback<OnDmnModelChange>((model) => { | ||
setState((prev) => { | ||
const newStack = prev.stack.slice(0, prev.pointer + 1); | ||
return { | ||
...prev, | ||
stack: [...newStack, model], | ||
pointer: newStack.length, | ||
}; | ||
}); | ||
}, []); | ||
|
||
return ( | ||
<> | ||
{DmnEditorWrapper({ | ||
model: currentModel, | ||
originalVersion: args.originalVersion, | ||
onModelChange, | ||
onRequestExternalModelByPath, | ||
onRequestExternalModelsAvailableToInclude, | ||
externalModelsByNamespace: externalModelsByNamespace, | ||
externalContextName: args.externalContextName, | ||
externalContextDescription: args.externalContextDescription, | ||
validationMessages: args.validationMessages, | ||
evaluationResults: args.evaluationResults, | ||
issueTrackerHref: args.issueTrackerHref, | ||
})} | ||
</> | ||
); | ||
} | ||
|
||
const meta: Meta<DmnEditorProps> = { | ||
title: "Misc/EmptyWithAvailableExternalModels", | ||
component: DmnEditor, | ||
includeStories: /^[A-Z]/, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof EmptyStoryWithIncludedModels>; | ||
|
||
export const EmptyWithAvailableExternalModels: Story = { | ||
render: (args) => EmptyStoryWithIncludedModels(args), | ||
args: { | ||
model: getMarshaller(initialModel, { upgradeTo: "latest" }).parser.parse(), | ||
originalVersion: "1.5", | ||
evaluationResults: {}, | ||
externalContextDescription: "External context description", | ||
externalContextName: "Storybook - DMN Editor", | ||
externalModelsByNamespace: {}, | ||
issueTrackerHref: "", | ||
validationMessages: {}, | ||
}, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the full id. The ID of the
selectedNodes[i].data.dmnObject!["@_id"]!
does not contain the href to the external model, only the "internal" id, so inside the mutation we would have to look everywhere looking for where is theDecision
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
namespace#xsd:ID
"@_id"
attribute of objectsdefinitions["@_namespace"]