Add extended graph and dummy auto-layout #59#77
Add extended graph and dummy auto-layout #59#77handreyrc wants to merge 1 commit intoserverlessworkflow:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds SDK graph generation support to the diagram editor core by introducing extended graph types (node size/position, edge types/waypoints), exposing a buildGraph wrapper, and providing a dummy auto-layout implementation with accompanying tests/snapshots.
Changes:
- Added extended graph type definitions and edge-type enrichment (
solveEdgeTypes). - Exposed
buildGraphvia the core SDK wrapper and re-exported core modules. - Added a dummy
applyAutoLayoutimplementation plus integration/unit tests and snapshots.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/serverless-workflow-diagram-editor/tests/fixtures/workflows.ts | Added new JSON/YAML workflow fixtures used by graph + auto-layout tests. |
| packages/serverless-workflow-diagram-editor/tests/core/workflowSdk.integration.test.ts | Added integration coverage for buildGraph and extended graph typing/snapshot. |
| packages/serverless-workflow-diagram-editor/tests/core/graph.test.ts | New unit tests for edge type enrichment across default/error/condition/nested cases. |
| packages/serverless-workflow-diagram-editor/tests/core/autoLayout.integration.test.ts | New integration test verifying dummy auto-layout positions/sizes and snapshot. |
| packages/serverless-workflow-diagram-editor/tests/core/snapshots/workflowSdk.integration.test.ts.snap | Snapshot for buildGraph result. |
| packages/serverless-workflow-diagram-editor/tests/core/snapshots/autoLayout.integration.test.ts.snap | Snapshot for applyAutoLayout result. |
| packages/serverless-workflow-diagram-editor/src/core/workflowSdk.ts | Switched to SDK namespace import; added buildGraph wrapper returning extended graph. |
| packages/serverless-workflow-diagram-editor/src/core/index.ts | Re-exported graph and autoLayout modules. |
| packages/serverless-workflow-diagram-editor/src/core/graph.ts | Added extended graph types and solveEdgeTypes. |
| packages/serverless-workflow-diagram-editor/src/core/autoLayout.ts | Added dummy applyAutoLayout implementation to set size/position. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
lornakelly
left a comment
There was a problem hiding this comment.
Thanks for PR Handrey, nice work. Just a few comments
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Could you review this PR, please? |
fantonangeli
left a comment
There was a problem hiding this comment.
@handreyrc this is a placeholder for the future real code, but I would make the structure a bit stronger if you agree
There was a problem hiding this comment.
If these tests are testing buildGraph and parseWorkflow from packages/serverless-workflow-diagram-editor/src/core/workflowSdk.ts with the input from the SDK, I expected them to be in workflowSdk.integration.test.ts.
Otherwise I would suggest testing only solveEdgeTypes and have a better output about an error, if something gets broken.
I would prefer the 2n option. Wdyt?
There was a problem hiding this comment.
@fantonangeli,
The issue here is that there it no other way test 'solveEdgeTypes' without parsing a workflow and then call buildGraph from the SDK to get a loaded graph.
I created an external test function to get a graph from the SDK and then called 'solveEdgeTypes' in the tests to make them more specifc and easy to understand.
I also moved the buildGraph error to workflowSdk.integration.test.ts.
It looks better IMO.
WDYT?
There was a problem hiding this comment.
@handreyrc For instance, I was thinking to mocking the object like this for the first test:
const graph = {"id":"root","type":"root","entryNode":{"type":"start","id":"root-entry-node"},"exitNode":{"type":"end","id":"root-exit-node"},"nodes":[{"type":"start","id":"root-entry-node"},{"type":"end","id":"root-exit-node"},{"type":"set","id":"/do/0/step1","label":"step1"},{"type":"set","id":"/do/1/step2","label":"step2"},{"type":"set","id":"/do/2/step3","label":"step3"},{"type":"set","id":"/do/3/step4","label":"step4"},{"type":"set","id":"/do/4/step5","label":"step5"}],"edges":[{"label":"","id":"/do/4/step5-root-exit-node","sourceId":"/do/4/step5","destinationId":"root-exit-node"},{"label":"","id":"/do/3/step4-/do/4/step5","sourceId":"/do/3/step4","destinationId":"/do/4/step5"},{"label":"","id":"/do/2/step3-/do/3/step4","sourceId":"/do/2/step3","destinationId":"/do/3/step4"},{"label":"","id":"/do/1/step2-/do/2/step3","sourceId":"/do/1/step2","destinationId":"/do/2/step3"},{"label":"","id":"/do/0/step1-/do/1/step2","sourceId":"/do/0/step1","destinationId":"/do/1/step2"},{"label":"","id":"root-entry-node-/do/0/step1","sourceId":"root-entry-node","destinationId":"/do/0/step1"}]} as sdk.Graph;
There was a problem hiding this comment.
@fantonangeli,
We are relying on the sdk Graph, I would like those tests to break / throw exception as we update the sdk versions and things change. If we hardcode it we lose it.
WDYT?
There was a problem hiding this comment.
I think this kind of testing is more integration tests, already tested in: workflowSdk.integration.test.ts
In addition, using plain object in tests allow to modify them easily and test also broken scenarios in the future
| } | ||
|
|
||
| for (let i = 0; i < graph.nodes.length; i++) { | ||
| const graphNode = graph.nodes[i]! as ExtendedGraph; |
There was a problem hiding this comment.
In this point we are copying a memory reference, because graph.nodes[i]! is an object.
In general, as a good practice, we should clone the input without changing it.
Do you agree?
There was a problem hiding this comment.
The goal of the 'solveEdgeTypes' function is to traverse a brand new graph created by the sdk.buildGraph to set properties. As you can see 'solveEdgeTypes' is and must only be called from within the buildGraph wrapper in the workflowSdk.ts. It does not make sense to call it unless you have a brand new graph instance created straight from the skd, so I don't see any risk of messing with references elsewhere.
I exported 'solveEdgeTypes' in order to write unit tests for it.
If we really want to make a graph clone to work with, we should use lodash to create a deepclone before calling the recursive function 'solveEdgeTypes'. The graph is a pretty complex object to navigate so it may go prety deep and there are circular references (parent refs) to be handled.
WDYT?
There was a problem hiding this comment.
@handreyrc I tried locally using https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone#browser_compatibility , which is a native deep clone.
TBH I never used it before, but it passed the tests.
Anyway there can be a bit of performance slow down with big graphs and I can also agree with your point:
"The goal of the 'solveEdgeTypes' function is to traverse a brand new graph created by the sdk.buildGraph to set properties"
There was a problem hiding this comment.
Thanks for the tip! The structuredClone worked well!
Fixed!
| export function applyAutoLayout(graph: ExtendedGraph): ExtendedGraph { | ||
| // TODO: This is just a temporary implementation until the actual auto-layout engine is integrated | ||
| const nodeSize: Size = { height: 50, width: 70 }; | ||
| let position: Position = { x: 0, y: 0 }; | ||
|
|
||
| it("returns correct translation", () => { | ||
| const i18n = createI18n(dictionaries, "en"); | ||
| expect(i18n.t("save")).toBe("Save"); | ||
| // TODO: Containment is not supported for now. | ||
| graph.nodes.forEach((node) => { | ||
| node.size = { ...nodeSize }; | ||
| node.position = { ...position }; | ||
| position.y = position.y + 100; | ||
| }); | ||
|
|
||
| it("returns key if missing", () => { | ||
| const i18n = createI18n(dictionaries, "fr"); | ||
| expect(i18n.t("cancel")).toBe("cancel"); | ||
| }); | ||
| }); | ||
| return graph; |
There was a problem hiding this comment.
Still to preserve an immutability code structure, even if it's a kind of placeholder ATM.
Also ok to skip this suggestion
| export function applyAutoLayout(graph: ExtendedGraph): ExtendedGraph { | |
| // TODO: This is just a temporary implementation until the actual auto-layout engine is integrated | |
| const nodeSize: Size = { height: 50, width: 70 }; | |
| let position: Position = { x: 0, y: 0 }; | |
| it("returns correct translation", () => { | |
| const i18n = createI18n(dictionaries, "en"); | |
| expect(i18n.t("save")).toBe("Save"); | |
| // TODO: Containment is not supported for now. | |
| graph.nodes.forEach((node) => { | |
| node.size = { ...nodeSize }; | |
| node.position = { ...position }; | |
| position.y = position.y + 100; | |
| }); | |
| it("returns key if missing", () => { | |
| const i18n = createI18n(dictionaries, "fr"); | |
| expect(i18n.t("cancel")).toBe("cancel"); | |
| }); | |
| }); | |
| return graph; | |
| const nodeSize: Size = { height: 50, width: 70 }; | |
| const applyLayoutToNode = (node: ExtendedGraphNode) => ({ | |
| ...node, | |
| size: { ...nodeSize }, | |
| position: { x: 0, y: 100 }, | |
| }); | |
| export function applyAutoLayout(graph: ExtendedGraph): ExtendedGraph { | |
| // TODO: This is just a temporary implementation until the actual auto-layout engine is integrated | |
| // TODO: Containment is not supported for now. | |
| return { | |
| ...graph, | |
| nodes: graph.nodes.map(applyLayoutToNode), | |
| entryNode: applyLayoutToNode(graph.entryNode), | |
| exitNode: applyLayoutToNode(graph.exitNode), | |
| }; | |
| } | |
There was a problem hiding this comment.
@fantonangeli,
I agree that we need to return a new graph instance. I would leave as is because this code will be completely replaced as we move forward with ELKjs.
We may need lodash to handle it properly, we have to discuss about it.
WDYT?
There was a problem hiding this comment.
The code will be replaced and I can agree to leave as is.
Anyway I found a native method to clone objects like with lodash: #77 (comment)
There was a problem hiding this comment.
Fixed! Thanks
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: handreyrc <handrey.cunha@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 13 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@fantonangeli, Thanks! |
Closes #59
Summary
This PR adds dummy auto-layout and extended graph types.
Changes