Skip to content

Commit

Permalink
Merge pull request #444 from serverlessworkflow/feat-dashboard-suspen…
Browse files Browse the repository at this point in the history
…d-resume-cancel

Added suspend/resume/cancel actions to workflow instances in the Dashboard
  • Loading branch information
JBBianchi authored Oct 25, 2024
2 parents 192bdf8 + fbf7627 commit 511d103
Show file tree
Hide file tree
Showing 21 changed files with 271 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
2 changes: 1 addition & 1 deletion src/api/Synapse.Api.Http/Synapse.Api.Http.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<OutputType>Library</OutputType>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
2 changes: 1 addition & 1 deletion src/api/Synapse.Api.Server/Synapse.Api.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
2 changes: 1 addition & 1 deletion src/cli/Synapse.Cli/Synapse.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
2 changes: 1 addition & 1 deletion src/core/Synapse.Core/Synapse.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<NeutralLanguage>en</NeutralLanguage>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>alpha5</VersionSuffix>
<VersionSuffix>alpha5.1</VersionSuffix>
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>The Synapse Authors</Authors>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@
<div class="dropdown d-flex align-content-center">
<button class="btn btn-sm btn-dark" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="" @onclick:stopPropagation="true"><i class="bi bi-three-dots-vertical"></i></button>
<ul class="dropdown-menu">
<li><a class="dropdown-item @(selectedInstanceNames.Count() == 0 ? "text-mute" : "text-danger")" href="#" @onclick="OnDeleteSelected" @onclick:preventDefault="true" @onclick:stopPropagation="true"><Icon Name="IconName.Trash" /> Delete selected</a></li>
<li><button class="dropdown-item @(selectedInstanceNames.Count() == 0 ? "text-mute" : "")" disabled="@(selectedInstanceNames.Count() == 0)" @onclick="OnSuspendSelectedClickedAsync" @onclick:preventDefault="true" @onclick:stopPropagation="true"><Icon Name="IconName.Pause" /> Suspend selected</button></li>
<li><button class="dropdown-item @(selectedInstanceNames.Count() == 0 ? "text-mute" : "")" disabled="@(selectedInstanceNames.Count() == 0)" @onclick="OnResumeSelectedClickedAsync" @onclick:preventDefault="true" @onclick:stopPropagation="true"><Icon Name="IconName.Play" /> Resume selected</button></li>
<li><button class="dropdown-item @(selectedInstanceNames.Count() == 0 ? "text-mute" : "")" disabled="@(selectedInstanceNames.Count() == 0)" @onclick="OnCancelSelectedClickedAsync" @onclick:preventDefault="true" @onclick:stopPropagation="true"><Icon Name="IconName.X" /> Cancel selected</button></li>
<li><button class="dropdown-item @(selectedInstanceNames.Count() == 0 ? "text-mute" : "text-danger")" disabled="@(selectedInstanceNames.Count() == 0)" @onclick="OnDeleteSelected" @onclick:preventDefault="true" @onclick:stopPropagation="true"><Icon Name="IconName.Trash" /> Delete selected</button></li>
</ul>
</div>
</div>
Expand All @@ -68,9 +71,9 @@
<tr>
@foreach (var column in knownColumns)
{
if ((Columns.Count() == 0 && column != "Delete") || Columns.Contains(column))
if ((Columns.Count() == 0 && !DirectActions.Contains(column)) || Columns.Contains(column))
{
<th class="sticky-header text-@GetColumnAlignment(column)">@(column != "Action" && column != "Delete" ? column : "")</th>
<th class="sticky-header text-@GetColumnAlignment(column)">@(column != "Actions" && !DirectActions.Contains(column) ? column : "")</th>
}
}
<th class="sticky-header text-center align-middle">
Expand All @@ -85,7 +88,7 @@
<tr @onclick="async _ => await OnShowClickedAsync(instance)" class="cursor-pointer @(ActiveRow == instance.GetName() ? "table-active" : "")">
@foreach (var column in knownColumns)
{
if ((Columns.Count() == 0 && column != "Delete") || Columns.Contains(column))
if ((Columns.Count() == 0 && !DirectActions.Contains(column)) || Columns.Contains(column))
{
<td class="text-@GetColumnAlignment(column)">
@switch(column)
Expand Down Expand Up @@ -144,16 +147,44 @@
<div class="dropdown">
<button class="btn btn-sm btn-dark" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="" @onclick:stopPropagation="true"><i class="bi bi-three-dots-vertical"></i></button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" @onclick="async _ => await OnShowClickedAsync(instance)" @onclick:preventDefault="true" @onclick:stopPropagation="true"><Icon Name="IconName.Eye" /> View</a></li>
<li><a class="dropdown-item text-danger" href="#" @onclick="async _ => await OnDeleteClickedAsync(instance)" @onclick:preventDefault="true" @onclick:stopPropagation="true"><Icon Name="IconName.Trash" /> Delete</a></li>
@if (instance.Status?.Phase == WorkflowInstanceStatusPhase.Running)
{
<li><button class="dropdown-item" @onclick="async _ => await OnSuspendClickedAsync(instance)" @onclick:stopPropagation="true"><Icon Name="IconName.Pause" /> Suspend</button></li>
}
@if (instance.Status?.Phase == WorkflowInstanceStatusPhase.Suspended)
{
<li><button class="dropdown-item" @onclick="async _ => await OnResumeClickedAsync(instance)" @onclick:stopPropagation="true"><Icon Name="IconName.Play" /> Resume</button></li>
}
@if (instance.IsOperative)
{
<li><button class="dropdown-item" @onclick="async _ => await OnCancelClickedAsync(instance)" @onclick:stopPropagation="true"><Icon Name="IconName.X" /> Cancel</button></li>
}
<li><button class="dropdown-item" @onclick="async _ => await OnReplayClickedAsync(instance)" @onclick:stopPropagation="true"><Icon Name="IconName.ArrowClockwise" /> Replay</button></li>
<li><button class="dropdown-item text-danger" @onclick="async _ => await OnDeleteClickedAsync(instance)" @onclick:stopPropagation="true"><Icon Name="IconName.Trash" /> Delete</button></li>
</ul>
</div>
break;
case "Resume":
@if (instance.Status?.Phase == WorkflowInstanceStatusPhase.Running)
{
<button class="btn btn-sm text-primary" @onclick="async _ => await OnSuspendClickedAsync(instance)" @onclick:stopPropagation="true" title="Suspend"><Icon Name="IconName.Pause" /></button>
}
@if (instance.Status?.Phase == WorkflowInstanceStatusPhase.Suspended)
{
<button class="btn btn-sm text-primary" @onclick="async _ => await OnResumeClickedAsync(instance)" @onclick:stopPropagation="true" title="Resume"><Icon Name="IconName.Play" /></button>
}
break;
case "Cancel":
@if (instance.IsOperative)
{
<button class="btn btn-sm text-warning" @onclick="async _ => await OnCancelClickedAsync(instance)" @onclick:stopPropagation="true" title="Cancel"><Icon Name="IconName.X" /></button>
}
break;
case "Replay":
<button class="btn btn-sm text-primary" @onclick="async _ => await OnReplayClickedAsync(instance)" @onclick:stopPropagation="true"><Icon Name="IconName.ArrowClockwise" /></button>
<button class="btn btn-sm text-primary" @onclick="async _ => await OnReplayClickedAsync(instance)" @onclick:stopPropagation="true" title="Replay"><Icon Name="IconName.ArrowClockwise" /></button>
break;
case "Delete":
<button class="btn btn-sm text-danger" @onclick="async _ => await OnDeleteClickedAsync(instance)" @onclick:stopPropagation="true"><Icon Name="IconName.Trash" /></button>
<button class="btn btn-sm text-danger" @onclick="async _ => await OnDeleteClickedAsync(instance)" @onclick:stopPropagation="true" title="Delete"><Icon Name="IconName.Trash" /></button>
break;
default:
break;
Expand Down Expand Up @@ -208,9 +239,21 @@
[Parameter] public EventCallback<string?> OnToggleSelected { get; set; }
[Parameter] public EventCallback<WorkflowInstance> OnDelete { get; set; }
[Parameter] public EventCallback<WorkflowInstance> OnReplay { get; set; }
[Parameter] public EventCallback<WorkflowInstance> OnSuspend { get; set; }
[Parameter] public EventCallback<WorkflowInstance> OnResume { get; set; }
[Parameter] public EventCallback<WorkflowInstance> OnCancel { get; set; }
[Parameter] public EventCallback OnDeleteSelected { get; set; }
[Parameter] public EventCallback OnSuspendSelected { get; set; }
[Parameter] public EventCallback OnResumeSelected { get; set; }
[Parameter] public EventCallback OnCancelSelected { get; set; }

IEnumerable<string> knownColumns = [
public static IEnumerable<string> DirectActions = [
"Resume",
"Cancel",
"Replay",
"Delete"
];
static IEnumerable<string> knownColumns = [
"Name",
"Namespace",
"Definition",
Expand All @@ -221,8 +264,7 @@
"Duration",
"Operator",
"Actions",
"Replay",
"Delete"
..DirectActions
];

ElementReference checkboxAll = default!;
Expand Down Expand Up @@ -324,11 +366,14 @@
/// <returns></returns>
string GetColumnAlignment(string column)
{
return column == "Name" || column == "Namespace"
? "start"
: column == "Action"
? "end"
: "center";
return (
column == "Name" || column == "Namespace"
? "start"
: column == "Action"
? "end"
: "center"
)
+ (DirectActions.Contains(column) ? " p-0": "");
}

/// <summary>
Expand Down Expand Up @@ -385,7 +430,7 @@
/// <summary>
/// Handles the click on the show button
/// </summary>
/// <param name="instance"></param>
/// <param name="instance">The instance to show</param>
/// <returns></returns>
protected async Task OnShowClickedAsync(WorkflowInstance instance)
{
Expand All @@ -398,7 +443,7 @@
/// <summary>
/// Handles the click on the delete button
/// </summary>
/// <param name="instance"></param>
/// <param name="instance">The instance to delete</param>
/// <returns></returns>
protected async Task OnDeleteClickedAsync(WorkflowInstance instance)
{
Expand All @@ -411,7 +456,7 @@
/// <summary>
/// Handles the click on the replay button
/// </summary>
/// <param name="instance"></param>
/// <param name="instance">The instance to replay</param>
/// <returns></returns>
protected async Task OnReplayClickedAsync(WorkflowInstance instance)
{
Expand All @@ -420,4 +465,94 @@
await this.OnReplay.InvokeAsync(instance);
}
}

/// <summary>
/// Handles the click on the suspend button
/// </summary>
/// <param name="instance">The instance to suspend</param>
/// <returns></returns>
protected async Task OnSuspendClickedAsync(WorkflowInstance instance)
{
if (this.OnSuspend.HasDelegate)
{
await this.OnSuspend.InvokeAsync(instance);
}
}

/// <summary>
/// Handles the click on the resume button
/// </summary>
/// <param name="instance">The instance to resume</param>
/// <returns></returns>
protected async Task OnResumeClickedAsync(WorkflowInstance instance)
{
if (this.OnResume.HasDelegate)
{
await this.OnResume.InvokeAsync(instance);
}
}

/// <summary>
/// Handles the click on the cancel button
/// </summary>
/// <param name="instance">The instance to cancel</param>
/// <returns></returns>
protected async Task OnCancelClickedAsync(WorkflowInstance instance)
{
if (this.OnCancel.HasDelegate)
{
await this.OnCancel.InvokeAsync(instance);
}
}

protected async Task OnSuspendSelectedClickedAsync()
{
var selected = selectedInstanceNames.ToList();
var nonRuningInstances = (WorkflowInstances??[]).Where(instance => selected.Contains(instance.GetName()) && instance.Status?.Phase != WorkflowInstanceStatusPhase.Running);
foreach(var instance in nonRuningInstances)
{
if (this.OnToggleSelected.HasDelegate)
{
await OnToggleSelected.InvokeAsync(instance.GetName());
}
}
if (this.OnSuspendSelected.HasDelegate)
{
await OnSuspendSelected.InvokeAsync();
}
}

protected async Task OnResumeSelectedClickedAsync()
{
var selected = selectedInstanceNames.ToList();
var nonSuspendedInstances = (WorkflowInstances ?? []).Where(instance => selected.Contains(instance.GetName()) && instance.Status?.Phase != WorkflowInstanceStatusPhase.Suspended);
foreach (var instance in nonSuspendedInstances)
{
if (this.OnToggleSelected.HasDelegate)
{
await OnToggleSelected.InvokeAsync(instance.GetName());
}
}
if (this.OnResumeSelected.HasDelegate)
{
await OnResumeSelected.InvokeAsync();
}
}

protected async Task OnCancelSelectedClickedAsync()
{
var selected = selectedInstanceNames.ToList();
var nonOperativeInstances = (WorkflowInstances ?? []).Where(instance => selected.Contains(instance.GetName()) && !instance.IsOperative);
foreach (var instance in nonOperativeInstances)
{
if (this.OnToggleSelected.HasDelegate)
{
await OnToggleSelected.InvokeAsync(instance.GetName());
}
}
if (this.OnCancelSelected.HasDelegate)
{
await OnCancelSelected.InvokeAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public static class StatusExtensions
//CorrelationContextStatus.Completed => "success",
WorkflowInstanceStatusPhase.Waiting => "cinereous",
TaskInstanceStatus.Suspended => "icterine",
//WorkflowInstanceStatusPhase.Suspended => "icterine",
TaskInstanceStatus.Skipped => "cinereous",
WorkflowInstanceStatusPhase.Pending => "mute",
//TaskInstanceStatus.Pending => "mute",
Expand Down
Loading

0 comments on commit 511d103

Please sign in to comment.