diff --git a/src/modules/Elsa.Studio.Workflows.Core/Domain/Contracts/IWorkflowDefinitionService.cs b/src/modules/Elsa.Studio.Workflows.Core/Domain/Contracts/IWorkflowDefinitionService.cs index d22555f8..dfe274d1 100644 --- a/src/modules/Elsa.Studio.Workflows.Core/Domain/Contracts/IWorkflowDefinitionService.cs +++ b/src/modules/Elsa.Studio.Workflows.Core/Domain/Contracts/IWorkflowDefinitionService.cs @@ -31,7 +31,7 @@ public interface IWorkflowDefinitionService /// /// Finds all workflow definitions by their IDs. /// - Task> FindManyByIdAsync(IEnumerable ids, CancellationToken cancellationToken = default); + Task> FindManyByIdAsync(IEnumerable ids, bool includeCompositeRoot = false, CancellationToken cancellationToken = default); /// /// Saves a workflow definition. diff --git a/src/modules/Elsa.Studio.Workflows.Core/Domain/Services/RemoteWorkflowDefinitionService.cs b/src/modules/Elsa.Studio.Workflows.Core/Domain/Services/RemoteWorkflowDefinitionService.cs index e9de8370..8cc2c980 100644 --- a/src/modules/Elsa.Studio.Workflows.Core/Domain/Services/RemoteWorkflowDefinitionService.cs +++ b/src/modules/Elsa.Studio.Workflows.Core/Domain/Services/RemoteWorkflowDefinitionService.cs @@ -57,10 +57,10 @@ public async Task> ListAsync(ListWo } /// - public async Task> FindManyByIdAsync(IEnumerable ids, CancellationToken cancellationToken = default) + public async Task> FindManyByIdAsync(IEnumerable ids, bool includeCompositeRoot = false, CancellationToken cancellationToken = default) { var api = await GetApiAsync(cancellationToken); - var response = await api.GetManyByIdAsync(ids.ToList(), true, cancellationToken); + var response = await api.GetManyByIdAsync(ids.ToList(), includeCompositeRoot, cancellationToken); return response.Items; } diff --git a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDesigner.razor b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDesigner.razor index f22cee9e..5c581efe 100644 --- a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDesigner.razor +++ b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDesigner.razor @@ -10,7 +10,7 @@ + OnClick="@(() => OnEditClicked())"> diff --git a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDesigner.razor.cs b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDesigner.razor.cs index d0b78f72..08642575 100644 --- a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDesigner.razor.cs +++ b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDesigner.razor.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using System.Text.Json.Nodes; using Elsa.Api.Client.Extensions; using Elsa.Api.Client.Resources.ActivityDescriptors.Models; @@ -71,6 +72,12 @@ public partial class WorkflowInstanceDesigner : IAsyncDisposable /// [Parameter] public EventCallback EditWorkflowDefinition { get; set; } + /// + /// Gets or sets the current selected sub-workflow. + /// + [Parameter] + public JsonObject? SelectedSubWorkflow { get; set; } = default!; + [Inject] private IActivityRegistry ActivityRegistry { get; set; } = default!; [Inject] private IDiagramDesignerService DiagramDesignerService { get; set; } = default!; [Inject] private IDomAccessor DomAccessor { get; set; } = default!; @@ -100,6 +107,16 @@ private RadzenSplitterPane ActivityPropertiesPane private MudTabs PropertyTabs { get; set; } = default!; private MudTabPanel EventsTabPanel { get; set; } = default!; + /// + /// Updates the selected sub-workflow. + /// + /// + public void UpdateSubWorkflow(JsonObject? obj) + { + SelectedSubWorkflow = obj; + StateHasChanged(); + } + /// protected override async Task OnInitializedAsync() { @@ -254,8 +271,24 @@ private async Task OnResize(RadzenSplitterResizeEventArgs arg) await UpdatePropertiesPaneHeightAsync(); } - private Task OnEditClicked(string definitionId) + private Task OnEditClicked() { + var definitionId = WorkflowDefinition!.DefinitionId; + + if (SelectedSubWorkflow != null) + { + var typeName = SelectedSubWorkflow.GetTypeName(); + var version = SelectedSubWorkflow.GetVersion(); + var descriptor = ActivityRegistry.Find(typeName, version); + var isWorkflowActivity = descriptor != null && + descriptor.CustomProperties.TryGetValue("RootType", out var rootTypeNameElement) && + ((JsonElement)rootTypeNameElement).GetString() == "WorkflowDefinitionActivity"; + if (isWorkflowActivity) + { + definitionId = SelectedSubWorkflow.GetWorkflowDefinitionId(); + } + } + var editWorkflowDefinition = this.EditWorkflowDefinition; if (editWorkflowDefinition.HasDelegate) diff --git a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDetails.razor b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDetails.razor index 9c6c0c31..c7a35e58 100644 --- a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDetails.razor +++ b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDetails.razor @@ -24,32 +24,64 @@ - - - Name - Type - Storage - Value - - - @context.Name - @context.GetTypeDisplayName() - @GetStorageDriverDisplayName(context.StorageDriverTypeName) - @GetVariableValue(context) - - +
+ @if (WorkflowVariableData.Any()) + { + + } + else + { No variables - - + } +
- Input output +
+ Inputs + @if (WorkflowInputData.Any()) + { + + } + else + { + + No inputs + + } +
+
+ Outputs + @if (WorkflowOutputData.Any()) + { + + } + else + { + + No outputs + + } +
+
+ + @if (SubWorkflowInputData.Any()) + { +
+ Sub-Workflow Inputs + +
+ } + @if (SubWorkflowOutputData.Any()) + { +
+ Sub-Workflow Outputs + +
+ }
diff --git a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDetails.razor.cs b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDetails.razor.cs index 8e49f264..713a44de 100644 --- a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDetails.razor.cs +++ b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceDetails.razor.cs @@ -1,6 +1,7 @@ using System.Text.Json; using System.Text.Json.Nodes; using Elsa.Api.Client.Extensions; +using Elsa.Api.Client.Resources.ActivityExecutions.Models; using Elsa.Api.Client.Resources.StorageDrivers.Models; using Elsa.Api.Client.Resources.WorkflowDefinitions.Models; using Elsa.Api.Client.Resources.WorkflowInstances.Enums; @@ -39,10 +40,17 @@ public partial class WorkflowInstanceDetails [Parameter] public JsonObject? SelectedSubWorkflow { get; set; } = default!; + /// + /// Gets or sets the current selected sub-workflow executions. + /// + [Parameter] + public ICollection? SelectedSubWorkflowExecutions { get; set; } = default!; + [Inject] private IStorageDriverService StorageDriverService { get; set; } = default!; [Inject] private IWorkflowInstanceObserverFactory WorkflowInstanceObserverFactory { get; set; } = default!; [Inject] private IWorkflowInstanceService WorkflowInstanceService { get; set; } = default!; [Inject] private IActivityRegistry ActivityRegistry { get; set; } = default!; + [Inject] private IActivityExecutionService ActivityExecutionService { get; set; } = default!; private IDictionary StorageDriverLookup { get; set; } = new Dictionary(); @@ -74,21 +82,59 @@ public partial class WorkflowInstanceDetails } } + private Dictionary WorkflowVariableData + { + get + { + if (WorkflowDefinition == null) + return new Dictionary(); + + return WorkflowDefinition.Variables.ToDictionary(entry => entry.Name, + entry => new DataPanelItem(@GetVariableValue(entry))); + } + } + + private Dictionary WorkflowInputData + { + get + { + if (_workflowInstance == null) + return new Dictionary(); + + return _workflowInstance.WorkflowState.Input.ToDictionary(entry => entry.Key, + entry => new DataPanelItem(entry.Value.ToString())); + } + } + + private Dictionary WorkflowOutputData + { + get + { + if (_workflowInstance == null) + return new Dictionary(); + + return _workflowInstance.WorkflowState.Output.ToDictionary(entry => entry.Key, + entry => new DataPanelItem(entry.Value.ToString())); + } + } + private Dictionary WorkflowInstanceSubWorkflowData { get { if (SelectedSubWorkflow == null) - return new (); + return new(); var typeName = SelectedSubWorkflow.GetTypeName(); var version = SelectedSubWorkflow.GetVersion(); var descriptor = ActivityRegistry.Find(typeName, version); - var isWorkflowActivity = descriptor != null && descriptor.CustomProperties.TryGetValue("RootType", out var rootTypeNameElement) && ((JsonElement)rootTypeNameElement).GetString() == "WorkflowDefinitionActivity"; + var isWorkflowActivity = descriptor != null && + descriptor.CustomProperties.TryGetValue("RootType", out var rootTypeNameElement) && + ((JsonElement)rootTypeNameElement).GetString() == "WorkflowDefinitionActivity"; var workflowDefinitionId = isWorkflowActivity ? SelectedSubWorkflow.GetWorkflowDefinitionId() : default; if (workflowDefinitionId == null) - return new (); + return new(); return new() { @@ -101,9 +147,72 @@ public partial class WorkflowInstanceDetails } } - public void UpdateSubWorkflow(JsonObject? obj) + private Dictionary SubWorkflowInputData + { + get + { + if (SelectedSubWorkflowExecutions == null || SelectedSubWorkflow == null) + return new Dictionary(); + + var execution = SelectedSubWorkflowExecutions.LastOrDefault(); + var inputData = new Dictionary(); + var activityState = execution?.ActivityState; + if (activityState != null) + { + var activityDescriptor = + ActivityRegistry.Find(SelectedSubWorkflow.GetTypeName(), SelectedSubWorkflow.GetVersion())!; + foreach (var inputDescriptor in activityDescriptor.Inputs) + { + var inputValue = activityState.TryGetValue(inputDescriptor.Name, out var value) ? value : default; + inputData[inputDescriptor.Name] = new(inputValue?.ToString()); + } + } + + return inputData; + } + } + + private Dictionary SubWorkflowOutputData + { + get + { + if (SelectedSubWorkflowExecutions == null || SelectedSubWorkflow == null) + return new Dictionary(); + + var execution = SelectedSubWorkflowExecutions.LastOrDefault(); + var outputData = new Dictionary(); + + if (execution != null) + { + var outputs = execution.Outputs; + var activityDescriptor = + ActivityRegistry.Find(SelectedSubWorkflow.GetTypeName(), SelectedSubWorkflow.GetVersion())!; + var outputDescriptors = activityDescriptor.Outputs; + + foreach (var outputDescriptor in outputDescriptors) + { + var outputValue = outputs != null + ? outputs.TryGetValue(outputDescriptor.Name, out var value) ? value : default + : default; + outputData[outputDescriptor.Name] = new(outputValue?.ToString()); + } + } + + return outputData; + } + } + + /// + /// Updates the selected sub-workflow. + /// + /// + public async Task UpdateSubWorkflowAsync(JsonObject? obj) { SelectedSubWorkflow = obj; + SelectedSubWorkflowExecutions = obj == null + ? null + : (await InvokeWithBlazorServiceContext(() => + ActivityExecutionService.ListAsync(WorkflowInstance!.Id, obj.GetNodeId()!))).ToList(); StateHasChanged(); } diff --git a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceWorkspace.razor b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceWorkspace.razor index 9afce602..bbc33509 100644 --- a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceWorkspace.razor +++ b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceWorkspace.razor @@ -19,6 +19,7 @@ var definition = WorkflowDefinitions.First(x => x.Id == workflowInstance.DefinitionVersionId); - + diff --git a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceWorkspace.razor.cs b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceWorkspace.razor.cs index 34bfd34c..f3522ae2 100644 --- a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceWorkspace.razor.cs +++ b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/Components/WorkflowInstanceWorkspace.razor.cs @@ -1,5 +1,6 @@ using System.Text.Json.Nodes; using Elsa.Api.Client.Extensions; +using Elsa.Api.Client.Resources.ActivityExecutions.Models; using Elsa.Api.Client.Resources.WorkflowDefinitions.Models; using Elsa.Api.Client.Resources.WorkflowInstances.Models; using Elsa.Studio.Workflows.Components.WorkflowDefinitionEditor.Components; @@ -15,6 +16,7 @@ public partial class WorkflowInstanceWorkspace : IWorkspace { private MudDynamicTabs _dynamicTabs = default!; private WorkflowInstanceDetails _workflowInstanceDetails = default!; + private WorkflowInstanceDesigner _workflowInstanceDesigner = default!; [Parameter] public IList WorkflowInstances { get; set; } = default!; [Parameter] public IList WorkflowDefinitions { get; set; } = default!; @@ -36,7 +38,7 @@ public partial class WorkflowInstanceWorkspace : IWorkspace ActiveTabIndex >= 0 && ActiveTabIndex < WorkflowInstances.Count ? WorkflowInstances.ElementAtOrDefault(ActiveTabIndex) : default; - + private WorkflowDefinition? SelectedWorkflowDefinition { get @@ -66,7 +68,8 @@ private async Task OnActivePanelIndexChanged(int value) private async Task OnPathChanged(DesignerPathChangedArgs args) { - _workflowInstanceDetails.UpdateSubWorkflow(args.CurrentActivity); + await _workflowInstanceDetails.UpdateSubWorkflowAsync(args.CurrentActivity); + _workflowInstanceDesigner.UpdateSubWorkflow(args.CurrentActivity); if (PathChanged != null) await PathChanged(args); diff --git a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/WorkflowInstanceViewer.razor.cs b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/WorkflowInstanceViewer.razor.cs index 7aa0a5e2..af38ecf1 100644 --- a/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/WorkflowInstanceViewer.razor.cs +++ b/src/modules/Elsa.Studio.Workflows/Components/WorkflowInstanceViewer/WorkflowInstanceViewer.razor.cs @@ -42,7 +42,7 @@ protected override async Task OnInitializedAsync() { var instance = await WorkflowInstanceService.GetAsync(InstanceId) ?? throw new InvalidOperationException($"Workflow instance with ID {InstanceId} not found."); var definitionVersionIds = new[] { instance.DefinitionVersionId }; - var response = await WorkflowDefinitionService.FindManyByIdAsync(definitionVersionIds); + var response = await WorkflowDefinitionService.FindManyByIdAsync(definitionVersionIds, true); _workflowInstances = new List { instance }; _workflowDefinitions = response.ToList(); await SelectWorkflowInstanceAsync(instance); diff --git a/src/modules/Elsa.Studio.Workflows/Shared/Components/DiagramDesignerWrapper.razor.cs b/src/modules/Elsa.Studio.Workflows/Shared/Components/DiagramDesignerWrapper.razor.cs index a94ce16f..f618d2bb 100644 --- a/src/modules/Elsa.Studio.Workflows/Shared/Components/DiagramDesignerWrapper.razor.cs +++ b/src/modules/Elsa.Studio.Workflows/Shared/Components/DiagramDesignerWrapper.razor.cs @@ -1,6 +1,11 @@ using System.Text.Json.Nodes; +using System.Threading.Tasks; using Elsa.Api.Client.Extensions; using Elsa.Api.Client.Shared.Models; +using Elsa.Studio.Contracts; +using Elsa.Studio.Workflows.Components.WorkflowInstanceViewer; +using Elsa.Studio.Workflows.Designer; +using Elsa.Studio.Workflows.Designer.Models; using Elsa.Studio.Workflows.Domain.Contexts; using Elsa.Studio.Workflows.Domain.Contracts; using Elsa.Studio.Workflows.Extensions;