From 7c628ded8e7cacb5265a5c61087a8cda3acb3685 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bianchi Date: Wed, 31 Jul 2024 18:19:51 +0200 Subject: [PATCH] fix(Dashboard): Refactored edge drawing in the graph Signed-off-by: Jean-Baptiste Bianchi --- .../Synapse.Api.Http/Synapse.Api.Http.csproj | 4 +- .../Synapse.Core.Infrastructure.csproj | 10 +- src/core/Synapse.Core/Synapse.Core.csproj | 4 +- .../Synapse.Correlator.csproj | 12 +- .../WorkflowDiagram/CallTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/DoTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/EmitTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/EndNodeViewModel.cs | 2 +- .../ExtensionTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/ForTaskNodeViewModel.cs | 5 +- .../ListenTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/NodeViewModelConfig.cs | 2 +- .../WorkflowDiagram/RaiseTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/RunTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/SetTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/StartNodeViewModel.cs | 2 +- .../SwitchTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/TaskNodeViewModel.cs | 4 +- .../Templates/NodeLabelTemplate.razor | 10 +- .../Templates/NodeShapeTemplate.razor | 22 +- .../Templates/NodeSymbolTemplate.razor | 12 +- .../Templates/WorkflowNodeTemplate.razor | 12 +- .../WorkflowDiagram/TryTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/WaitTaskNodeViewModel.cs | 5 +- .../WorkflowDiagram/WorkflowDiagram.razor | 6 +- .../WorkflowDiagram/WorkflowNodeViewModel.cs | 19 +- .../Interfaces/IWorkflowGraphBuilder.cs | 2 +- .../Services/WorkflowGraphBuilder.cs | 246 ++++++++++-------- .../Synapse.Dashboard.csproj | 3 +- .../Synapse.Runner/Synapse.Runner.csproj | 10 +- .../Synapse.UnitTests.csproj | 6 +- 31 files changed, 248 insertions(+), 200 deletions(-) diff --git a/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj b/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj index e6afd741c..ef8f1a9b6 100644 --- a/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj +++ b/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj b/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj index f601d3c5a..8a87a4cdd 100644 --- a/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj +++ b/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj @@ -10,11 +10,11 @@ - - - - - + + + + + diff --git a/src/core/Synapse.Core/Synapse.Core.csproj b/src/core/Synapse.Core/Synapse.Core.csproj index 5b4a51042..557bfb1d6 100644 --- a/src/core/Synapse.Core/Synapse.Core.csproj +++ b/src/core/Synapse.Core/Synapse.Core.csproj @@ -32,8 +32,8 @@ - - + + diff --git a/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj b/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj index e1b46019e..e2de0e553 100644 --- a/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj +++ b/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj @@ -16,12 +16,12 @@ - - - - - - + + + + + + diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CallTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CallTaskNodeViewModel.cs index 14e5cd834..0965e8d92 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CallTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CallTaskNodeViewModel.cs @@ -22,11 +22,12 @@ public class CallTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content /// The type of call - public CallTaskNodeViewModel(string name, string content, string callType = "") - : base(new() { Label = name, CssClass = "call-task-node" }) + public CallTaskNodeViewModel(string taskReference, string name, string content, string callType = "") + : base(taskReference, new() { Label = name, CssClass = "call-task-node" }) { Content = content; Symbol = !string.IsNullOrEmpty(callType) ? $"{callType.ToLower()}-symbol" : "call-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/DoTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/DoTaskNodeViewModel.cs index c9fbda422..c9f2a92db 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/DoTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/DoTaskNodeViewModel.cs @@ -22,10 +22,11 @@ public class DoTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content - public DoTaskNodeViewModel(string name, string content) - : base(new() { Label = name, CssClass = "do-task-node" }) + public DoTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "do-task-node" }) { Content = content; Symbol = "do-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EmitTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EmitTaskNodeViewModel.cs index 4cecee45f..a11684014 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EmitTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EmitTaskNodeViewModel.cs @@ -22,10 +22,11 @@ public class EmitTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content - public EmitTaskNodeViewModel(string name, string content) - : base(new() { Label = name, CssClass = "emit-task-node" }) + public EmitTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "emit-task-node" }) { Content = content; Symbol = "emit-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EndNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EndNodeViewModel.cs index 765b756ec..5ff3fe779 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EndNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EndNodeViewModel.cs @@ -19,6 +19,6 @@ namespace Synapse.Dashboard.Components; /// Represents the object that holds the data required to render the view of a workflow's end node /// public class EndNodeViewModel() - : WorkflowNodeViewModel(new() { CssClass = "end-node", Shape = NodeShape.Circle, Width = WorkflowGraphBuilder.StartEndNodeRadius, Height = WorkflowGraphBuilder.StartEndNodeRadius }) + : WorkflowNodeViewModel("end-node", new() { CssClass = "end-node", Shape = NodeShape.Circle, Width = WorkflowGraphBuilder.StartEndNodeRadius, Height = WorkflowGraphBuilder.StartEndNodeRadius }) { } diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ExtensionTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ExtensionTaskNodeViewModel.cs index 7cda60da5..228754c0c 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ExtensionTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ExtensionTaskNodeViewModel.cs @@ -22,9 +22,10 @@ public class ExtensionTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name - public ExtensionTaskNodeViewModel(string name) - : base(new() { Label = name, CssClass = "extension-task-node" }) + public ExtensionTaskNodeViewModel(string taskReference, string name) + : base(taskReference, new() { Label = name, CssClass = "extension-task-node" }) { Symbol = "extension-symbol"; Type = "EXTENSION"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ForTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ForTaskNodeViewModel.cs index f99f5abfe..b171ac0cb 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ForTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ForTaskNodeViewModel.cs @@ -22,10 +22,11 @@ public class ForTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content - public ForTaskNodeViewModel(string name, string content) - : base(new() { Label = name, CssClass = "for-task-node" }) + public ForTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "for-task-node" }) { Content = content; Symbol = "for-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ListenTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ListenTaskNodeViewModel.cs index e0cb9fb6f..ce5f96201 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ListenTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ListenTaskNodeViewModel.cs @@ -22,10 +22,11 @@ public class ListenTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content - public ListenTaskNodeViewModel(string name, string content) - : base(new() { Label = name, CssClass = "listen-task-node" }) + public ListenTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "listen-task-node" }) { Content = content; Symbol = "listen-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/NodeViewModelConfig.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/NodeViewModelConfig.cs index e0af8cc3b..9b634c2b3 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/NodeViewModelConfig.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/NodeViewModelConfig.cs @@ -73,7 +73,7 @@ public class NodeViewModelConfig /// /// Gets/sets parent node id /// - public Guid? ParentId { get; set; } + public string? ParentId { get; set; } /// /// Gets/sets the shape the node diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RaiseTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RaiseTaskNodeViewModel.cs index f62bb7432..4640d52f9 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RaiseTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RaiseTaskNodeViewModel.cs @@ -22,10 +22,11 @@ public class RaiseTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content - public RaiseTaskNodeViewModel(string name, string content) - : base(new() { Label = name, CssClass = "raise-task-node" }) + public RaiseTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "raise-task-node" }) { Content = content; Symbol = "raise-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RunTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RunTaskNodeViewModel.cs index 583095a75..83fe4469d 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RunTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RunTaskNodeViewModel.cs @@ -24,11 +24,12 @@ public class RunTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content /// The type of run - public RunTaskNodeViewModel(string name, string content, string runType = "") - : base(new() { Label = name, CssClass = "run-task-node" }) + public RunTaskNodeViewModel(string taskReference, string name, string content, string runType = "") + : base(taskReference, new() { Label = name, CssClass = "run-task-node" }) { Content = content; Symbol = !string.IsNullOrEmpty(runType) ? $"{runType.ToLower()}-symbol" : "run-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SetTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SetTaskNodeViewModel.cs index 45c3e1f68..5a5407eec 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SetTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SetTaskNodeViewModel.cs @@ -22,10 +22,11 @@ public class SetTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content - public SetTaskNodeViewModel(string name, string content) - : base(new() { Label = name, CssClass = "set-task-node" }) + public SetTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "set-task-node" }) { Content = content; Symbol = "set-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/StartNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/StartNodeViewModel.cs index a1f4aec3b..87a05e66e 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/StartNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/StartNodeViewModel.cs @@ -20,7 +20,7 @@ namespace Synapse.Dashboard.Components; /// Represents the object that holds the data required to render the view of a workflow's start node /// public class StartNodeViewModel(bool hasSuccessor = false) - : WorkflowNodeViewModel(new() { CssClass = "start-node", Shape = NodeShape.Circle, Width = WorkflowGraphBuilder.StartEndNodeRadius, Height = WorkflowGraphBuilder.StartEndNodeRadius }) + : WorkflowNodeViewModel("start-node", new() { CssClass = "start-node", Shape = NodeShape.Circle, Width = WorkflowGraphBuilder.StartEndNodeRadius, Height = WorkflowGraphBuilder.StartEndNodeRadius }) { /// diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SwitchTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SwitchTaskNodeViewModel.cs index 15d09e730..96beb1e65 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SwitchTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SwitchTaskNodeViewModel.cs @@ -22,10 +22,11 @@ public class SwitchTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content - public SwitchTaskNodeViewModel(string name, string content) - : base(new() { Label = name, CssClass = "switch-task-node" }) + public SwitchTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "switch-task-node" }) { Content = content; Symbol = "switch-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TaskNodeViewModel.cs index fa54dd026..36f8bd20c 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TaskNodeViewModel.cs @@ -30,12 +30,14 @@ public class TaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The name of the the represents /// The the represents /// Indicates whether or not the task to create the for is the first task of the workflow it belongs to - public TaskNodeViewModel(string name, TaskDefinition definition, bool isFirst = false) + public TaskNodeViewModel(string taskReference, string name, TaskDefinition definition, bool isFirst = false) : base(null, name) { + this.Id = taskReference; this.Name = name; this.Definition = definition; this.IsFirst = isFirst; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeLabelTemplate.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeLabelTemplate.razor index 2e79de05d..67b5b8d16 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeLabelTemplate.razor +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeLabelTemplate.razor @@ -18,11 +18,11 @@ else if (!string.IsNullOrWhiteSpace(Node.Label) ) @code { - IBoundingBox bbox => Node.BBox!; - string x => bbox.X.ToInvariantString() ?? string.Empty; - string y => bbox.Y.ToInvariantString() ?? string.Empty; - string width => bbox.Width.ToInvariantString() ?? string.Empty; - string height => bbox.Height.ToInvariantString() ?? Neuroglia.Blazor.Dagre.Constants.LabelHeight.ToInvariantString(); + BoundingBox bounds => Node.Bounds!; + string x => bounds.X.ToInvariantString() ?? string.Empty; + string y => bounds.Y.ToInvariantString() ?? string.Empty; + string width => bounds.Width.ToInvariantString() ?? string.Empty; + string height => bounds.Height.ToInvariantString() ?? Neuroglia.Blazor.Dagre.Constants.LabelHeight.ToInvariantString(); [CascadingParameter(Name = "Node")] public INodeViewModel Node { get; set; } = null!; [CascadingParameter(Name = "LabelTemplate")] public RenderFragment? LabelTemplate { get; set; } = null!; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeShapeTemplate.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeShapeTemplate.razor index cea246a44..cc09064e6 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeShapeTemplate.razor +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeShapeTemplate.razor @@ -3,31 +3,31 @@ @if (Node.Shape == NodeShape.Circle) { - + } else if (Node.Shape == NodeShape.Ellipse) { - + } else if (Node.Shape == SynapseNodeShape.Cartouche) { - - + + } else { - + } @code { - IBoundingBox bbox => Node.BBox!; + BoundingBox bounds => Node.Bounds!; [CascadingParameter(Name = "Node")] public INodeViewModel Node { get; set; } = null!; } \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeSymbolTemplate.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeSymbolTemplate.razor index bf519b36b..9a0498770 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeSymbolTemplate.razor +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeSymbolTemplate.razor @@ -12,18 +12,18 @@ @code { Double _symbolSize = 30; - IBoundingBox bbox => Node.BBox!; + BoundingBox bounds => Node.Bounds!; string? width => Node.Shape != SynapseNodeShape.Cartouche ? - (bbox.Width / 2).ToInvariantString() : + (bounds.Width / 2).ToInvariantString() : _symbolSize.ToInvariantString(); string? height => Node.Shape != SynapseNodeShape.Cartouche ? - (bbox.Height / 2).ToInvariantString() : + (bounds.Height / 2).ToInvariantString() : _symbolSize.ToInvariantString(); string? x => Node.Shape != SynapseNodeShape.Cartouche ? - (0 - bbox.Width / 4).ToInvariantString() : - (0 - (bbox.Width - _symbolSize) / 2).ToInvariantString(); + (0 - bounds.Width / 4).ToInvariantString() : + (0 - (bounds.Width - _symbolSize) / 2).ToInvariantString(); string? y => Node.Shape != SynapseNodeShape.Cartouche ? - (0 - bbox.Height / 4).ToInvariantString() : + (0 - bounds.Height / 4).ToInvariantString() : (0 - _symbolSize / 2).ToInvariantString(); [CascadingParameter(Name = "Node")] public INodeViewModel Node { get; set; } = null!; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/WorkflowNodeTemplate.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/WorkflowNodeTemplate.razor index ee03f16e4..b67b8beee 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/WorkflowNodeTemplate.razor +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/WorkflowNodeTemplate.razor @@ -1,7 +1,7 @@ @namespace Synapse.Dashboard @inherits NodeTemplate - + @@ -24,15 +24,15 @@ @code { protected virtual IWorkflowNodeViewModel WorkflowNode => (IWorkflowNodeViewModel)this.Node; - IBoundingBox bbox => Node.BBox!; + BoundingBox bounds => Node.Bounds!; RenderFragment? LabelTemplate => !string.IsNullOrWhiteSpace(WorkflowNode.Type) || !string.IsNullOrWhiteSpace(WorkflowNode.Content) ? (__builder) => { - +

@if (!string.IsNullOrWhiteSpace(Node.Label)) diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TryTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TryTaskNodeViewModel.cs index 34ff04476..fa7499721 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TryTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TryTaskNodeViewModel.cs @@ -22,10 +22,11 @@ public class TryTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content - public TryTaskNodeViewModel(string name, string content) - : base(new() { Label = name, CssClass = "try-task-node" }) + public TryTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "try-task-node" }) { Content = content; Symbol = "try-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WaitTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WaitTaskNodeViewModel.cs index 29624e055..490f73a64 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WaitTaskNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WaitTaskNodeViewModel.cs @@ -22,10 +22,11 @@ public class WaitTaskNodeViewModel /// /// Initializes a new /// + /// The node task reference /// The node name /// The node content - public WaitTaskNodeViewModel(string name, string content) - : base(new() { Label = name, CssClass = "wait-task-node" }) + public WaitTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "wait-task-node" }) { Content = content; Symbol = "wait-symbol"; diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagram.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagram.razor index 3435b8d7f..c881fca85 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagram.razor +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagram.razor @@ -22,9 +22,9 @@ [Parameter] public EventCallback> OnMouseUp { get; set; } - protected override async Task OnParametersSetAsync() + protected override void OnParametersSet() { - await base.OnParametersSetAsync(); + base.OnParametersSet(); if (this.WorkflowDefinition != null && this.WorkflowDefinition != this.workflowDefinition) { this.workflowDefinition = this.WorkflowDefinition; @@ -32,7 +32,7 @@ { Direction = this.Orientation == WorkflowDiagramOrientation.LeftToRight ? DagreGraphDirection.LeftToRight : DagreGraphDirection.TopToBottom }; - this.graph = await this.WorkflowGraphBuilder.Build(this.workflowDefinition); + this.graph = this.WorkflowGraphBuilder.Build(this.workflowDefinition); this.isDirty = true; } } diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowNodeViewModel.cs index 730c3d950..e58636b13 100644 --- a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowNodeViewModel.cs +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowNodeViewModel.cs @@ -18,12 +18,8 @@ namespace Synapse.Dashboard.Components; /// /// Represents the base class for all workflow-related node models /// -/// -/// Initialiazes a new -/// -/// -public abstract class WorkflowNodeViewModel(NodeViewModelConfig? config = null) - : NodeViewModel(config?.Label, config?.CssClass, config?.Shape, config?.Width, config?.Height, config?.RadiusX, config?.RadiusY, config?.X, config?.Y, config?.ComponentType, config?.ParentId), IWorkflowNodeViewModel +public abstract class WorkflowNodeViewModel + : NodeViewModel, IWorkflowNodeViewModel { int _operativeInstances = 0; @@ -60,6 +56,17 @@ public int FaultedInstancesCount } } + /// + /// Initialiazes a new + /// + /// The node task reference + /// The for the node + public WorkflowNodeViewModel(string taskReference, NodeViewModelConfig? config = null) + : base(config?.Label, config?.CssClass, config?.Shape, config?.Width ?? 0, config?.Height ?? 0, config?.RadiusX ?? 0, config?.RadiusY ?? 0, config?.X ?? 0, config?.Y ?? 0, config?.ComponentType, config?.ParentId) + { + this.Id = taskReference; + } + /// public void ResetInstancesCount() { diff --git a/src/dashboard/Synapse.Dashboard/Services/Interfaces/IWorkflowGraphBuilder.cs b/src/dashboard/Synapse.Dashboard/Services/Interfaces/IWorkflowGraphBuilder.cs index a9c26b9b8..cee013aad 100644 --- a/src/dashboard/Synapse.Dashboard/Services/Interfaces/IWorkflowGraphBuilder.cs +++ b/src/dashboard/Synapse.Dashboard/Services/Interfaces/IWorkflowGraphBuilder.cs @@ -27,6 +27,6 @@ public interface IWorkflowGraphBuilder ///

/// The to build a new for /// A new - Task Build(WorkflowDefinition workflow); + IGraphViewModel Build(WorkflowDefinition workflow); } diff --git a/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs b/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs index a5231daa3..0131ba7fb 100644 --- a/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs +++ b/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs @@ -19,6 +19,7 @@ using ServerlessWorkflow.Sdk.Models.Calls; using ServerlessWorkflow.Sdk.Models.Tasks; using System.Diagnostics; +using System.Xml.Linq; namespace Synapse.Dashboard.Services; @@ -47,7 +48,7 @@ public class WorkflowGraphBuilder(IYamlSerializer yamlSerializer, IJsonSerialize protected IJsonSerializer JsonSerializer { get; } = jsonSerializer; /// - public async Task Build(WorkflowDefinition workflow) + public IGraphViewModel Build(WorkflowDefinition workflow) { ArgumentNullException.ThrowIfNull(workflow); Stopwatch sw = Stopwatch.StartNew(); @@ -55,10 +56,14 @@ public async Task Build(WorkflowDefinition workflow) var graph = new GraphViewModel(); var startNode = this.BuildStartNode(!isEmpty); var endNode = this.BuildEndNode(); - await graph.AddElementAsync(startNode); - await graph.AddElementAsync(endNode); - if (isEmpty) await this.BuildEdgeAsync(graph, startNode, endNode); - else await this.BuildTaskNodesAsync(new(workflow, graph, 0, workflow.Do.First().Key, workflow.Do.First().Value, null, "/do", endNode, startNode)); + graph.AddNode(startNode); + graph.AddNode(endNode); + var nextNode = endNode; + if (!isEmpty) + { + nextNode = this.BuildTaskNode(new(workflow, graph, 0, workflow.Do.First().Key, workflow.Do.First().Value, null, "/do", endNode, startNode)); + } + this.BuildEdge(graph, startNode, nextNode); sw.Stop(); Console.WriteLine($"WorkflowGraphBuilder.Build took {sw.ElapsedMilliseconds} ms"); return graph; @@ -72,30 +77,29 @@ public async Task Build(WorkflowDefinition workflow) protected virtual NodeViewModel BuildStartNode(bool hasSuccessor = false) => new StartNodeViewModel(hasSuccessor); /// - /// Recursively builds task nodes and their edges + /// Gets the next in the graph /// /// The rendering context for the task nodes - /// The last built task - protected async Task BuildTaskNodesAsync(TaskNodeRenderingContext context) + /// The current task node + /// A transition, if different from the context task definition's + /// The next task + /// + protected NodeViewModel GetNextNode(TaskNodeRenderingContext context, NodeViewModel currentNode, string? transition = null) { - var lastNode = await this.BuildTaskNodeAsync(context); - if (context.TaskDefinition.Then == FlowDirective.End || context.TaskDefinition.Then == FlowDirective.Exit) await this.BuildEdgeAsync(context.Graph, lastNode, context.EndNode); - else - { - var nextTaskName = string.IsNullOrWhiteSpace(context.TaskDefinition.Then) || context.TaskDefinition.Then == FlowDirective.Continue + transition = !string.IsNullOrWhiteSpace(transition) ? transition : context.TaskDefinition.Then; + if (transition == FlowDirective.End || transition == FlowDirective.Exit) return context.EndNode; + var nextTaskName = string.IsNullOrWhiteSpace(transition) || transition == FlowDirective.Continue ? context.Workflow.GetTaskAfter(new(context.TaskName, context.TaskDefinition), context.ParentReference)?.Key - : context.TaskDefinition.Then; - if (string.IsNullOrWhiteSpace(nextTaskName)) await this.BuildEdgeAsync(context.Graph, lastNode, context.EndNode); - else - { - var nextTaskIndex = context.Workflow.IndexOf(nextTaskName, context.ParentReference); - var nextTaskReference = $"{context.ParentReference}/{nextTaskIndex}/{nextTaskName}"; - var nextTask = context.Workflow.GetComponent(nextTaskReference) ?? throw new Exception($"Failed to find the task at '{nextTaskReference}' in workflow '{context.Workflow.Document.Name}.{context.Workflow.Document.Namespace}:{context.Workflow.Document.Version}'"); - var nextTaskNode = await this.BuildTaskNodesAsync(new(context.Workflow, context.Graph, nextTaskIndex, nextTaskName, nextTask, context.TaskGroup, context.ParentReference, context.EndNode, lastNode)); - await this.BuildEdgeAsync(context.Graph, lastNode, nextTaskNode); - } + : transition; + if (string.IsNullOrWhiteSpace(nextTaskName)) return context.EndNode; + var nextTaskIndex = context.Workflow.IndexOf(nextTaskName, context.ParentReference); + var nextTaskReference = $"{context.ParentReference}/{nextTaskIndex}/{nextTaskName}"; + var nextTask = context.Workflow.GetComponent(nextTaskReference) ?? throw new Exception($"Failed to find the task at '{nextTaskReference}' in workflow '{context.Workflow.Document.Name}.{context.Workflow.Document.Namespace}:{context.Workflow.Document.Version}'"); + if (!context.Graph.AllNodes.ContainsKey(nextTaskReference)) + { + this.BuildTaskNode(new(context.Workflow, context.Graph, nextTaskIndex, nextTaskName, nextTask, context.TaskGroup, context.ParentReference, context.EndNode, currentNode)); } - return lastNode; + return (NodeViewModel)context.Graph.AllNodes[nextTaskReference]; } /// @@ -103,23 +107,23 @@ protected async Task BuildTaskNodesAsync(TaskNodeRenderingContext /// /// The rendering context for the task node /// A new - protected async Task BuildTaskNodeAsync(TaskNodeRenderingContext context) + protected NodeViewModel BuildTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); return context.TaskDefinition switch { - CallTaskDefinition => await this.BuildCallTaskNodeAsync(context.OfType()), - DoTaskDefinition => await this.BuildDoTaskNodeAsync(context.OfType()), - EmitTaskDefinition => await this.BuildEmitTaskNodeAsync(context.OfType()), - ExtensionTaskDefinition => await this.BuildExtensionTaskNodeAsync(context.OfType()), - ForTaskDefinition => await this.BuildForTaskNodeAsync(context.OfType()), - ListenTaskDefinition => await this.BuildListenTaskNodeAsync(context.OfType()), - RaiseTaskDefinition => await this.BuildRaiseTaskNodeAsync(context.OfType()), - RunTaskDefinition => await this.BuildRunTaskNodeAsync(context.OfType()), - SetTaskDefinition => await this.BuildSetTaskNodeAsync(context.OfType()), - SwitchTaskDefinition => await this.BuildSwitchTaskNodeAsync(context.OfType()), - TryTaskDefinition => await this.BuildTryTaskNodeAsync(context.OfType()), - WaitTaskDefinition => await this.BuildWaitTaskNodeAsync(context.OfType()), + CallTaskDefinition => this.BuildCallTaskNode(context.OfType()), + DoTaskDefinition => this.BuildDoTaskNode(context.OfType()), + EmitTaskDefinition => this.BuildEmitTaskNode(context.OfType()), + ExtensionTaskDefinition => this.BuildExtensionTaskNode(context.OfType()), + ForTaskDefinition => this.BuildForTaskNode(context.OfType()), + ListenTaskDefinition => this.BuildListenTaskNode(context.OfType()), + RaiseTaskDefinition => this.BuildRaiseTaskNode(context.OfType()), + RunTaskDefinition => this.BuildRunTaskNode(context.OfType()), + SetTaskDefinition => this.BuildSetTaskNode(context.OfType()), + SwitchTaskDefinition => this.BuildSwitchTaskNode(context.OfType()), + TryTaskDefinition => this.BuildTryTaskNode(context.OfType()), + WaitTaskDefinition => this.BuildWaitTaskNode(context.OfType()), _ => throw new NotSupportedException($"The specified task type '{context.TaskDefinition.GetType()}' is not supported") } ?? throw new Exception($"Unable to define a last node for task '{context.TaskName}'"); } @@ -129,7 +133,7 @@ protected async Task BuildTaskNodeAsync(TaskNodeRenderingContext /// /// The rendering context for the call task node /// A new - protected virtual async Task BuildCallTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildCallTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); var content = string.Empty; @@ -169,11 +173,11 @@ protected virtual async Task BuildCallTaskNodeAsync(TaskNodeRende callType = string.Empty; break; } - var lastNode = new CallTaskNodeViewModel(context.TaskName, content, callType); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new CallTaskNodeViewModel(context.TaskReference, context.TaskName, content, callType); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -181,15 +185,15 @@ protected virtual async Task BuildCallTaskNodeAsync(TaskNodeRende /// /// The rendering context for the do task node /// A new - protected virtual async Task BuildDoTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildDoTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); var taskCount = context.TaskDefinition.Do.Count; - var lastNode = new DoTaskNodeViewModel(context.TaskName, $"{taskCount} task{(taskCount > 1 ? "s": "")}"); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new DoTaskNodeViewModel(context.TaskReference, context.TaskName, $"{taskCount} task{(taskCount > 1 ? "s": "")}"); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -197,14 +201,14 @@ protected virtual async Task BuildDoTaskNodeAsync(TaskNodeRenderi /// /// The rendering context for the emit task node /// A new - protected virtual async Task BuildEmitTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildEmitTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); - var lastNode = new EmitTaskNodeViewModel(context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Emit.Event.With)); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new EmitTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Emit.Event.With)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -212,14 +216,14 @@ protected virtual async Task BuildEmitTaskNodeAsync(TaskNodeRende /// /// The rendering context for the extension task node /// A new - protected virtual async Task BuildExtensionTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildExtensionTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); - var lastNode = new ExtensionTaskNodeViewModel(context.TaskName); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new ExtensionTaskNodeViewModel(context.TaskReference, context.TaskName); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -227,14 +231,14 @@ protected virtual async Task BuildExtensionTaskNodeAsync(TaskNode /// /// The rendering context for the for task node /// A new - protected virtual async Task BuildForTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildForTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); - var lastNode = new ForTaskNodeViewModel(context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.For)); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new ForTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.For)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -242,14 +246,14 @@ protected virtual async Task BuildForTaskNodeAsync(TaskNodeRender /// /// The rendering context for the listen task node /// A new - protected virtual async Task BuildListenTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildListenTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); - var lastNode = new ListenTaskNodeViewModel(context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Listen)); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new ListenTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Listen)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -257,14 +261,14 @@ protected virtual async Task BuildListenTaskNodeAsync(TaskNodeRen /// /// The rendering context for the raise task node /// A new - protected virtual async Task BuildRaiseTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildRaiseTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); - var lastNode = new RaiseTaskNodeViewModel(context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Raise.Error)); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new RaiseTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Raise.Error)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -272,7 +276,7 @@ protected virtual async Task BuildRaiseTaskNodeAsync(TaskNodeRend /// /// The rendering context for the run task node /// A new - protected virtual async Task BuildRunTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildRunTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); string content = string.Empty; @@ -307,11 +311,11 @@ protected virtual async Task BuildRunTaskNodeAsync(TaskNodeRender runType = string.Empty; break; } - var lastNode = new RunTaskNodeViewModel(context.TaskName, content, runType); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new RunTaskNodeViewModel(context.TaskReference, context.TaskName, content, runType); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -319,14 +323,14 @@ protected virtual async Task BuildRunTaskNodeAsync(TaskNodeRender /// /// The rendering context for the set task node /// A new - protected virtual async Task BuildSetTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildSetTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); - var lastNode = new SetTaskNodeViewModel(context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Set)); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new SetTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Set)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -334,14 +338,23 @@ protected virtual async Task BuildSetTaskNodeAsync(TaskNodeRender /// /// The rendering context for the switch task node /// A new - protected virtual async Task BuildSwitchTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildSwitchTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); - var lastNode = new SwitchTaskNodeViewModel(context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Switch)); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new SwitchTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Switch)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + foreach (var switchCase in context.TaskDefinition.Switch) + { + var nextTaskNode = this.GetNextNode(context, node, switchCase.Value.Then); + this.BuildEdge(context.Graph, node, nextTaskNode, switchCase.Key); + //node = nextTaskNode; + } + if (!context.TaskDefinition.Switch.Any(switchCase => string.IsNullOrEmpty(switchCase.Value.When))) + { + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + } + return node; } /// @@ -349,15 +362,15 @@ protected virtual async Task BuildSwitchTaskNodeAsync(TaskNodeRen /// /// The rendering context for the try task node /// A new - protected virtual async Task BuildTryTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildTryTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); var taskCount = context.TaskDefinition.Try.Count; - var lastNode = new TryTaskNodeViewModel(context.TaskName, $"{taskCount} task{(taskCount > 1 ? "s" : "")}"); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new TryTaskNodeViewModel(context.TaskReference, context.TaskName, $"{taskCount} task{(taskCount > 1 ? "s" : "")}"); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -365,14 +378,14 @@ protected virtual async Task BuildTryTaskNodeAsync(TaskNodeRender /// /// The rendering context for the wait task node /// A new - protected virtual async Task BuildWaitTaskNodeAsync(TaskNodeRenderingContext context) + protected virtual NodeViewModel BuildWaitTaskNode(TaskNodeRenderingContext context) { ArgumentNullException.ThrowIfNull(context); - var lastNode = new WaitTaskNodeViewModel(context.TaskName, context.TaskDefinition.Wait.ToTimeSpan().ToString("hh\\:mm\\:ss\\.fff")); - if (context.TaskGroup == null) await context.Graph.AddElementAsync(lastNode); - else await context.TaskGroup.AddChildAsync(lastNode); - await this.BuildEdgeAsync(context.Graph, context.PreviousNode, lastNode); - return lastNode; + var node = new WaitTaskNodeViewModel(context.TaskReference, context.TaskName, context.TaskDefinition.Wait.ToTimeSpan().ToString("hh\\:mm\\:ss\\.fff")); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; } /// @@ -387,8 +400,21 @@ protected virtual async Task BuildWaitTaskNodeAsync(TaskNodeRende /// The current /// The node to draw the edge from /// The node to draw the edge to + /// The edge label, if any /// A new awaitable - protected virtual Task BuildEdgeAsync(GraphViewModel graph, NodeViewModel source, NodeViewModel target) => graph.AddElementAsync(new EdgeViewModel(source.Id, target.Id, null)); + protected virtual void BuildEdge(GraphViewModel graph, NodeViewModel source, NodeViewModel target, string? label = null) + { + var existingEdge = graph.Edges.Select(keyValuePair => keyValuePair.Value).FirstOrDefault(edge => edge.SourceId == source.Id && edge.TargetId == target.Id); + if (existingEdge != null) + { + if (!string.IsNullOrEmpty(label)) { + existingEdge.Label = existingEdge.Label + " / " + label; + } + return; + } + if (graph.Edges.Select(keyValuePair => keyValuePair.Value).Any(edge => edge.SourceId == source.Id && edge.TargetId == target.Id && edge.Label == label)) return; + graph.AddEdge(new EdgeViewModel(source.Id, target.Id, label)); + } /// /// Represents the context for rendering a task node within a workflow. diff --git a/src/dashboard/Synapse.Dashboard/Synapse.Dashboard.csproj b/src/dashboard/Synapse.Dashboard/Synapse.Dashboard.csproj index 23519f509..9431650cb 100644 --- a/src/dashboard/Synapse.Dashboard/Synapse.Dashboard.csproj +++ b/src/dashboard/Synapse.Dashboard/Synapse.Dashboard.csproj @@ -34,7 +34,8 @@ - + + diff --git a/src/runner/Synapse.Runner/Synapse.Runner.csproj b/src/runner/Synapse.Runner/Synapse.Runner.csproj index 1c5f70b4a..c8bc88f38 100644 --- a/src/runner/Synapse.Runner/Synapse.Runner.csproj +++ b/src/runner/Synapse.Runner/Synapse.Runner.csproj @@ -40,11 +40,11 @@ - - - - - + + + + + diff --git a/tests/Synapse.UnitTests/Synapse.UnitTests.csproj b/tests/Synapse.UnitTests/Synapse.UnitTests.csproj index fea3207f3..4606f124a 100644 --- a/tests/Synapse.UnitTests/Synapse.UnitTests.csproj +++ b/tests/Synapse.UnitTests/Synapse.UnitTests.csproj @@ -19,9 +19,9 @@ - - - + + +