Skip to content

Enabled label management when creating or editing a workflow #533

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
limitations under the License.
*@

@namespace Synapse.Dashboard.Components
@using Json.Schema
@using Neuroglia.Data.Infrastructure.ResourceOriented.Properties
@using ServerlessWorkflow.Sdk
@using ServerlessWorkflow.Sdk.Models
@using System.Xml.Schema
@using Synapse.Core.Infrastructure.Services
@using System.Text.Json.Nodes
@namespace Synapse.Dashboard.Components
@inject MonacoInterop MonacoInterop
@inject IExternalResourceProvider ExternalResourceProvider
@inject IJsonSerializer JsonSerializer
Expand All @@ -38,7 +38,7 @@
</Content>
</Tab>
}
<Tab Title="Text">
<Tab Title="Raw">
<Content>
<div class="pt-3">
<MonacoEditor OnTextChanged="OnTextChanged" ModelName="@modelName" Document="input" />
Expand All @@ -49,19 +49,37 @@

<Accordion class="py-3">
<AccordionItem Title="Advanced Settings">
<Content>
@if (Operators != null && Operators.Count() > 0)
{
<label for="operator">Select an Operator to run the Workflow:</label>
<select id="operator" class="form-select" @onchange="OnSelectOperatorChanged">
<option value="">Any Operator</option>
@foreach (var op in Operators)
{
var name = op.GetName() + "." + op.GetNamespace();
<option value="@name" selected="@(name == operatorName)">@name</option>
}
</select>
}
<Content>
<div>
@if (Operators != null && Operators.Count() > 0)
{
<label for="operator" class="fw-bolder mb-2">Run on:</label>
<select id="operator" class="form-select" @onchange="(e) => SetOperator(e.Value?.ToString())">
<option value="">Any Operator</option>
@foreach (var op in Operators)
{
var name = op.GetName() + "." + op.GetNamespace();
<option value="@name" selected="@(name == operatorName)">@name</option>
}
</select>
}
</div>
<div class="mt-4">
<DictionaryEditor Title="Labels"
KeyPlaceholder="Enter label key"
ValuePlaceholder="Enter label value"
Entries="parameters.Labels"
OnAddEntry="(kvp) => AddLabel(kvp.Key, kvp.Value)"
OnRemoveEntry="RemoveLabel" />
</div>
<div class="mt-4">
<DictionaryEditor Title="Annotations"
KeyPlaceholder="Enter annotation key"
ValuePlaceholder="Enter annotation value"
Entries="parameters.Annotations"
OnAddEntry="(kvp) => AddAnnotation(kvp.Key, kvp.Value)"
OnRemoveEntry="RemoveAnnotation" />
</div>
</Content>
</AccordionItem>
</Accordion>
Expand All @@ -77,33 +95,37 @@

WorkflowDefinition? workflowDefinition;
JsonSchema? schema;
string payload = string.Empty;
string modelName = string.Empty;
EquatableDictionary<string, object>? input;
CreateWorkflowInstanceParameters parameters = new();
string? operatorName;

[Parameter] public WorkflowDefinition? WorkflowDefinition { get; set; }
[Parameter] public IEnumerable<Operator> Operators { get; set; } = [];
[Parameter] public string? OperatorName { get; set; } = null;
[Parameter] public EquatableDictionary<string, object>? Input { get; set; }
[Parameter] public EquatableDictionary<string, string>? Labels { get; set; }
[Parameter] public EquatableDictionary<string, string>? Annotations { get; set; }
[Parameter] public EventCallback<CreateWorkflowInstanceParameters> OnCreate { get; set; }
[Parameter] public EventCallback<ProblemDetails> OnProblem { get; set; }

void OnValueChanged(object? value)
{
payload = value == null ? string.Empty : JsonSerializer.SerializeToText(value);
parameters.Input = value == null ? string.Empty : JsonSerializer.SerializeToText(value);
}

void OnTextChanged(string value)
{
payload = value;
parameters.Input = value;
}

protected override async Task OnParametersSetAsync()
{
await base.OnParametersSetAsync();
if (input != Input) input = Input;
if (operatorName != OperatorName) operatorName = OperatorName;
if (Labels != null && Labels.Count > 0) SetLabels(Labels);
else SetLabels(null);
if (Annotations != null && Annotations.Count > 0) SetAnnotations(Annotations);
else SetAnnotations(null);
if (workflowDefinition != WorkflowDefinition)
{
workflowDefinition = WorkflowDefinition;
Expand All @@ -116,16 +138,80 @@
}
}

protected void OnSelectOperatorChanged(ChangeEventArgs e)
protected void SetLabels(EquatableDictionary<string, string>? labels)
{
parameters.Labels = labels != null ? [.. labels] : [];
if (labels != null) operatorName = labels.TryGetValue(SynapseDefaults.Resources.Labels.Operator, out var label) ? label : null;
}

protected void AddLabel(string key, string value)
{
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value))
{
return;
}
if (parameters.Labels.ContainsKey(key))
{
parameters.Labels.Remove(key);
}
parameters.Labels.Add(key, value);
}

protected void RemoveLabel(string key)
{
if (string.IsNullOrWhiteSpace(key))
{
return;
}
if (parameters.Labels.ContainsKey(key))
{
parameters.Labels.Remove(key);
}
}

protected void SetOperator(string? operatorName)
{
if (string.IsNullOrEmpty(operatorName))
RemoveLabel(SynapseDefaults.Resources.Labels.Operator);
else
AddLabel(SynapseDefaults.Resources.Labels.Operator, operatorName);
}

protected void SetAnnotations(EquatableDictionary<string, string>? annotations)
{
parameters.Annotations = annotations != null ? [.. annotations] : [];
}

protected void AddAnnotation(string key, string value)
{
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value))
{
return;
}
if (parameters.Annotations.ContainsKey(key))
{
parameters.Annotations.Remove(key);
}
parameters.Annotations.Add(key, value);
}

protected void RemoveAnnotation(string key)
{
operatorName = e.Value?.ToString();
if (string.IsNullOrWhiteSpace(key))
{
return;
}
if (parameters.Annotations.ContainsKey(key))
{
parameters.Annotations.Remove(key);
}
}

async Task OnStartAsync()
{
if (schema != null)
{
var node = string.IsNullOrWhiteSpace(payload) ? null : JsonSerializer.Deserialize<JsonNode>(payload);
var node = string.IsNullOrWhiteSpace(parameters.Input) ? null : JsonSerializer.Deserialize<JsonNode>(parameters.Input);
var evaluationOptions = new EvaluationOptions()
{
OutputFormat = OutputFormat.List
Expand All @@ -141,10 +227,7 @@
}
if (OnCreate.HasDelegate)
{
await OnCreate.InvokeAsync(new CreateWorkflowInstanceParameters() {
Input = payload,
Operator = operatorName
});
await OnCreate.InvokeAsync(parameters);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ public record CreateWorkflowInstanceParameters
public string? Input { get; set; }

/// <summary>
/// Gets/sets the workflow instance operator.
/// Gets/sets the workflow instance's labels.
/// </summary>
public string? Operator { get; set; }
public EquatableDictionary<string, string> Labels { get; set; } = [];

/// <summary>
/// Gets/sets the workflow instance's annotations.
/// </summary>
public EquatableDictionary<string, string> Annotations { get; set; } = [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
@*
Copyright © 2024-Present The Synapse Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*@

@namespace Synapse.Dashboard.Components

<div class="fw-bolder mb-2">@Title:</div>
<div class="border rounded p-3">
<table class="table">
<tbody>
<tr>
<td class="ps-0 py-0 pe-2">
<input name="label-key" type="text" class="form-control border-0" placeholder="@KeyPlaceholder" @bind="newKey" />
</td>
<td class="ps-0 py-0 pe-2">
<input name="label-value" type="text" class="form-control border-0" placeholder="@ValuePlaceholder" @bind="newValue" />
</td>
<td class="text-end pt-2 fit">
<Button Color="ButtonColor.Primary" Size="ButtonSize.Small" Outline="true" class="m-1" @onclick="_ => AddLabel()">
<Icon Color="IconColor.Primary" Name="IconName.Plus" />
</Button>
</td>
</tr>
@foreach (KeyValuePair<string, string> entry in Entries)
{
<tr>
<td class="pt-2 ps-2">@entry.Key</td>
<td class="pt-2 ps-2">@entry.Value</td>
<td class="text-end pt-2 fit">
<Button Color="ButtonColor.Primary" Size="ButtonSize.Small" Outline="true" class="m-1" @onclick="() => RemoveLabel(entry.Key)">
<Icon Color="IconColor.Primary" Name="IconName.Trash" />
</Button>
</td>
</tr>
}
</tbody>
</table>
</div>


@code {
private string newKey = string.Empty;
private string newValue = string.Empty;

[Parameter] public string Title { get; set; } = "Labels";
[Parameter] public string KeyPlaceholder { get; set; } = "Enter label key";
[Parameter] public string ValuePlaceholder { get; set; } = "Enter label value";
[Parameter] public EquatableDictionary<string, string> Entries { get; set; } = new();
[Parameter] public EventCallback<KeyValuePair<string, string>> OnAddEntry { get; set; }
[Parameter] public EventCallback<string> OnRemoveEntry { get; set; }

private void AddLabel()
{
if (string.IsNullOrWhiteSpace(newKey) || string.IsNullOrWhiteSpace(newValue)) return;
var entry = new KeyValuePair<string, string>(newKey.Trim(), newValue.Trim());
if (OnAddEntry.HasDelegate)
{
OnAddEntry.InvokeAsync(entry);
}
newKey = string.Empty;
newValue = string.Empty;
}

private void RemoveLabel(string key)
{
if (string.IsNullOrWhiteSpace(key)) return;
if (OnRemoveEntry.HasDelegate)
{
OnRemoveEntry.InvokeAsync(key);
}
}

}
15 changes: 10 additions & 5 deletions src/dashboard/Synapse.Dashboard/Pages/Workflows/Create/State.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ public record CreateWorkflowViewState
/// </summary>
public string? WorkflowDefinitionText { get; set; }

/// <summary>
/// Gets/sets the workflow's labels
/// </summary>
public EquatableDictionary<string, string> Labels { get; set; } = [];

/// <summary>
/// Gets/sets the workflow's annotations
/// </summary>
public EquatableDictionary<string, string> Annotations { get; set; } = [];

/// <summary>
/// Gets/sets a boolean indicating whether or not the state is being loaded
/// </summary>
Expand Down Expand Up @@ -102,11 +112,6 @@ public record CreateWorkflowViewState
/// </summary>
public EquatableList<Operator>? Operators { get; set; }

/// <summary>
/// Gets/sets the active operator filter
/// </summary>
public string? Operator { get; set; } = null;

/// <summary>
/// Gets/sets the list of <see cref="ProblemDetails"/> errors that occurred when trying to save the resource, if any
/// </summary>
Expand Down
Loading