Skip to content

Commit df4b685

Browse files
committed
fix(Dashboard): fixed few transition bugs
Signed-off-by: Jean-Baptiste Bianchi <jb.bianchi@neuroglia.io>
1 parent 88887f7 commit df4b685

File tree

1 file changed

+65
-25
lines changed

1 file changed

+65
-25
lines changed

src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)