Skip to content

Commit

Permalink
Dispaly input and output in the workflow instance (#143)
Browse files Browse the repository at this point in the history
* added includeCompositeRoot on the service

* Lazy loading draft

* Show input and output in the instance viewer and updated edit button to go inside the sub-workflow

* Removed code

* Removed unused code

* Fixed padding

* Updated client package

* Updated package

* Remove whitespace

---------

Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
  • Loading branch information
MariusVuscanNx and sfmskywalker committed Feb 19, 2024
1 parent 88ac339 commit 61ea672
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public interface IWorkflowDefinitionService
/// <summary>
/// Finds all workflow definitions by their IDs.
/// </summary>
Task<IEnumerable<WorkflowDefinition>> FindManyByIdAsync(IEnumerable<string> ids, CancellationToken cancellationToken = default);
Task<IEnumerable<WorkflowDefinition>> FindManyByIdAsync(IEnumerable<string> ids, bool includeCompositeRoot = false, CancellationToken cancellationToken = default);

/// <summary>
/// Saves a workflow definition.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ public async Task<PagedListResponse<WorkflowDefinitionSummary>> ListAsync(ListWo
}

/// <inheritdoc />
public async Task<IEnumerable<WorkflowDefinition>> FindManyByIdAsync(IEnumerable<string> ids, CancellationToken cancellationToken = default)
public async Task<IEnumerable<WorkflowDefinition>> FindManyByIdAsync(IEnumerable<string> 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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<MudTooltip Text="Edit Workflow Definition">
<MudIconButton
Icon="@Icons.Material.Outlined.EditNote"
OnClick="@(() => OnEditClicked(WorkflowDefinition.DefinitionId))">
OnClick="@(() => OnEditClicked())">
</MudIconButton>
</MudTooltip>
<ElapsedTime StartTime="WorkflowInstance.CreatedAt" EndTime="@(WorkflowInstance.Status == WorkflowStatus.Running ? DateTimeOffset.UtcNow : WorkflowInstance.FinishedAt ?? DateTimeOffset.UtcNow)"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -71,6 +72,12 @@ public partial class WorkflowInstanceDesigner : IAsyncDisposable
/// </summary>
[Parameter] public EventCallback<string> EditWorkflowDefinition { get; set; }

/// <summary>
/// Gets or sets the current selected sub-workflow.
/// </summary>
[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!;
Expand Down Expand Up @@ -100,6 +107,16 @@ private RadzenSplitterPane ActivityPropertiesPane
private MudTabs PropertyTabs { get; set; } = default!;
private MudTabPanel EventsTabPanel { get; set; } = default!;

/// <summary>
/// Updates the selected sub-workflow.
/// </summary>
/// <param name="obj"></param>
public void UpdateSubWorkflow(JsonObject? obj)
{
SelectedSubWorkflow = obj;
StateHasChanged();
}

/// <inheritdoc />
protected override async Task OnInitializedAsync()
{
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,64 @@
</MudTabPanel>
<MudTabPanel Text="Variables">
<Well>
<MudTable
T="Variable"
Items="WorkflowDefinition.Variables">
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh>Type</MudTh>
<MudTh>Storage</MudTh>
<MudTh>Value</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>@context.Name</MudTd>
<MudTd>@context.GetTypeDisplayName()</MudTd>
<MudTd>@GetStorageDriverDisplayName(context.StorageDriverTypeName)</MudTd>
<MudTd>@GetVariableValue(context)</MudTd>
</RowTemplate>
<NoRecordsContent>
<div>
@if (WorkflowVariableData.Any())
{
<DataPanel Data="WorkflowVariableData" HideEmptyValues="false"/>
}
else
{
<Well>
No variables
</Well>
</NoRecordsContent>
</MudTable>
}
</div>
</Well>
</MudTabPanel>
<MudTabPanel Text="Input/output">
<Well>
<MudAlert Variant="Variant.Outlined">Input output</MudAlert>
<div>
<MudText Typo="Typo.overline" GutterBottom="true" Align="Align.Left">Inputs</MudText>
@if (WorkflowInputData.Any())
{
<DataPanel Data="WorkflowInputData" HideEmptyValues="false"/>
}
else
{
<Well>
No inputs
</Well>
}
</div>
<div>
<MudText Typo="Typo.overline" GutterBottom="true" Align="Align.Left">Outputs</MudText>
@if (WorkflowOutputData.Any())
{
<DataPanel Data="WorkflowOutputData" HideEmptyValues="false"/>
}
else
{
<Well>
No outputs
</Well>
}
</div>
</Well>
<Well>
@if (SubWorkflowInputData.Any())
{
<div>
<MudText Typo="Typo.overline" GutterBottom="true" Align="Align.Left">Sub-Workflow Inputs</MudText>
<DataPanel Data="SubWorkflowInputData" HideEmptyValues="false"/>
</div>
}
@if (SubWorkflowOutputData.Any())
{
<div>
<MudText Typo="Typo.overline" GutterBottom="true" Align="Align.Left">Sub-Workflow Outputs</MudText>
<DataPanel Data="SubWorkflowOutputData" HideEmptyValues="false"/>
</div>
}
</Well>
</MudTabPanel>
</MudTabs>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -39,10 +40,17 @@ public partial class WorkflowInstanceDetails
[Parameter]
public JsonObject? SelectedSubWorkflow { get; set; } = default!;

/// <summary>
/// Gets or sets the current selected sub-workflow executions.
/// </summary>
[Parameter]
public ICollection<ActivityExecutionRecord>? 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<string, StorageDriverDescriptor> StorageDriverLookup { get; set; } =
new Dictionary<string, StorageDriverDescriptor>();
Expand Down Expand Up @@ -74,21 +82,59 @@ public partial class WorkflowInstanceDetails
}
}

private Dictionary<string, DataPanelItem> WorkflowVariableData
{
get
{
if (WorkflowDefinition == null)
return new Dictionary<string, DataPanelItem>();

return WorkflowDefinition.Variables.ToDictionary(entry => entry.Name,
entry => new DataPanelItem(@GetVariableValue(entry)));
}
}

private Dictionary<string, DataPanelItem> WorkflowInputData
{
get
{
if (_workflowInstance == null)
return new Dictionary<string, DataPanelItem>();

return _workflowInstance.WorkflowState.Input.ToDictionary(entry => entry.Key,
entry => new DataPanelItem(entry.Value.ToString()));
}
}

private Dictionary<string, DataPanelItem> WorkflowOutputData
{
get
{
if (_workflowInstance == null)
return new Dictionary<string, DataPanelItem>();

return _workflowInstance.WorkflowState.Output.ToDictionary(entry => entry.Key,
entry => new DataPanelItem(entry.Value.ToString()));
}
}

private Dictionary<string, DataPanelItem> 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()
{
Expand All @@ -101,9 +147,72 @@ public partial class WorkflowInstanceDetails
}
}

public void UpdateSubWorkflow(JsonObject? obj)
private Dictionary<string, DataPanelItem> SubWorkflowInputData
{
get
{
if (SelectedSubWorkflowExecutions == null || SelectedSubWorkflow == null)
return new Dictionary<string, DataPanelItem>();

var execution = SelectedSubWorkflowExecutions.LastOrDefault();
var inputData = new Dictionary<string, DataPanelItem>();
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<string, DataPanelItem> SubWorkflowOutputData
{
get
{
if (SelectedSubWorkflowExecutions == null || SelectedSubWorkflow == null)
return new Dictionary<string, DataPanelItem>();

var execution = SelectedSubWorkflowExecutions.LastOrDefault();
var outputData = new Dictionary<string, DataPanelItem>();

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;
}
}

/// <summary>
/// Updates the selected sub-workflow.
/// </summary>
/// <param name="obj"></param>
public async Task UpdateSubWorkflowAsync(JsonObject? obj)
{
SelectedSubWorkflow = obj;
SelectedSubWorkflowExecutions = obj == null
? null
: (await InvokeWithBlazorServiceContext(() =>
ActivityExecutionService.ListAsync(WorkflowInstance!.Id, obj.GetNodeId()!))).ToList();
StateHasChanged();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
var definition = WorkflowDefinitions.First(x => x.Id == workflowInstance.DefinitionVersionId);
<MudTabPanel Text="@workflowInstance.Id" ShowCloseIcon="false" Style="height: 100%">
<WorkflowInstanceDesigner
@ref="@_workflowInstanceDesigner"
WorkflowInstance="workflowInstance"
WorkflowDefinition="definition"
PathChanged="OnPathChanged"
Expand All @@ -33,7 +34,7 @@

</RadzenSplitterPane>
<RadzenSplitterPane Size="35%" Min="100px">
<WorkflowInstanceDetails @ref="@_workflowInstanceDetails" WorkflowInstance="@SelectedWorkflowInstance" WorkflowDefinition="SelectedWorkflowDefinition" />
<WorkflowInstanceDetails @ref="@_workflowInstanceDetails" WorkflowInstance="@SelectedWorkflowInstance" WorkflowDefinition="SelectedWorkflowDefinition"/>
</RadzenSplitterPane>

</RadzenSplitter>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,6 +16,7 @@ public partial class WorkflowInstanceWorkspace : IWorkspace
{
private MudDynamicTabs _dynamicTabs = default!;
private WorkflowInstanceDetails _workflowInstanceDetails = default!;
private WorkflowInstanceDesigner _workflowInstanceDesigner = default!;

[Parameter] public IList<WorkflowInstance> WorkflowInstances { get; set; } = default!;
[Parameter] public IList<WorkflowDefinition> WorkflowDefinitions { get; set; } = default!;
Expand All @@ -36,7 +38,7 @@ public partial class WorkflowInstanceWorkspace : IWorkspace
ActiveTabIndex >= 0 && ActiveTabIndex < WorkflowInstances.Count
? WorkflowInstances.ElementAtOrDefault(ActiveTabIndex)
: default;

private WorkflowDefinition? SelectedWorkflowDefinition
{
get
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<WorkflowInstance> { instance };
_workflowDefinitions = response.ToList();
await SelectWorkflowInstanceAsync(instance);
Expand Down

0 comments on commit 61ea672

Please sign in to comment.