Skip to content

Commit 2adf2b7

Browse files
committed
fix(Dashboard): added workflow input schema validation
Signed-off-by: Jean-Baptiste Bianchi <[email protected]>
1 parent 3296e52 commit 2adf2b7

File tree

18 files changed

+165
-67
lines changed

18 files changed

+165
-67
lines changed

src/dashboard/Synapse.Dashboard/Components/MonacoEditor/IMonacoEditorHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,6 @@ public interface IMonacoEditorHelper
6868
/// Generates a unique resource URI for <see cref="TextModel"/>
6969
/// </summary>
7070
/// <returns></returns>
71-
string GetResourceUri() { return $"inmemory://random-resource-{GetNextModelIndex()}"; }
71+
string GetResourceUri(string resourceType = "unknown") { return $"inmemory://{resourceType}-{GetNextModelIndex()}"; }
7272

7373
}

src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditor.razor

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,20 @@
4848
/// The <see cref="EventCallback"/>see called when the text changes
4949
/// </summary>
5050
[Parameter] public EventCallback<string> OnTextChanged { get; set; }
51+
/// <summary>
52+
/// The document's model name, if any
53+
/// </summary>
54+
[Parameter] public string ModelName { get; set; } = string.Empty;
5155

5256
/// <inheritdoc/>
5357
protected override async Task OnParametersSetAsync()
5458
{
5559
await base.OnParametersSetAsync();
5660
Store.SetIsReadOnly(IsReadOnly);
61+
if (!string.IsNullOrWhiteSpace(ModelName))
62+
{
63+
Store.SetTexModelName(ModelName);
64+
}
5765
if (Document != null)
5866
{
5967
Store.SetDocument(Document);

src/dashboard/Synapse.Dashboard/Components/MonacoEditor/State.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,9 @@ public record MonacoEditorState
2727
/// Gets/sets a boolean indicating the editor is read-only
2828
/// </summary>
2929
public bool IsReadOnly { get; set; } = false;
30+
31+
/// <summary>
32+
/// Gets/sets the document's model name, if any
33+
/// </summary>
34+
public string ModelName { get; set; } = string.Empty;
3035
}

src/dashboard/Synapse.Dashboard/Components/MonacoEditor/Store.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ IYamlSerializer yamlSerializer
3434
{
3535

3636
private TextModel? _textModel = null;
37-
private readonly string _textModelUri = monacoEditorHelper.GetResourceUri();
37+
private string _textModelUri = monacoEditorHelper.GetResourceUri();
3838

3939
/// <summary>
4040
/// Gets the service used to interact with the Synapse API
@@ -170,6 +170,22 @@ public void SetDocumentYaml(string? documentYaml)
170170
DocumentText = documentText
171171
});
172172
}
173+
/// <summary>
174+
/// Sets the state's <see cref="MonacoEditorState.ModelName"/>
175+
/// </summary>
176+
/// <param name="modelName">The new value</param>
177+
public void SetTexModelName(string modelName)
178+
{
179+
var current = this.Get(state => state.ModelName);
180+
if (current != modelName)
181+
{
182+
this.Reduce(state => state with
183+
{
184+
ModelName = modelName
185+
});
186+
this._textModelUri = !string.IsNullOrEmpty(modelName) ? monacoEditorHelper.GetResourceUri(modelName) : monacoEditorHelper.GetResourceUri();
187+
}
188+
}
173189
#endregion
174190

175191
#region Actions

src/dashboard/Synapse.Dashboard/Components/ResourceManagement/NamespacedResourceManagementComponent.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,39 @@ public abstract class NamespacedResourceManagementComponent<TComponent, TStore,
2929
{
3030

3131
/// <summary>
32-
/// Gets the list of available <see cref="Namespace"/>s
32+
/// Gets the list of available <see cref="@namespace"/>s
3333
/// </summary>
3434
protected EquatableList<Namespace>? Namespaces { get; set; }
3535

3636
/// <summary>
37-
/// Gets current namespace
37+
/// Gets/sets current namespace
3838
/// </summary>
39-
protected string? Namespace { get; set; }
39+
public string? @namespace;
40+
/// <summary>
41+
/// Gets/sets current namespace
42+
/// </summary>
43+
[Parameter] public string? Namespace { get; set; }
4044

4145
/// <inheritdoc/>
4246
protected override async Task OnInitializedAsync()
4347
{
4448
await base.OnInitializedAsync();
45-
this.Store.Namespace.Subscribe(@namespace => this.OnStateChanged(cmp => cmp.Namespace = @namespace), token: this.CancellationTokenSource.Token);
46-
this.Store.Namespaces.Subscribe(namespaces => this.OnStateChanged(cmp => cmp.Namespaces = namespaces), token: this.CancellationTokenSource.Token);
49+
this.Store.Namespace.Subscribe(value => this.OnStateChanged(_ =>
50+
{
51+
this.@namespace = value;
52+
if (Namespace != value) this.Namespace = value;
53+
}), token: this.CancellationTokenSource.Token);
54+
this.Store.Namespaces.Subscribe(value => this.OnStateChanged(_ => Namespaces = value), token: this.CancellationTokenSource.Token);
55+
}
56+
57+
/// <inheritdoc/>
58+
protected override async Task OnParametersSetAsync()
59+
{
60+
if (Namespace != @namespace)
61+
{
62+
Store.SetNamespace(Namespace);
63+
}
64+
await base.OnParametersSetAsync();
4765
}
4866

4967
/// <summary>

src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponent.cs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,49 @@ public abstract class ResourceManagementComponent<TComponent, TStore, TState, TR
7777
/// </summary>
7878
protected bool Loading { get; set; } = false;
7979

80+
string activeResourcesName = null!;
81+
/// <summary>
82+
/// Gets/sets the name of the active resource
83+
/// </summary>
84+
[Parameter] public string? Name { get; set; }
85+
8086
/// <inheritdoc/>
8187
protected override async Task OnInitializedAsync()
8288
{
8389
await base.OnInitializedAsync().ConfigureAwait(false);
84-
this.Store.Resources.Subscribe(resources => this.OnStateChanged(cmp => cmp.Resources = resources), token: this.CancellationTokenSource.Token);
85-
this.Store.SearchTerm.Subscribe(searchTerm => this.OnStateChanged(cmp => cmp.SearchTerm = searchTerm), token: this.CancellationTokenSource.Token);
86-
this.Store.Loading.Subscribe(loading => this.OnStateChanged(cmp => cmp.Loading = loading), token: this.CancellationTokenSource.Token);
90+
this.Store.Resources.Subscribe(value => this.OnStateChanged(_ => this.Resources = value), token: this.CancellationTokenSource.Token);
91+
this.Store.ActiveResourceName.Subscribe(value => this.OnStateChanged(_ => this.activeResourcesName = value), token: this.CancellationTokenSource.Token);
92+
this.Store.SearchTerm.Subscribe(value => this.OnStateChanged(_ => this.SearchTerm = value), token: this.CancellationTokenSource.Token);
93+
this.Store.Loading.Subscribe(value => this.OnStateChanged(_ => this.Loading = value), token: this.CancellationTokenSource.Token);
8794
this.Store.Definition.SubscribeAsync(async definition =>
8895
{
8996
if (this.Definition != definition)
9097
{
9198
this.Definition = definition;
9299
if (this.Definition != null && this.MonacoInterop != null)
93100
{
94-
await this.MonacoInterop.AddValidationSchemaAsync(this.Serializer.SerializeToText(this.Definition.Spec.Versions.First().Schema.OpenAPIV3Schema ?? new JsonSchemaBuilder().Build()), $"https://synapse.io/schemas/{typeof(TResource).Name.ToLower()}.json", $"{typeof(TResource).Name.ToLower()}").ConfigureAwait(false);
101+
await this.MonacoInterop.AddValidationSchemaAsync(this.Serializer.SerializeToText(this.Definition.Spec.Versions.First().Schema.OpenAPIV3Schema ?? new JsonSchemaBuilder().Build()), $"https://synapse.io/schemas/{typeof(TResource).Name.ToLower()}.json", $"{typeof(TResource).Name.ToLower()}*").ConfigureAwait(false);
95102
}
96103
}
97104
}, cancellationToken: this.CancellationTokenSource.Token);
105+
this.Store.ActiveResource.SubscribeAsync(async resource =>
106+
{
107+
if (resource != null)
108+
{
109+
await this.OnShowResourceDetailsAsync(resource);
110+
}
111+
}, cancellationToken: CancellationTokenSource.Token);
112+
}
113+
114+
115+
/// <inheritdoc/>
116+
protected override async Task OnParametersSetAsync()
117+
{
118+
if (!string.IsNullOrEmpty(Name) && Name != activeResourcesName)
119+
{
120+
Store.SetActiveResourceName(Name);
121+
}
122+
await base.OnParametersSetAsync();
98123
}
99124

100125
/// <summary>

src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponentState.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,9 @@ public record ResourceManagementComponentState<TResource>
4646
/// </summary>
4747
public bool Loading { get; set; } = false;
4848

49+
/// <summary>
50+
/// Gets/sets the name of the selected resource
51+
/// </summary>
52+
public string ActiveResourceName { get; set; } = string.Empty;
53+
4954
}

src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponentStoreBase.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,21 @@ public abstract class ResourceManagementComponentStoreBase<TState, TResource>(IS
8181
.Select(labelSelectors => new ResourcesFilter() { LabelSelectors = labelSelectors })
8282
.DistinctUntilChanged();
8383

84+
/// <summary>
85+
/// Gets an <see cref="IObservable{T}"/> used to observe the <see cref="ResourceManagementComponentState{TResource}.ActiveResourceName"/> changes
86+
/// </summary>
87+
public virtual IObservable<string> ActiveResourceName => this.Select(state => state.ActiveResourceName).DistinctUntilChanged();
88+
89+
/// <summary>
90+
/// Gets an <see cref="IObservable{T}"/> used to observe the active resource changes
91+
/// </summary>
92+
public virtual IObservable<TResource?> ActiveResource => Observable.CombineLatest(
93+
this.Resources.Where(resources => resources != null),
94+
this.ActiveResourceName.Where(name => !string.IsNullOrWhiteSpace(name)),
95+
(resources, name) => resources!.FirstOrDefault(r => r.GetName() == name)
96+
)
97+
.DistinctUntilChanged();
98+
8499
/// <summary>
85100
/// Gets the service used to interact with the Synapse API
86101
/// </summary>
@@ -127,19 +142,31 @@ public override async Task InitializeAsync()
127142
/// Sets the <see cref="ResourceManagementComponentState{TResource}.SearchTerm" />
128143
/// </summary>
129144
/// <param name="searchTerm">The new search term</param>
130-
public void SetSearchTerm(string? searchTerm)
145+
public virtual void SetSearchTerm(string? searchTerm)
131146
{
132147
this.Reduce(state => state with
133148
{
134149
SearchTerm = searchTerm
135150
});
136151
}
137152

153+
/// <summary>
154+
/// Sets the <see cref="ResourceManagementComponentState{TResource}.ActiveResourceName" />
155+
/// </summary>
156+
/// <param name="activeResourceName">The new value</param>
157+
public virtual void SetActiveResourceName(string activeResourceName)
158+
{
159+
this.Reduce(state => state with
160+
{
161+
ActiveResourceName = activeResourceName ?? string.Empty
162+
});
163+
}
164+
138165
/// <summary>
139166
/// Sets the <see cref="ResourceManagementComponentState{TResource}.LabelSelectors" />
140167
/// </summary>
141168
/// <param name="labelSelectors">The new label selectors</param>
142-
public void SetLabelSelectors(EquatableList<LabelSelector>? labelSelectors)
169+
public virtual void SetLabelSelectors(EquatableList<LabelSelector>? labelSelectors)
143170
{
144171
this.Reduce(state => state with
145172
{
@@ -151,7 +178,7 @@ public void SetLabelSelectors(EquatableList<LabelSelector>? labelSelectors)
151178
/// Adds a single <see cref="LabelSelector" />
152179
/// </summary>
153180
/// <param name="labelSelector">The label selector to add</param>
154-
public void AddLabelSelector(LabelSelector labelSelector)
181+
public virtual void AddLabelSelector(LabelSelector labelSelector)
155182
{
156183
if (labelSelector == null)
157184
{

src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceCreation/WorkflowInstanceCreation.razor

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
*@
1616

1717
@namespace Synapse.Dashboard.Components
18+
@using ServerlessWorkflow.Sdk.Models
19+
@inject MonacoInterop MonacoInterop
20+
@inject IJsonSerializer Serializer
1821

19-
<MonacoEditor OnTextChanged="OnTextChanged" />
22+
<MonacoEditor OnTextChanged="OnTextChanged" ModelName="@modelName" />
2023
<div class="text-center">
2124
<Button Outline="true" Color="ButtonColor.Primary" class="m-auto mt-3" @onclick="async (_) => await OnStart()">
2225
<Icon Name="IconName.Play" />
@@ -25,21 +28,34 @@
2528
</div>
2629

2730
@code {
28-
private string _input = string.Empty;
31+
string payload = string.Empty;
32+
string modelName = string.Empty;
2933

34+
[Parameter] public WorkflowDefinition? WorkflowDefinition { get; set; }
3035
[Parameter] public EventCallback<string> OnCreate { get; set; }
3136

3237
void OnTextChanged(string value)
3338
{
34-
_input = value;
39+
payload = value;
3540
}
3641

3742
async Task OnStart()
3843
{
3944
if (OnCreate.HasDelegate)
4045
{
41-
await OnCreate.InvokeAsync(_input);
46+
await OnCreate.InvokeAsync(payload);
4247
}
4348
}
4449

50+
protected override async Task OnParametersSetAsync()
51+
{
52+
await base.OnParametersSetAsync();
53+
if (this.WorkflowDefinition?.Input?.Schema?.Document != null)
54+
{
55+
modelName = this.WorkflowDefinition.Document.Name + "-" + this.WorkflowDefinition.Document.Version;
56+
await this.MonacoInterop.AddValidationSchemaAsync(this.Serializer.SerializeToText(this.WorkflowDefinition.Input.Schema.Document), $"https://synapse.io/schemas/{modelName}.json", $"{modelName}*").ConfigureAwait(false);
57+
}
58+
59+
}
60+
4561
}

src/dashboard/Synapse.Dashboard/Pages/Correlations/List/View.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
{
2222
foreach (var namespaceResource in Namespaces)
2323
{
24-
<option value="@namespaceResource.GetName()">@namespaceResource.GetName()</option>
24+
<option value="@namespaceResource.GetName()" selected="@(@namespaceResource.GetName() == @namespace)">@namespaceResource.GetName()</option>
2525
}
2626
}
2727
</select>

0 commit comments

Comments
 (0)