@@ -61,6 +61,7 @@ public class WorkflowGraphBuilder(ILogger<WorkflowGraphBuilder> logger, IYamlSer
6161 public IGraphViewModel Build ( WorkflowDefinition workflow )
6262 {
6363 ArgumentNullException . ThrowIfNull ( workflow ) ;
64+ this . Logger . LogTrace ( "Starting WorkflowGraphBuilder.Build" ) ;
6465 Stopwatch sw = Stopwatch . StartNew ( ) ;
6566 var graph = new GraphViewModel ( ) ;
6667 var startNode = this . BuildStartNode ( ) ;
@@ -95,9 +96,15 @@ protected virtual INodeViewModel GetNodeAnchor(INodeViewModel node, NodePortType
9596 /// <param name="currentTask">The current task</param>
9697 /// <param name="transition">A specific transition, if any (use for switch cases)</param>
9798 /// <returns>The next task identity</returns>
98- protected virtual TaskIdentity ? GetNextTask ( Map < string , TaskDefinition > tasksList , string ? currentTask , string ? transition = null )
99+ protected virtual TaskIdentity GetNextTask ( Map < string , TaskDefinition > tasksList , string ? currentTask , string ? transition = null )
99100 {
100- if ( transition == FlowDirective . End || transition == FlowDirective . Exit ) return null ;
101+ ArgumentNullException . ThrowIfNull ( tasksList ) ;
102+ var taskDefinition = tasksList . FirstOrDefault ( taskEntry => taskEntry . Key == currentTask ) ? . Value ;
103+ transition = ! string . IsNullOrWhiteSpace ( transition ) ? transition : taskDefinition != null ? taskDefinition . Then : null ;
104+ if ( transition == FlowDirective . End || transition == FlowDirective . Exit )
105+ {
106+ return new TaskIdentity ( transition , - 1 , null ) ;
107+ }
101108 int index ;
102109 if ( ! string . IsNullOrWhiteSpace ( transition ) && transition != FlowDirective . Continue )
103110 {
@@ -108,7 +115,7 @@ protected virtual INodeViewModel GetNodeAnchor(INodeViewModel node, NodePortType
108115 index = tasksList . Keys . ToList ( ) . IndexOf ( currentTask ) + 1 ;
109116 if ( index >= tasksList . Count )
110117 {
111- return null ;
118+ return new TaskIdentity ( FlowDirective . Exit , - 1 , null ) ;
112119 }
113120 }
114121 else
@@ -129,26 +136,45 @@ protected virtual INodeViewModel GetNodeAnchor(INodeViewModel node, NodePortType
129136 /// <param name="context">The rendering context of the provided node</param>
130137 protected virtual void BuildTransitions ( INodeViewModel node , TaskNodeRenderingContext context )
131138 {
132- List < TaskIdentity ? > transitions = [ ] ;
133- TaskIdentity ? nextTask = this . GetNextTask ( context . TasksList , context . TaskName ) ;
139+ ArgumentNullException . ThrowIfNull ( node ) ;
140+ ArgumentNullException . ThrowIfNull ( context ) ;
141+ this . Logger . LogTrace ( $ "Starting WorkflowGraphBuilder.BuildTransitions from '{ node . Id } '") ;
142+ List < TaskIdentity > transitions = [ ] ;
143+ TaskIdentity nextTask = this . GetNextTask ( context . TasksList , context . TaskName ) ;
134144 transitions . Add ( nextTask ) ;
135- while ( ! string . IsNullOrWhiteSpace ( nextTask ? . Definition . If ) )
145+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] found transition to '{ nextTask ? . Name } '") ;
146+ while ( ! string . IsNullOrWhiteSpace ( nextTask ? . Definition ? . If ) )
136147 {
148+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] if clause found, looking up next task.") ;
137149 nextTask = this . GetNextTask ( context . TasksList , nextTask . Name ) ;
138150 transitions . Add ( nextTask ) ;
151+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] found transition to '{ nextTask ? . Name } '") ;
139152 }
140- foreach ( var transition in transitions . Distinct ( ) )
153+ foreach ( var transition in transitions . Distinct ( new TaskIdentityComparer ( ) ) )
141154 {
142- if ( transition != null )
155+ if ( transition . Index != - 1 )
143156 {
157+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] Building node '{ transition . Name } '") ;
144158 var transitionNode = this . BuildTaskNode ( new ( context . Workflow , context . Graph , context . TasksList , transition . Index , transition . Name , transition . Definition , context . TaskGroup , context . ParentReference , context . ParentContext , context . EntryNode , context . ExitNode ) ) ;
159+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] Building edge to node '{ transition . Name } '") ;
145160 this . BuildEdge ( context . Graph , this . GetNodeAnchor ( node , NodePortType . Exit ) , GetNodeAnchor ( transitionNode , NodePortType . Entry ) ) ;
146161 }
147- else
162+ else if ( transition . Name == FlowDirective . Exit )
148163 {
164+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] Exit transition, building edge to node '{ context . ExitNode } '") ;
149165 this . BuildEdge ( context . Graph , this . GetNodeAnchor ( node , NodePortType . Exit ) , context . ExitNode ) ;
150166 }
167+ else if ( transition . Name == FlowDirective . End )
168+ {
169+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] End transition, building edge to node '{ context . ExitNode } '") ;
170+ this . BuildEdge ( context . Graph , this . GetNodeAnchor ( node , NodePortType . Exit ) , context . Graph . AllNodes . Skip ( 1 ) . First ( ) . Value ) ;
171+ }
172+ else
173+ {
174+ throw new IndexOutOfRangeException ( "Invalid transition" ) ;
175+ }
151176 }
177+ this . Logger . LogTrace ( $ "Exiting WorkflowGraphBuilder.BuildTransitions from '{ node . Id } '") ;
152178 }
153179
154180 /// <summary>
@@ -165,12 +191,15 @@ protected virtual void BuildTransitions(INodeViewModel node, TaskNodeRenderingCo
165191 protected INodeViewModel BuildTaskNode ( TaskNodeRenderingContext context )
166192 {
167193 ArgumentNullException . ThrowIfNull ( context ) ;
194+ this . Logger . LogTrace ( $ "Starting WorkflowGraphBuilder.BuildTaskNode for { context . TaskName } ") ;
168195 if ( context . Graph . AllNodes . ContainsKey ( context . TaskReference ) )
169196 {
197+ this . Logger . LogTrace ( $ "Exiting WorkflowGraphBuilder.BuildTaskNode for { context . TaskName } , found existing node.") ;
170198 return context . Graph . AllNodes [ context . TaskReference ] ;
171199 }
172200 if ( context . Graph . AllClusters . ContainsKey ( context . TaskReference ) )
173201 {
202+ this . Logger . LogTrace ( $ "Exiting WorkflowGraphBuilder.BuildTaskNode for { context . TaskName } , found existing cluster.") ;
174203 return context . Graph . AllClusters [ context . TaskReference ] ;
175204 }
176205 return context . TaskDefinition switch
@@ -233,7 +262,7 @@ protected virtual NodeViewModel BuildCallTaskNode(TaskNodeRenderingContext<CallT
233262 break ;
234263 }
235264 default :
236- callType = context . TaskDefinition . Call ;
265+ callType = context . TaskDefinition . Call . ToLower ( ) ;
237266 break ;
238267 }
239268 var node = new CallTaskNodeViewModel ( context . TaskReference , context . TaskName ! , content , callType ) ;
@@ -334,7 +363,7 @@ protected virtual NodeViewModel BuildForkTaskNode(TaskNodeRenderingContext<ForkT
334363 for ( int i = 0 , c = context . TaskDefinition . Fork . Branches . Count ; i < c ; i ++ )
335364 {
336365 var branch = context . TaskDefinition . Fork . Branches . ElementAt ( i ) ;
337- var branchNode = this . BuildTaskNode ( new ( context . Workflow , context . Graph , context . TasksList , i , branch . Key , branch . Value , cluster , context . TaskReference + "/fork/branches" , context , context . ExitNode , context . EntryNode ) ) ;
366+ var branchNode = this . BuildTaskNode ( new ( context . Workflow , context . Graph , [ ] , i , branch . Key , branch . Value , cluster , context . TaskReference + "/fork/branches" , context , entryPort , exitPort ) ) ;
338367 if ( branchNode is WorkflowClusterViewModel branchCluster )
339368 {
340369 this . BuildEdge ( context . Graph , entryPort , branchCluster . AllNodes . Values . First ( ) ) ;
@@ -456,19 +485,7 @@ protected virtual NodeViewModel BuildSwitchTaskNode(TaskNodeRenderingContext<Swi
456485 foreach ( var switchCase in context . TaskDefinition . Switch )
457486 {
458487 var switchCaseTask = this . GetNextTask ( context . TasksList , context . TaskName , switchCase . Value . Then ) ! ;
459- var switchCaseNode = this . BuildTaskNode ( new (
460- context . Workflow ,
461- context . Graph ,
462- context . TasksList ,
463- switchCaseTask . Index ,
464- switchCaseTask . Name ,
465- switchCaseTask . Definition ,
466- context . TaskGroup ,
467- context . ParentReference ,
468- context . ParentContext ,
469- context . EntryNode ,
470- context . ExitNode
471- ) ) ;
488+ var switchCaseNode = this . BuildTaskNode ( new ( context . Workflow , context . Graph , context . TasksList , switchCaseTask . Index , switchCaseTask . Name , switchCaseTask . Definition , context . TaskGroup , context . ParentReference , context . ParentContext , context . EntryNode , context . ExitNode ) ) ;
472489 this . BuildEdge ( context . Graph , this . GetNodeAnchor ( node , NodePortType . Exit ) , GetNodeAnchor ( switchCaseNode , NodePortType . Entry ) ) ;
473490 }
474491 if ( ! context . TaskDefinition . Switch . Any ( switchCase => string . IsNullOrEmpty ( switchCase . Value . When ) ) )
@@ -702,7 +719,7 @@ protected class TaskNodeRenderingContext<TDefinition>(WorkflowDefinition workflo
702719 /// <param name="Name">The task name</param>
703720 /// <param name="Index">The task index</param>
704721 /// <param name="Definition">The task definition</param>
705- protected record TaskIdentity ( string Name , int Index , TaskDefinition Definition )
722+ protected record TaskIdentity ( string Name , int Index , TaskDefinition ? Definition )
706723 {
707724 }
708725
@@ -720,4 +737,27 @@ protected enum NodePortType
720737 /// </summary>
721738 Exit = 1
722739 }
740+
741+ /// <summary>
742+ /// The object used to compare <see cref="TaskIdentity"/>
743+ /// </summary>
744+ protected class TaskIdentityComparer : IEqualityComparer < TaskIdentity >
745+ {
746+ /// <inheritdoc/>
747+ public bool Equals ( TaskIdentity ? identity1 , TaskIdentity ? identity2 )
748+ {
749+ if ( ReferenceEquals ( identity1 , identity2 ) )
750+ return true ;
751+
752+ if ( identity1 is null || identity2 is null )
753+ return false ;
754+
755+ return identity1 . Name == identity2 . Name &&
756+ identity1 . Index == identity2 . Index &&
757+ identity1 . Definition == identity2 . Definition ;
758+ }
759+
760+ /// <inheritdoc/>
761+ public int GetHashCode ( TaskIdentity identity ) => identity . Name . GetHashCode ( ) ;
762+ }
723763}
0 commit comments