-
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
Conversation
…to add external Decisions to a Decision Service.
More testes for you, @ljmotta 😄 |
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 comment
The 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 comment
The 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"?
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.
Thanks for the PR @danielzhe! Made some change requests inline, please consider them.
}: { | ||
definitions: Normalized<DMN15__tDefinitions>; | ||
decisionId: string; | ||
drgElement: DrgElement; | ||
decisionServiceId: string; | ||
drdIndex: number; | ||
snapGrid: SnapGrid; | ||
decisionShape: Normalized<DMNDI15__DMNShape>; | ||
elementId: string; | ||
}) { |
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.
elementId
--> decisionId
I understand drgElement
doesn't necessarily is inside definitions
, so please make this action dependent on external models too. There's no way to know whether or not drgElement
and elementId
are aligned...
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.
In the previous code we was getting the ID, in Diagram.tsx
using the following code:
decisionId: selectedNodes[i].data.dmnObject!["@_id"]!, // We can assume that all selected nodes are Decisions because the contaiment was validated above.
So I think it is safe to assume that selectedNodes[i].data.dmnObject
is always a Decision
which is the DrgElement
that we looking for. Maybe I should change the type of DrgElement
I defined here
to:
export type DecisionDrgElement = Normalized<{ __$$element: "decision" } & DMN15__tDecision>
About the ID, I can't see any case where the ID of the selected node will be different than the DRG, except for the imported nodes the ID in the DRG is not full, it is only the local ID (the Guid), but from selectedNodes[I].["@_id"]
is the real ID used in the current diagram (namespace + id).
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.
In fact, we don't have DRG element always in the definitions: the external nodes do not have DRG element, only DMNDI. The DRG we have in selectedNodes[i].data.dmnObject
is a loaded DRG from the external model, that's why its ID is different to the internal one, the one in href.
See below:
<decisionService name="New Decision Service" id="_A0908B2B-DBE7-41C7-B055-0C0DAD17CE54">
<variable name="New Decision Service" id="_145CD7A7-B14D-4D63-B9D3-85601FC915B1" />
<outputDecision href="https://kie.apache.org/dmn/_D19B0015-2CBD-4BA8-84A9-5F554D84A9E1#_05621ED4-9236-47F1-B93A-164A4527B136" />
<encapsulatedDecision href="https://kie.apache.org/dmn/_D19B0015-2CBD-4BA8-84A9-5F554D84A9E1#_1991FB34-1253-4A54-AD3D-89697938DDFA" />
<encapsulatedDecision href="#_8E89DDF6-7ABC-4FBF-A1B6-B68BC32A52AE" />
</decisionService>
<decision name="New Decision" id="_8E89DDF6-7ABC-4FBF-A1B6-B68BC32A52AE">
<variable name="New Decision" id="_BFB8A43B-5ADA-47D8-815E-8A0505814AA1" />
</decision>
Notice only the 8E89DDF6-7ABC-4FBF-A1B6-B68BC32A52AE
is present in the file.
The other nodes are not, only as a dmndi
.
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.
We only need the ID, but we need to know that this ID represents a Decision. The only way to know that is having the externalModelsByNamespace passed as parameter to the "addDecisionToDecisionService" mutation, so we can make that validation. You're mentioning the arguments you're passing when calling this mutation, but the mutation itself, after your changes, can be called with
{
elementId: "bar"
drgElement: {
"@_id": "foo"
...
}
}
which has the potential of being wrong. What I'm saying is that we rollback to the way it was before, and we include the externalModelsByNamespace
parameters so we can look if that href (nmspc#id
) represents a Decision on the model with namespace nmspc
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.
I think got your point! I guarantee in the Diagram.tsx
that the ID represents a decision, but I am not doing the same inside the mutation, in other words, the mutation is not "safe" anymore, right?
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.
Yes, you can guarantee that you're calling it correctly now, but you can't guarantee that this will be true forever :P
<div | ||
className={"kie-dmn-editor--palette-nodes-popover"} | ||
style={{ maxHeight }} | ||
data-testid={"kie-tools--dmn-editor--external-nodes-container"} | ||
> |
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.
The "DRG nodes" panel doesn't have a data-testid
, why don't we use the same strategy we're using for it?
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.
Do we have tests for "DRG Nodes" panel? I only found for drag nodes from the palette not from the "DRG Nodes" panel. 🤔
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.
@tiagobento actually we are about to add data-testid
also for the DRG Nodes
panel, see the PR #2462
One thing we could unify is the data-testid
suffix. @danielzhe is using -container
while me is using -popover
, what do you prefer? I chose -popover
due to related className
.
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.
All good then! Thanks for the insights. I have no preference for the suffix, just please coordinate that it is consistent. @jomarko @danielzhe
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.
I'll change it to -popover
since it is related to the className
and makes more sense! Thank you, @jomarko
decisionShape: selectedNodes[i].data.shape, | ||
elementId: selectedNodes[i].id, // The "real" id is here, which can be the local id or an imported node id (uri + external element id). |
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.
Please see my comment about the mutation itself...
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.
Can we rename this to externalNodes
? I mean, this is the first test we have on that "domain".
await editor.changeTab({ tab: TabName.EDITOR }); | ||
await palette.dragNewNode({ type: NodeType.DECISION_SERVICE, targetPosition: { x: 400, y: 200 } }); | ||
|
||
await palette.getExternalNodesButton().click(); |
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.
I think this is leaking internal details... I would expect something like palette.openExternalNodesPanel()
.
test("add to a Decision Service", async ({ editor, page, palette, diagram, nodes, includedModels }) => { | ||
await editor.changeTab({ tab: TabName.INCLUDED_MODELS }); | ||
|
||
await includedModels.getIncludeModelButton().click(); |
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.
I think this is leaking internal details... I would expect something like includedModels.addNew(). Not sure how this is being done in other parts of our test suite, though.
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.
I did as you suggested at first, but then I found in other tests that we were using this way, getting the button and then calling the click(), like here, for example, and changed to this "clicky" approach. I will change it back. 😄
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.
Yeah, I like it better when the actual screen elements are not exposed, to be honest, since that's how we're doing for the other parts of our tests.
}); | ||
|
||
// We click on the button again to close the external nodes panel. | ||
await palette.getExternalNodesButton().click(); |
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.
Same? Maybe instead of openExternalNodesPanel
you could call it toggleExternalNodesPanel
// We move it to make it sure that the nodes are added inside the Decision Services | ||
// and are really attached to it, not only "visually over it". | ||
await nodes.move({ name: DefaultNodeName.DECISION_SERVICE, targetPosition: { x: 120, y: 120 } }); |
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.
Great comment.
|
||
await expect(diagram.get()).toHaveScreenshot("add-included-node-inside-decision-service.png"); | ||
}); | ||
}); |
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.
Please add more tests to the basic operations, like changing from one DS to another. Removing an external Decision from a DS, things like that.
// Decision Services only have some draggable areas near the borders. | ||
// If you drag it to the center, you'll drag the divide line. | ||
// Also, neither the entire upper area nor the entire downer area is draggable. | ||
await this.get({ name: args.name }).dragTo(this.diagram.get(), { | ||
targetPosition: args.targetPosition, | ||
sourcePosition: { x: 20, y: 20 }, | ||
}); |
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.
Great comment also.
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.
Unfortunately, we have one more problem, that node.move()
can be invoked also for a decision service node, that is already renamed and is completely different from DefaultNodeName.DECISION_SERVICE
I think we should also document that the decision service move will work only for 'default name' decision service nodes.
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.
Good point @jomarko. @danielzhe I think we can update this to have a flag "grabAtBorders" that we use as true
when moving Decision Services around.
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.
I made a recent change where the name must now contain DefaultNodeName.DECISION_SERVICE
, but not exactly it. But I agree, it isn't good to rely on the name. What we can do? A separate "move" for Decision Service or add a doc in the code?
We also can just change the move
to always get the node from a corner not from the middle. Then it will work for every node. Can add an optional parameter where the user can add the sourcePosition
also. I think this is the best option. What do you guys think? @jomarko @tiagobento
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.
public async move(args: { grabAtBorder?: boolean; targetPosition: { x: number; y: number } }) {
if (args.grabAtBorder ?? false) {
// Decision Services only have some draggable areas near the borders.
// If you drag it to the center, you'll drag the divide line.
// Also, neither the entire upper area nor the entire downer area is draggable.
await this.get({ name: args.name }).dragTo(this.diagram.get(), {
targetPosition: args.targetPosition,
sourcePosition: { x: 20, y: 20 },
});
} else {
await this.get({ name: args.name }).dragTo(this.diagram.get(), {
targetPosition: args.targetPosition,
});
}
}
I will review this tomorrow. |
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.
Some comments inline, however manual check passed and the feature works well!
@@ -159,6 +159,7 @@ export function ExternalNodesPanel() { | |||
externalDrgElementId: drgElement["@_id"]!, | |||
}) | |||
} | |||
data-testid={`kie-tools--dmn-editor--external-node-${_import["@_name"]}-${drgElement["@_name"]}`} |
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.
Is ${_import["@_name"]} always non empty? I mean containing some non white characters?
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.
No, it can be an empty string. Great catch.
// Decision Services only have some draggable areas near the borders. | ||
// If you drag it to the center, you'll drag the divide line. | ||
// Also, neither the entire upper area nor the entire downer area is draggable. | ||
await this.get({ name: args.name }).dragTo(this.diagram.get(), { | ||
targetPosition: args.targetPosition, | ||
sourcePosition: { x: 20, y: 20 }, | ||
}); |
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.
Unfortunately, we have one more problem, that node.move()
can be invoked also for a decision service node, that is already renamed and is completely different from DefaultNodeName.DECISION_SERVICE
I think we should also document that the decision service move will work only for 'default name' decision service nodes.
public async dragExternalNode(args: { | ||
includedModelName: string; | ||
nodeName: string; | ||
targetPosition: { x: number; y: number }; | ||
}) { | ||
await this.page | ||
.getByTestId("kie-tools--dmn-editor--external-nodes-container") | ||
.getByTestId(`kie-tools--dmn-editor--external-node-${args.includedModelName}-${args.nodeName}`) | ||
.dragTo(this.diagram.get(), { targetPosition: args.targetPosition }); | ||
} | ||
|
||
public async toggleExternalNodesPanel() { | ||
await this.page.getByRole("button", { name: "External nodes" }).click(); | ||
} |
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.
In my PR #2462, I added similar as separate fixture drgNodes.ts
. Should I also add add it into palette.ts
instead of a separate fixture file?
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.
I moved to palette because we understand the "palette" as all those buttons on the left of the screen. So I think the toggle should be done in the palette.
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.
Maybe it is not so clear , if the MY_MODEL_Diff
is really out of the decision service node. Could we maybe move it a little bit more to make it clear? Or is this something like corner case test?
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.
Agree, this screenshot is kind of ambiguous...
await includedModels.fillModelToInclude({ modelName: "sumDiffDs.dmn" }); | ||
await includedModels.selectModel({ modelName: "sumDiffDs.dmn" }); |
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.
Why both fillModelToInclude
and selectModel
are needed please?
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.
One "types" the string, and the other picks it from the list.
data-testid renamed. General code review. Typo fix.
@@ -992,8 +992,9 @@ export const Diagram = React.forwardRef<DiagramRef, { container: React.RefObject | |||
for (let i = 0; i < selectedNodes.length; i++) { | |||
deleteDecisionFromDecisionService({ | |||
definitions: state.dmn.model.definitions, | |||
decisionId: selectedNodes[i].data.dmnObject!["@_id"]!, // We can assume that all selected nodes are Decisions because the contaiment was validated above. | |||
decisionId: selectedNodes[i].id, // We can assume that all selected nodes are Decisions because the contaiment was validated above. |
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 the Decision
.
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.
- "Full ID" = href
- href =
namespace#xsd:ID
- xsd:ID = typically the
"@_id"
attribute of objects - namespace =
definitions["@_namespace"]
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 comment
The reason will be displayed to describe this comment to others. Learn more.
So decisionId
can be in any format here:
#id
uri#id
id
But, if it is an external node, we only be able to find it if the full id is passed: uri#id
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.
Good point. I think we better rename decisionId
to decisionHref
.
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.
...and the id
of a node is an href.... please see apache/incubator-kie-issues#983.... :x
@tiagobento @jomarko Changes applied! Ready for you to continue. |
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.
Thanks @danielzhe! This is looking great. Few last comments from my side.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Nice
const xmlHref = buildXmlHref({ namespace: href.namespace, id: href.id }); | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Nice
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 comment
The 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"?
packages/dmn-editor/src/mutations/addDecisionToDecisionService.ts
Outdated
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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very good topic.
I like to split in variables because I find it easier to "think" step-by-step and have less complexity per line.
Also, in this case, I'm giving "meaning" to what (externalModel.model as Normalized<DmnLatestModel>).definitions.drgElement
: it is the "external drgs". So I don't have to care where it comes from in line 59 or what it is. It is the "external drgs that come from somewhere". Ok, "somewhere" is just a line above, but in my mind, looking at line 59, it is a "problem solved" that I don't have to care about anymore.
But the main point is that if someone new looks at the code it can easily get what (externalModel.model as Normalized<DmnLatestModel>).definitions.drgElement
means.
packages/dmn-editor/src/mutations/deleteDecisionFromDecisionService.ts
Outdated
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Also same as above.
Co-authored-by: Tiago Bento <[email protected]>
Co-authored-by: Tiago Bento <[email protected]>
…rvice.ts Co-authored-by: Tiago Bento <[email protected]>
Sorry, I get lost in the conversation, since my approval it seems more code landed in. Should I do a re-reiew? |
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.
I've made some comments inline. One tip regarding objects. When the object property and value has the same name you could short it just the property name:
drgElementsByNamespace: drgElementsByNamespace,
externalModelsByNamespace: externalModelsByNamespace,
drgElementsByNamespace,
externalModelsByNamespace,
Also, all test methods are with the page
in their signatures, but none use it.
Example:
async ({ editor, page, palette, diagram, nodes, includedModels })
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
The decisionHref
isn't the same as the hrefString
?
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.
No. The decisionHref
can be something like uri#id
, id
, or even #id
.
The buildXmlHref
makes sure that we have uri#id
or #id
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.
A href
will always have the the #
on its structure. If it's possible to have a href
variable without it (an id
value), the variable is wrongly named. Now, looking at the source of this value, it comes from the computeDiagramData.ts
file which creates the selectedNodesById
map. The map id
is a value made with the buildXmlHref
, so all ids are href
, meaning the decisionHref
is correctly named.
I think this is a problem in our codebase, where we mix the id
and the href
, and at some point we need to address this. FYI @tiagobento
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.
@danielzhe @tiagobento how are we going to proceed here?
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.
@danielzhe Please use decisionHref
where hrefString
is currently being used, since they are exactly the same string.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
The same here. xmlHref
will not have the same value as the decisionHref
?
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.
Same answer as above.
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.
xmlHref
is exactly the same as href
... you're even building it with the same values. No need to have this xmlHref
variable...
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}'` | ||
); | ||
} |
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.
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 decision === undefined
the element doesn't exist message. And the decision.__$$element !== "decision"
the error telling isn't a Decision. WDYT?
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.
I'm following the existing error message patterns used in this mutation and in deleteDecisionFromDecisionService
. In my opinion, we will only be adding more code to do something that we can do in fewer lines.
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}'` | ||
); | ||
} |
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.
The same with this error.
for (const hrefString of hrefsToDecisionsInsideDecisionService) { | ||
const href = parseXmlHref(hrefString); | ||
const externalModel = externalModelsByNamespace?.[href.namespace ?? ""]; | ||
if (externalModel) { | ||
const externalDecision = (externalModel?.model as Normalized<DmnLatestModel>).definitions.drgElement?.find( | ||
(drgElement) => drgElement["@_id"] === href.id | ||
) as Normalized<DMN15__tDecision>; | ||
|
||
if (externalDecision) { | ||
(externalDecision.informationRequirement ?? []).flatMap((ir) => { | ||
if (ir.requiredDecision) { | ||
const externalHref = parseXmlHref(ir.requiredDecision["@_href"]); | ||
// If the requiredDecision has namespace, it means that it is pointing to a node in a 3rd model, | ||
// not this one (the local model) neither the model in the `href.namespace`. | ||
if (externalHref.namespace) { | ||
requirements.set(`${ir.requiredDecision["@_href"]}`, "decisionIr"); | ||
} else { | ||
requirements.set(`${href.namespace}${ir.requiredDecision["@_href"]}`, "decisionIr"); | ||
} | ||
} else if (ir.requiredInput) { | ||
// If the requiredInput has namespace, it means that it is pointing to a node in a 3rd model, | ||
// not this one (the local model) neither the model in the `href.namespace`. | ||
const externalHref = parseXmlHref(ir.requiredInput["@_href"]); | ||
if (externalHref.namespace) { | ||
requirements.set(`${ir.requiredInput["@_href"]}`, "inputDataIr"); | ||
} else { | ||
requirements.set(`${href.namespace}${ir.requiredInput["@_href"]}`, "inputDataIr"); | ||
} | ||
} else { | ||
throw new Error( | ||
`DMN MUTATION: Invalid information requirement referenced by external DecisionService: '${externalDecision["@_id"]}'` | ||
); | ||
} | ||
}); | ||
} | ||
} | ||
} |
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.
Adding just a suggestion here. The usage of guard clauses to reduce nesting:
if (externalModel === undefined) {
continue;
}
const externalDecision = (externalModel.model as Normalized<DmnLatestModel>).definitions.drgElement?.find((drgElement) => drgElement["@_id"] === href.id) as Normalized<DMN15__tDecision>;
if (externalDecision === undefined) {
continue;
}
...
Also, why did you used the flatMap
instead of forEach
?
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.
The code with flatMap
is a copy-paste of the same code a few lines above, but it uses the drgElement
and has a simpler logic. Do you want me to change both?
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.
Good point (the nesting)!
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.
Yes, please. It doesn't make sense to use flatMap
if we're not retuning the value.
stories: async ({ baseURL, page }, use) => { | ||
await use(new Stories(page, baseURL)); | ||
}, | ||
includedModels: async ({ baseURL, page }, use) => { |
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.
Unused baseUrl
I'm aware of this. If I didn't do it probably I just overlooked it or it was something that was renamed. 😄 @ljmotta I'm checking the other things you reported. Thank you. |
Changes, applied, @ljmotta |
@@ -471,6 +472,7 @@ function IncludedModelCard({ | |||
definitions: state.dmn.model.definitions, | |||
__readonly_index: index, | |||
__readonly_externalModelTypesByNamespace: externalModelTypesByNamespace, | |||
externalModelsByNamespace, |
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.
__readonly_externalModelsByNamespace
@@ -876,6 +879,7 @@ export const Diagram = React.forwardRef<DiagramRef, { container: React.RefObject | |||
__readonly_externalModelTypesByNamespace: state | |||
.computed(state) | |||
.getExternalModelTypesByNamespace(externalModelsByNamespace), | |||
externalModelsByNamespace, |
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.
__readonly_externalModelsByNamespace
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 comment
The reason will be displayed to describe this comment to others. Learn more.
xmlHref
is exactly the same as href
... you're even building it with the same values. No need to have this xmlHref
variable...
packages/dmn-editor/src/mutations/repopulateInputDataAndDecisionsOnDecisionService.ts
Outdated
Show resolved
Hide resolved
packages/dmn-editor/src/mutations/repopulateInputDataAndDecisionsOnDecisionService.ts
Outdated
Show resolved
Hide resolved
loadDependentModels( | ||
includedModel, | ||
externalModelsIndex, | ||
resourcesByNamespace, | ||
loadedDmnsByPathRelativeToTheWorkspaceRoot, | ||
thisDmnsNormalizedPosixPathRelativeToTheWorkspaceRoot | ||
); |
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 useCallback
depends on itself.. This is not very good I'd say. Not even sure how ESLint didn't complain about this, as you're inhenritenly depending on loadDependentModels
from a previous tick (I.e., outdated getIncludedNamespacesFromModel
).
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.
Great changes, the rename helped a lot. I've made some comments inline regarding error messages and minor things.
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
@danielzhe @tiagobento how are we going to proceed here?
@@ -20,21 +20,45 @@ | |||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
unused import
|
||
const nodeTypeTooltipDescription = useMemo(() => { | ||
if (dmnObject === undefined) { | ||
throw new Error("nodeTypeDescription can't be defined without a DMN object"); |
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.
The nodeTypeDescription
and DMN Object
doesn't make much sense in the error message. The dmnObject
is a drgElement
, I'm not sure about the context behind this name, maybe @tiagobento could help us here. "Can't define the tooltip description without a DRG element" WDYT?
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.
Yeah, the name dmnObject
is definitely sub-optimal. That's on me, though. We just need to be careful to not crash the editor over an error like this. Let's define to the "Unknown" string and remove both throw
s please.
} | ||
const nodeType = getNodeTypeFromDmnObject(dmnObject); | ||
if (nodeType === undefined) { | ||
throw new Error("Can't determine nodeTypeDescription with undefined node type"); |
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.
The same here. "Can't define the tooltip description without the node type" WDYT?
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.
Same as above. @danielzhe
I found some interesting cases doing some manual checkings:
Recording.2024-09-18.212657.mp4
Recording.2024-09-18.212950.mp4 |
Co-authored-by: Luiz João Motta <[email protected]>
@ljmotta Can you please discriminate between what's already on |
@danielzhe I would double-check those too, just to be sure. |
@tiagobento Both, 1 and 2 are present on |
Recording.2024-09-26.175024.mp4 |
moving.tests.mp4 |
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.
@danielzhe I've done some manual checks, and I couldn't find any new bug related to this PR. Thanks for your patience
Closes: apache/incubator-kie-issues#1298
ds.mp4