From df4b68557a8961cd49c3ca15045c4ecf32bab4e0 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bianchi Date: Thu, 12 Dec 2024 16:06:58 +0100 Subject: [PATCH] fix(Dashboard): fixed few transition bugs Signed-off-by: Jean-Baptiste Bianchi --- .../Services/WorkflowGraphBuilder.cs | 90 +++++++++++++------ 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs b/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs index ce527202..31726aaf 100644 --- a/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs +++ b/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs @@ -61,6 +61,7 @@ public class WorkflowGraphBuilder(ILogger logger, IYamlSer public IGraphViewModel Build(WorkflowDefinition workflow) { ArgumentNullException.ThrowIfNull(workflow); + this.Logger.LogTrace("Starting WorkflowGraphBuilder.Build"); Stopwatch sw = Stopwatch.StartNew(); var graph = new GraphViewModel(); var startNode = this.BuildStartNode(); @@ -95,9 +96,15 @@ protected virtual INodeViewModel GetNodeAnchor(INodeViewModel node, NodePortType /// The current task /// A specific transition, if any (use for switch cases) /// The next task identity - protected virtual TaskIdentity? GetNextTask(Map tasksList, string? currentTask, string? transition = null) + protected virtual TaskIdentity GetNextTask(Map tasksList, string? currentTask, string? transition = null) { - if (transition == FlowDirective.End || transition == FlowDirective.Exit) return null; + ArgumentNullException.ThrowIfNull(tasksList); + var taskDefinition = tasksList.FirstOrDefault(taskEntry => taskEntry.Key == currentTask)?.Value; + transition = !string.IsNullOrWhiteSpace(transition) ? transition : taskDefinition != null ? taskDefinition.Then : null; + if (transition == FlowDirective.End || transition == FlowDirective.Exit) + { + return new TaskIdentity(transition, -1, null); + } int index; if (!string.IsNullOrWhiteSpace(transition) && transition != FlowDirective.Continue) { @@ -108,7 +115,7 @@ protected virtual INodeViewModel GetNodeAnchor(INodeViewModel node, NodePortType index = tasksList.Keys.ToList().IndexOf(currentTask) + 1; if (index >= tasksList.Count) { - return null; + return new TaskIdentity(FlowDirective.Exit, -1, null); } } else @@ -129,26 +136,45 @@ protected virtual INodeViewModel GetNodeAnchor(INodeViewModel node, NodePortType /// The rendering context of the provided node protected virtual void BuildTransitions(INodeViewModel node, TaskNodeRenderingContext context) { - List transitions = []; - TaskIdentity? nextTask = this.GetNextTask(context.TasksList, context.TaskName); + ArgumentNullException.ThrowIfNull(node); + ArgumentNullException.ThrowIfNull(context); + this.Logger.LogTrace($"Starting WorkflowGraphBuilder.BuildTransitions from '{node.Id}'"); + List transitions = []; + TaskIdentity nextTask = this.GetNextTask(context.TasksList, context.TaskName); transitions.Add(nextTask); - while (!string.IsNullOrWhiteSpace(nextTask?.Definition.If)) + this.Logger.LogTrace($"[WorkflowGraphBuilder.BuildTransitions][{node.Id}] found transition to '{nextTask?.Name}'"); + while (!string.IsNullOrWhiteSpace(nextTask?.Definition?.If)) { + this.Logger.LogTrace($"[WorkflowGraphBuilder.BuildTransitions][{node.Id}] if clause found, looking up next task."); nextTask = this.GetNextTask(context.TasksList, nextTask.Name); transitions.Add(nextTask); + this.Logger.LogTrace($"[WorkflowGraphBuilder.BuildTransitions][{node.Id}] found transition to '{nextTask?.Name}'"); } - foreach (var transition in transitions.Distinct()) + foreach (var transition in transitions.Distinct(new TaskIdentityComparer())) { - if (transition != null) + if (transition.Index != -1) { + this.Logger.LogTrace($"[WorkflowGraphBuilder.BuildTransitions][{node.Id}] Building node '{transition.Name}'"); 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)); + this.Logger.LogTrace($"[WorkflowGraphBuilder.BuildTransitions][{node.Id}] Building edge to node '{transition.Name}'"); this.BuildEdge(context.Graph, this.GetNodeAnchor(node, NodePortType.Exit), GetNodeAnchor(transitionNode, NodePortType.Entry)); } - else + else if(transition.Name == FlowDirective.Exit) { + this.Logger.LogTrace($"[WorkflowGraphBuilder.BuildTransitions][{node.Id}] Exit transition, building edge to node '{context.ExitNode}'"); this.BuildEdge(context.Graph, this.GetNodeAnchor(node, NodePortType.Exit), context.ExitNode); } + else if (transition.Name == FlowDirective.End) + { + this.Logger.LogTrace($"[WorkflowGraphBuilder.BuildTransitions][{node.Id}] End transition, building edge to node '{context.ExitNode}'"); + this.BuildEdge(context.Graph, this.GetNodeAnchor(node, NodePortType.Exit), context.Graph.AllNodes.Skip(1).First().Value); + } + else + { + throw new IndexOutOfRangeException("Invalid transition"); + } } + this.Logger.LogTrace($"Exiting WorkflowGraphBuilder.BuildTransitions from '{node.Id}'"); } /// @@ -165,12 +191,15 @@ protected virtual void BuildTransitions(INodeViewModel node, TaskNodeRenderingCo protected INodeViewModel BuildTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); + this.Logger.LogTrace($"Starting WorkflowGraphBuilder.BuildTaskNode for {context.TaskName}"); if (context.Graph.AllNodes.ContainsKey(context.TaskReference)) { + this.Logger.LogTrace($"Exiting WorkflowGraphBuilder.BuildTaskNode for {context.TaskName}, found existing node."); return context.Graph.AllNodes[context.TaskReference]; } if (context.Graph.AllClusters.ContainsKey(context.TaskReference)) { + this.Logger.LogTrace($"Exiting WorkflowGraphBuilder.BuildTaskNode for {context.TaskName}, found existing cluster."); return context.Graph.AllClusters[context.TaskReference]; } return context.TaskDefinition switch @@ -233,7 +262,7 @@ protected virtual NodeViewModel BuildCallTaskNode(TaskNodeRenderingContext string.IsNullOrEmpty(switchCase.Value.When))) @@ -702,7 +719,7 @@ protected class TaskNodeRenderingContext(WorkflowDefinition workflo /// The task name /// The task index /// The task definition - protected record TaskIdentity(string Name, int Index, TaskDefinition Definition) + protected record TaskIdentity(string Name, int Index, TaskDefinition? Definition) { } @@ -720,4 +737,27 @@ protected enum NodePortType /// Exit = 1 } + + /// + /// The object used to compare + /// + protected class TaskIdentityComparer : IEqualityComparer + { + /// + public bool Equals(TaskIdentity? identity1, TaskIdentity? identity2) + { + if (ReferenceEquals(identity1, identity2)) + return true; + + if (identity1 is null || identity2 is null) + return false; + + return identity1.Name == identity2.Name && + identity1.Index == identity2.Index && + identity1.Definition == identity2.Definition; + } + + /// + public int GetHashCode(TaskIdentity identity) => identity.Name.GetHashCode(); + } }