Skip to content
Merged
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
112 changes: 33 additions & 79 deletions packages/serverless-workflow-diagram-editor/src/core/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,94 +14,48 @@
* limitations under the License.
*/

import { Graph, GraphEdge, GraphNode, GraphNodeType } from "@serverlessworkflow/sdk";
import { FlatGraph, FlatGraphNode, GraphNodeType } from "@serverlessworkflow/sdk";

// Override / add multiple properties of a type in a generic way
export type Override<T, NewProps> = Omit<T, keyof NewProps> & NewProps;

// Supported edge types
export enum GraphEdgeType {
Default = "default",
Error = "error",
Condition = "condition",
export function getNodesByType(graph: FlatGraph, type: GraphNodeType): FlatGraphNode[] {
return graph.nodes.filter((node) => node.type === type);
}

export type Point = {
x: number;
y: number;
};

export type Position = Point;

export type Size = {
height: number;
width: number;
};

export type WayPoints = Point[];
// Inner entry and exit nodes cannot be connected external nodes so connections shall be moved to parent node
export function fixNodesConnections(graph: FlatGraph): FlatGraph {
const entryNodes = getNodesByType(graph, GraphNodeType.Entry);
const exitNodes = getNodesByType(graph, GraphNodeType.Exit);

// Add extra properties to GraphNode
export type ExtendedGraphNode = Override<
GraphNode,
{
position?: Position;
size?: Size;
}
>;

// Add extra properties to GraphEdge
export type ExtendedGraphEdge = GraphEdge & {
type?: GraphEdgeType;
wayPoints?: WayPoints;
};
// Build maps of {entryNodeId -> parentId} and {exitNodeId -> parentId}
const entryNodeToParent = new Map<string, string>();
entryNodes.forEach((node) => {
if (node.parentId) {
entryNodeToParent.set(node.id, node.parentId);
}
});

export type ExtendedGraph = Override<
Graph,
{
parent?: ExtendedGraph | null;
nodes: ExtendedGraphNode[];
edges: ExtendedGraphEdge[];
entryNode: ExtendedGraphNode;
exitNode: ExtendedGraphNode;
}
>;
const exitNodeToParent = new Map<string, string>();
exitNodes.forEach((node) => {
if (node.parentId) {
exitNodeToParent.set(node.id, node.parentId);
}
});

export function solveEdgeTypes(graph: ExtendedGraph): ExtendedGraph {
const graphClone = structuredClone(graph);
Comment thread
handreyrc marked this conversation as resolved.

// root level
setEdgeTypes(graphClone);
// children n level
graphClone.nodes.flat().forEach((node) => setEdgeTypes(node as ExtendedGraph));

return graphClone;
}

function setEdgeTypes(graph: ExtendedGraph): ExtendedGraph {
if (!graph.edges || !graph.nodes) {
return graph;
}

for (let i = 0; i < graph.nodes.length; i++) {
const graphNode = graph.nodes[i]! as ExtendedGraph;

for (let j = 0; j < graph.edges.length; j++) {
const graphEdge = graph.edges[j]!;
// Single pass over edges to rewrite sourceId/targetId
graphClone.edges.forEach((edge) => {
// Move entry node incoming connections to parent
const entryParent = entryNodeToParent.get(edge.targetId);
if (entryParent) {
edge.targetId = entryParent;
}

if (graphNode.id === graphEdge.sourceId) {
switch (graphNode.type) {
case GraphNodeType.Raise:
graphEdge.type = GraphEdgeType.Error;
break;
case GraphNodeType.Switch:
graphEdge.type = GraphEdgeType.Condition;
break;
default:
graphEdge.type = GraphEdgeType.Default;
}
}
// Move exit node outgoing connections to parent
const exitParent = exitNodeToParent.get(edge.sourceId);
if (exitParent) {
edge.sourceId = exitParent;
}
}
});
Comment thread
handreyrc marked this conversation as resolved.

return graph;
return graphClone;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,3 @@

export * from "./workflowSdk";
export * from "./graph";
export * from "./autoLayout";
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import yaml from "js-yaml";
import * as sdk from "@serverlessworkflow/sdk";
import { ExtendedGraph, solveEdgeTypes } from "./graph";
import { fixNodesConnections } from "./graph";

export type WorkflowParseResult = {
model: sdk.Specification.Workflow | null;
Expand Down Expand Up @@ -55,6 +55,6 @@ export function parseWorkflow(text: string): WorkflowParseResult {
return { model, errors };
}

export function buildGraph(model: sdk.Specification.Workflow): ExtendedGraph {
return solveEdgeTypes(sdk.buildGraph(model));
export function buildFlatGraph(model: sdk.Specification.Workflow): sdk.FlatGraph {
return fixNodesConnections(sdk.buildFlatGraph(model));
}
Loading
Loading