@@ -61,6 +61,7 @@ public class WorkflowGraphBuilder(ILogger<WorkflowGraphBuilder> logger, IYamlSer
61
61
public IGraphViewModel Build ( WorkflowDefinition workflow )
62
62
{
63
63
ArgumentNullException . ThrowIfNull ( workflow ) ;
64
+ this . Logger . LogTrace ( "Starting WorkflowGraphBuilder.Build" ) ;
64
65
Stopwatch sw = Stopwatch . StartNew ( ) ;
65
66
var graph = new GraphViewModel ( ) ;
66
67
var startNode = this . BuildStartNode ( ) ;
@@ -95,9 +96,15 @@ protected virtual INodeViewModel GetNodeAnchor(INodeViewModel node, NodePortType
95
96
/// <param name="currentTask">The current task</param>
96
97
/// <param name="transition">A specific transition, if any (use for switch cases)</param>
97
98
/// <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 )
99
100
{
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
+ }
101
108
int index ;
102
109
if ( ! string . IsNullOrWhiteSpace ( transition ) && transition != FlowDirective . Continue )
103
110
{
@@ -108,7 +115,7 @@ protected virtual INodeViewModel GetNodeAnchor(INodeViewModel node, NodePortType
108
115
index = tasksList . Keys . ToList ( ) . IndexOf ( currentTask ) + 1 ;
109
116
if ( index >= tasksList . Count )
110
117
{
111
- return null ;
118
+ return new TaskIdentity ( FlowDirective . Exit , - 1 , null ) ;
112
119
}
113
120
}
114
121
else
@@ -129,26 +136,45 @@ protected virtual INodeViewModel GetNodeAnchor(INodeViewModel node, NodePortType
129
136
/// <param name="context">The rendering context of the provided node</param>
130
137
protected virtual void BuildTransitions ( INodeViewModel node , TaskNodeRenderingContext context )
131
138
{
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 ) ;
134
144
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 ) )
136
147
{
148
+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] if clause found, looking up next task.") ;
137
149
nextTask = this . GetNextTask ( context . TasksList , nextTask . Name ) ;
138
150
transitions . Add ( nextTask ) ;
151
+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] found transition to '{ nextTask ? . Name } '") ;
139
152
}
140
- foreach ( var transition in transitions . Distinct ( ) )
153
+ foreach ( var transition in transitions . Distinct ( new TaskIdentityComparer ( ) ) )
141
154
{
142
- if ( transition != null )
155
+ if ( transition . Index != - 1 )
143
156
{
157
+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] Building node '{ transition . Name } '") ;
144
158
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 } '") ;
145
160
this . BuildEdge ( context . Graph , this . GetNodeAnchor ( node , NodePortType . Exit ) , GetNodeAnchor ( transitionNode , NodePortType . Entry ) ) ;
146
161
}
147
- else
162
+ else if ( transition . Name == FlowDirective . Exit )
148
163
{
164
+ this . Logger . LogTrace ( $ "[WorkflowGraphBuilder.BuildTransitions][{ node . Id } ] Exit transition, building edge to node '{ context . ExitNode } '") ;
149
165
this . BuildEdge ( context . Graph , this . GetNodeAnchor ( node , NodePortType . Exit ) , context . ExitNode ) ;
150
166
}
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
+ }
151
176
}
177
+ this . Logger . LogTrace ( $ "Exiting WorkflowGraphBuilder.BuildTransitions from '{ node . Id } '") ;
152
178
}
153
179
154
180
/// <summary>
@@ -165,12 +191,15 @@ protected virtual void BuildTransitions(INodeViewModel node, TaskNodeRenderingCo
165
191
protected INodeViewModel BuildTaskNode ( TaskNodeRenderingContext context )
166
192
{
167
193
ArgumentNullException . ThrowIfNull ( context ) ;
194
+ this . Logger . LogTrace ( $ "Starting WorkflowGraphBuilder.BuildTaskNode for { context . TaskName } ") ;
168
195
if ( context . Graph . AllNodes . ContainsKey ( context . TaskReference ) )
169
196
{
197
+ this . Logger . LogTrace ( $ "Exiting WorkflowGraphBuilder.BuildTaskNode for { context . TaskName } , found existing node.") ;
170
198
return context . Graph . AllNodes [ context . TaskReference ] ;
171
199
}
172
200
if ( context . Graph . AllClusters . ContainsKey ( context . TaskReference ) )
173
201
{
202
+ this . Logger . LogTrace ( $ "Exiting WorkflowGraphBuilder.BuildTaskNode for { context . TaskName } , found existing cluster.") ;
174
203
return context . Graph . AllClusters [ context . TaskReference ] ;
175
204
}
176
205
return context . TaskDefinition switch
@@ -233,7 +262,7 @@ protected virtual NodeViewModel BuildCallTaskNode(TaskNodeRenderingContext<CallT
233
262
break ;
234
263
}
235
264
default :
236
- callType = context . TaskDefinition . Call ;
265
+ callType = context . TaskDefinition . Call . ToLower ( ) ;
237
266
break ;
238
267
}
239
268
var node = new CallTaskNodeViewModel ( context . TaskReference , context . TaskName ! , content , callType ) ;
@@ -334,7 +363,7 @@ protected virtual NodeViewModel BuildForkTaskNode(TaskNodeRenderingContext<ForkT
334
363
for ( int i = 0 , c = context . TaskDefinition . Fork . Branches . Count ; i < c ; i ++ )
335
364
{
336
365
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 ) ) ;
338
367
if ( branchNode is WorkflowClusterViewModel branchCluster )
339
368
{
340
369
this . BuildEdge ( context . Graph , entryPort , branchCluster . AllNodes . Values . First ( ) ) ;
@@ -456,19 +485,7 @@ protected virtual NodeViewModel BuildSwitchTaskNode(TaskNodeRenderingContext<Swi
456
485
foreach ( var switchCase in context . TaskDefinition . Switch )
457
486
{
458
487
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 ) ) ;
472
489
this . BuildEdge ( context . Graph , this . GetNodeAnchor ( node , NodePortType . Exit ) , GetNodeAnchor ( switchCaseNode , NodePortType . Entry ) ) ;
473
490
}
474
491
if ( ! context . TaskDefinition . Switch . Any ( switchCase => string . IsNullOrEmpty ( switchCase . Value . When ) ) )
@@ -702,7 +719,7 @@ protected class TaskNodeRenderingContext<TDefinition>(WorkflowDefinition workflo
702
719
/// <param name="Name">The task name</param>
703
720
/// <param name="Index">The task index</param>
704
721
/// <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 )
706
723
{
707
724
}
708
725
@@ -720,4 +737,27 @@ protected enum NodePortType
720
737
/// </summary>
721
738
Exit = 1
722
739
}
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
+ }
723
763
}
0 commit comments