@@ -2,19 +2,19 @@ import { graphlib } from "dagre-d3";
22import { v4 as uuidv4 } from "uuid" ;
33import * as R from "ramda" ;
44import { StepFunction , State , Operator , stringifyChoiceOperator } from "./stepFunction" ;
5+ import { getNodeOptions , getClusterOptions , getEdgeOptions , getMissingStyle } from "./graphStyles" ;
56
67const makeGroupName = ( ) => `Group_${ uuidv4 ( ) } ` ;
78const makeNodeName = ( ) => `Node_${ uuidv4 ( ) } ` ;
89
910const createMissingNodes = ( g : graphlib . Graph ) => {
10- const style = "fill: #ff0000;" ;
1111 const makeLabel = ( edgePointer ) => `${ edgePointer } (Missing)` ;
1212 g . edges ( ) . forEach ( ( edge ) => {
1313 if ( ! g . node ( edge . v ) ) {
14- g . setNode ( edge . v , { label : makeLabel ( edge . v ) , style } ) ;
14+ g . setNode ( edge . v , { label : makeLabel ( edge . v ) , style : getMissingStyle ( ) } ) ;
1515 }
1616 if ( ! g . node ( edge . w ) ) {
17- g . setNode ( edge . w , { label : makeLabel ( edge . w ) , style } ) ;
17+ g . setNode ( edge . w , { label : makeLabel ( edge . w ) , style : getMissingStyle ( ) } ) ;
1818 }
1919 } ) ;
2020 return g ;
@@ -31,21 +31,6 @@ const roundNodes = (g: graphlib.Graph) => {
3131 return g ;
3232} ;
3333
34- const stroke = "#999" ;
35- const red = "#a80d35" ;
36- const green = "#2BD62E" ;
37-
38- const getNodeOptions = ( state ) => {
39- switch ( state . Type ) {
40- case "Fail" :
41- return { style : `stroke: ${ red } ;` } ;
42- case "Succeed" :
43- return { style : `stroke: ${ green } ;` } ;
44- default :
45- return { } ;
46- }
47- } ;
48-
4934const isTerminalState = ( state : State ) => {
5035 return (
5136 ( state . End && state . Type !== "Parallel" && state . Type !== "Map" ) ||
@@ -56,6 +41,15 @@ const isTerminalState = (state: State) => {
5641
5742const serializeGraph = R . compose ( JSON . stringify , graphlib . json . write ) ;
5843
44+ const makeCluster = ( g : graphlib . Graph , state : State , parentClusterName : string ) => {
45+ const clusterName = makeGroupName ( ) ;
46+ g . setNode ( clusterName , getClusterOptions ( state ) ) ;
47+ if ( parentClusterName ) {
48+ g . setParent ( clusterName , parentClusterName ) ;
49+ }
50+ return clusterName ;
51+ } ;
52+
5953export const buildGraph = ( stepFunction : StepFunction ) => {
6054 const g = new graphlib . Graph ( { compound : true , multigraph : true } ) . setGraph ( { } ) . setDefaultEdgeLabel ( ( ) => ( { } ) ) ;
6155
@@ -65,12 +59,22 @@ export const buildGraph = (stepFunction: StepFunction) => {
6559 g . setNode ( startNodeName , { label : "Start" , shape : "circle" , style : "fill: #fcba03;" } ) ;
6660 g . setNode ( endNodeName , { label : "End" , shape : "circle" , style : "fill: #fcba03;" } ) ;
6761
68- const traverse = ( stepFunction : StepFunction , g : graphlib . Graph , groupName ?: string ) => {
62+ const traverse = (
63+ stepFunction : StepFunction ,
64+ g : graphlib . Graph ,
65+ parentClusterName ?: string ,
66+ fromState ?: string ,
67+ nextState ?: string
68+ ) => {
6969 const startAtName = stepFunction . StartAt ;
70- const isRootLevel = ! groupName ;
70+ const isRootLevel = ! parentClusterName ;
71+
72+ if ( fromState ) {
73+ g . setEdge ( fromState , startAtName ) ;
74+ }
7175
72- if ( groupName ) {
73- g . setParent ( startAtName , groupName ) ;
76+ if ( parentClusterName ) {
77+ g . setParent ( startAtName , parentClusterName ) ;
7478 }
7579
7680 const statesToAddToParent = new Set ( Object . keys ( stepFunction . States ) ) ;
@@ -84,77 +88,39 @@ export const buildGraph = (stepFunction: StepFunction) => {
8488
8589 switch ( state . Type ) {
8690 case "Parallel" : {
87- const newGroupName = makeGroupName ( ) ;
88- g . setNode ( newGroupName , {
89- label : "Parallel" ,
90- style : `stroke: ${ stroke } ; stroke-width: 2px; stroke-dasharray: 8, 4; rx: 5;` ,
91- clusterLabelPos : "top" ,
92- } ) ;
93- if ( groupName ) {
94- g . setParent ( newGroupName , groupName ) ;
95- }
96-
91+ const clusterName = makeCluster ( g , state , parentClusterName ) ;
9792 state . Branches . forEach ( ( branch ) => {
98- g . setEdge ( stateName , branch . StartAt ) ;
99- traverse ( branch , g , newGroupName ) ;
100-
101- R . toPairs ( branch . States )
102- . filter ( ( [ branchStateName , branchState ] ) => isTerminalState ( branchState ) )
103- . forEach ( ( [ branchStateName , branchState ] ) => g . setEdge ( branchStateName , state . Next || endNodeName ) ) ;
93+ traverse ( branch , g , clusterName , stateName , state . Next ) ;
10494 } ) ;
10595 break ;
10696 }
10797 case "Map" : {
108- const newGroupName = makeGroupName ( ) ;
109- g . setNode ( newGroupName , {
110- label : "Map" ,
111- style : `stroke: ${ stroke } ; stroke-width: 2px; stroke-dasharray: 16, 4; rx: 5;` ,
112- clusterLabelPos : "top" ,
113- } ) ;
114- if ( groupName ) {
115- g . setParent ( newGroupName , groupName ) ;
116- }
117- const branch = state . Iterator ;
118- g . setEdge ( stateName , branch . StartAt ) ;
119- traverse ( branch , g , newGroupName ) ;
120- R . toPairs ( branch . States )
121- . filter ( ( [ branchStateName , branchState ] ) => isTerminalState ( branchState ) )
122- . forEach ( ( [ branchStateName , branchState ] ) => g . setEdge ( branchStateName , state . Next || endNodeName ) ) ;
98+ const clusterName = makeCluster ( g , state , parentClusterName ) ;
99+ traverse ( state . Iterator , g , clusterName , stateName , state . Next ) ;
123100 break ;
124101 }
125102 case "Choice" : {
126103 if ( state . Choices ) {
127- const newGroupName = makeGroupName ( ) ;
128- g . setNode ( newGroupName , {
129- label : "Choice" ,
130- style : "fill: #d9dddc; rx: 5;" ,
131- clusterLabelPos : "top" ,
132- } ) ;
133-
134- if ( groupName ) {
135- g . setParent ( newGroupName , groupName ) ;
136- }
137-
138- const edgeOptions = { labelStyle : "font-style: italic;" } ;
104+ const clusterName = makeCluster ( g , state , parentClusterName ) ;
139105
140106 state . Choices . forEach ( ( choice : Operator ) => {
141107 const label = stringifyChoiceOperator ( choice ) ;
142- g . setEdge ( stateName , choice . Next , { label, ...edgeOptions } ) ;
143- g . setParent ( choice . Next , newGroupName ) ;
108+ g . setEdge ( stateName , choice . Next , { label, ...getEdgeOptions ( ) } ) ;
109+ g . setParent ( choice . Next , clusterName ) ;
144110 statesToAddToParent . delete ( choice . Next ) ;
145111 } ) ;
146112 if ( state . Default ) {
147113 const label = "Default" ;
148- g . setEdge ( stateName , state . Default , { label, ...edgeOptions } ) ;
149- g . setParent ( state . Default , newGroupName ) ;
114+ g . setEdge ( stateName , state . Default , { label, ...getEdgeOptions ( ) } ) ;
115+ g . setParent ( state . Default , clusterName ) ;
150116 statesToAddToParent . delete ( state . Default ) ;
151117 }
152118 }
153119 break ;
154120 }
155121 default : {
156- if ( isTerminalState ( state ) && isRootLevel ) {
157- g . setEdge ( stateName , endNodeName ) ;
122+ if ( isTerminalState ( state ) ) {
123+ g . setEdge ( stateName , nextState || endNodeName ) ;
158124 }
159125 if ( state . Next ) {
160126 g . setEdge ( stateName , state . Next ) ;
@@ -165,19 +131,19 @@ export const buildGraph = (stepFunction: StepFunction) => {
165131 if ( state . Catch ) {
166132 state . Catch . forEach ( ( catcher ) => {
167133 const label = ( catcher . ErrorEquals || [ ] ) . join ( " or " ) ;
168- g . setEdge ( stateName , catcher . Next , { label, labelStyle : "font-style: italic;" } ) ;
134+ g . setEdge ( stateName , catcher . Next , { label, ... getEdgeOptions ( ) } ) ;
169135 } ) ;
170136 }
171137 if ( state . Retry ) {
172138 const conditionsLength = ( state . Retry || [ ] ) . length ;
173139 const label = `(${ conditionsLength } condition${ conditionsLength > 1 ? "s" : "" } )` ;
174- g . setEdge ( stateName , stateName , { label, labelStyle : "font-style: italic;" } ) ;
140+ g . setEdge ( stateName , stateName , { label, ... getEdgeOptions ( ) } ) ;
175141 }
176142 } ) ;
177143
178- if ( groupName ) {
144+ if ( parentClusterName ) {
179145 [ ...statesToAddToParent ] . forEach ( ( stateName ) => {
180- g . setParent ( stateName , groupName ) ;
146+ g . setParent ( stateName , parentClusterName ) ;
181147 } ) ;
182148 }
183149
0 commit comments