Skip to content

Commit 9d7b1ec

Browse files
committed
feat(Dashboard): Worked on Workflow Instance list and detail view (WIP)
Signed-off-by: Charles d'Avernas <charles.davernas@neuroglia.io>
1 parent 6cdf503 commit 9d7b1ec

File tree

37 files changed

+357
-102
lines changed

37 files changed

+357
-102
lines changed

src/api/Synapse.Api.Application/Extensions/IServiceCollectionExtensions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ public static IServiceCollection AddApiCommands(this IServiceCollection services
126126
handlerImplementationType = typeof(PatchResourceCommandHandler<>).MakeGenericType(resourceType);
127127
services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime));
128128

129+
commandType = typeof(PatchResourceStatusCommand<>).MakeGenericType(resourceType);
130+
resultType = typeof(IOperationResult<>).MakeGenericType(resourceType);
131+
handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType);
132+
handlerImplementationType = typeof(PatchResourceStatusCommandHandler<>).MakeGenericType(resourceType);
133+
services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime));
134+
129135
commandType = typeof(DeleteResourceCommand<>).MakeGenericType(resourceType);
130136
resultType = typeof(IOperationResult<>).MakeGenericType(resourceType);
131137
handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType);

src/api/Synapse.Api.Client.Http/Services/SynapseHttpApiClient.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ public class SynapseHttpApiClient
3232
/// <param name="serviceProvider">The current <see cref="IServiceProvider"/></param>
3333
/// <param name="loggerFactory">The service used to create <see cref="ILogger"/>s</param>
3434
/// <param name="serializer">The service used to serialize/deserialize objects to/from JSON</param>
35-
/// <param name="httpClient">The service used to perform http requests</param>
36-
public SynapseHttpApiClient(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IJsonSerializer serializer, HttpClient httpClient)
35+
/// <param name="httpClientFactory">The service used to create <see cref="System.Net.Http.HttpClient"/>s</param>
36+
public SynapseHttpApiClient(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IJsonSerializer serializer, IHttpClientFactory httpClientFactory)
3737
{
3838
this.ServiceProvider = serviceProvider;
3939
this.Logger = loggerFactory.CreateLogger(GetType());
4040
this.Serializer = serializer;
41-
this.HttpClient = httpClient;
41+
this.HttpClient = httpClientFactory.CreateClient(typeof(SynapseHttpApiClient).Name);
4242
this.WorkflowData = ActivatorUtilities.CreateInstance<DocumentHttpApiClient>(this.ServiceProvider, this.HttpClient);
4343
foreach (var apiProperty in GetType().GetProperties().Where(p => p.CanRead && p.PropertyType.GetGenericType(typeof(IResourceApiClient<>)) != null))
4444
{

src/api/Synapse.Api.Client.Http/Synapse.Api.Client.Http.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.4" />
13-
<PackageReference Include="System.Reactive" Version="6.0.0" />
12+
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.6" />
13+
<PackageReference Include="System.Reactive" Version="6.0.1" />
1414
</ItemGroup>
1515

1616
<ItemGroup>

src/api/Synapse.Api.Http/Synapse.Api.Http.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Neuroglia.Mediation.AspNetCore" Version="4.9.15" />
14-
<PackageReference Include="Neuroglia.Security.AspNetCore" Version="4.9.15" />
13+
<PackageReference Include="Neuroglia.Mediation.AspNetCore" Version="4.10.0" />
14+
<PackageReference Include="Neuroglia.Security.AspNetCore" Version="4.10.0" />
1515
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.6.2" />
1616
</ItemGroup>
1717

src/api/Synapse.Api.Server/Synapse.Api.Server.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16-
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.5" />
16+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.6" />
1717
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
1818
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.6.2" />
1919
</ItemGroup>

src/core/Synapse.Core.Infrastructure/Extensions/IServiceCollectionExtensions.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using Microsoft.Extensions.Configuration;
22
using Microsoft.Extensions.DependencyInjection;
33
using Neuroglia.Data.Infrastructure;
4-
using Neuroglia.Data.Infrastructure.Mongo.Services;
4+
using Neuroglia.Data.Infrastructure.Redis.Services;
55
using Neuroglia.Data.Infrastructure.ResourceOriented.Redis;
66
using Neuroglia.Data.Infrastructure.ResourceOriented.Services;
77
using Neuroglia.Data.Infrastructure.Services;
@@ -33,8 +33,8 @@ public static IServiceCollection AddSynapse(this IServiceCollection services, IC
3333
services.AddSerialization();
3434
services.AddJsonSerializer();
3535
services.AddYamlDotNetSerializer();
36-
services.AddScoped<IUserInfoProvider, UserInfoProvider>();
3736
services.AddMediator();
37+
services.AddScoped<IUserInfoProvider, UserInfoProvider>();
3838
services.AddServerlessWorkflowIO();
3939
services.AddPluginProvider();
4040

@@ -44,9 +44,8 @@ public static IServiceCollection AddSynapse(this IServiceCollection services, IC
4444
if (!string.IsNullOrWhiteSpace(redisConnectionString)) services.AddRedisDatabase(redisConnectionString, ServiceLifetime.Singleton);
4545
services.AddHostedService<Core.Infrastructure.Services.DatabaseInitializer>();
4646

47-
services.AddPlugin(typeof(IRepository<Document>), provider => provider.GetRequiredService<MongoRepository<Document, string>>(), serviceLifetime: ServiceLifetime.Scoped);
48-
services.AddMongoDatabase("synapse");
49-
services.AddMongoRepository<Document, string>(lifetime: ServiceLifetime.Scoped);
47+
services.AddPlugin(typeof(IRepository<Document>), provider => provider.GetRequiredService<RedisRepository<Document, string>>(), serviceLifetime: ServiceLifetime.Scoped);
48+
services.AddRedisRepository<Document, string>(lifetime: ServiceLifetime.Scoped);
5049

5150
services.AddScoped<IResourceRepository, ResourceRepository>();
5251
services.AddScoped<IAdmissionControl, AdmissionControl>();

src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="IdentityModel" Version="7.0.0" />
13-
<PackageReference Include="Neuroglia.Data.Expressions.Abstractions" Version="4.9.15" />
14-
<PackageReference Include="Neuroglia.Data.Infrastructure.Mongo" Version="4.9.15" />
15-
<PackageReference Include="Neuroglia.Data.Infrastructure.ResourceOriented.Redis" Version="4.9.15" />
16-
<PackageReference Include="Neuroglia.Mediation" Version="4.9.15" />
17-
<PackageReference Include="Neuroglia.Plugins" Version="4.9.15" />
13+
<PackageReference Include="Neuroglia.Data.Expressions.Abstractions" Version="4.10.0" />
14+
<PackageReference Include="Neuroglia.Data.Infrastructure.Redis" Version="4.10.0" />
15+
<PackageReference Include="Neuroglia.Data.Infrastructure.ResourceOriented.Redis" Version="4.10.0" />
16+
<PackageReference Include="Neuroglia.Mediation" Version="4.10.0" />
17+
<PackageReference Include="Neuroglia.Plugins" Version="4.10.0" />
1818
<PackageReference Include="ServerlessWorkflow.Sdk.IO" Version="1.0.0-alpha1" />
1919
</ItemGroup>
2020

src/core/Synapse.Core/Resources/Document.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
using MongoDB.Bson.Serialization.Attributes;
15-
1614
namespace Synapse.Resources;
1715

1816
/// <summary>
@@ -38,7 +36,7 @@ public record Document
3836
/// Gets the document's content
3937
/// </summary>
4038
[Required]
41-
[DataMember(Name = "content", Order = 3), JsonPropertyName("content"), JsonPropertyOrder(3), YamlMember(Alias = "content", Order = 3), BsonSerializer(typeof(Serialization.Bson.ObjectSerializer))]
39+
[DataMember(Name = "content", Order = 3), JsonPropertyName("content"), JsonPropertyOrder(3), YamlMember(Alias = "content", Order = 3)]
4240
public required virtual object Content { get; set; } = null!;
4341

4442
[IgnoreDataMember, JsonIgnore, YamlIgnore]

src/core/Synapse.Core/Serialization/Bson/ObjectSerializer.cs

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/core/Synapse.Core/Synapse.Core.csproj

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@
2626
</ItemGroup>
2727

2828
<ItemGroup>
29-
<PackageReference Include="MongoDB.Bson" Version="2.25.0" />
30-
<PackageReference Include="Neuroglia.Data.Infrastructure.ResourceOriented" Version="4.9.15" />
31-
<PackageReference Include="Neuroglia.Eventing.CloudEvents" Version="4.9.15" />
29+
<PackageReference Include="Neuroglia.Data.Infrastructure.ResourceOriented" Version="4.10.0" />
30+
<PackageReference Include="Neuroglia.Eventing.CloudEvents" Version="4.10.0" />
3231
<PackageReference Include="Semver" Version="2.3.0" />
3332
<PackageReference Include="ServerlessWorkflow.Sdk" Version="1.0.0-alpha1" />
3433
</ItemGroup>
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
@inject NavigationManager Navigation
2+
@inject IMonacoEditorHelper MonacoEditorHelper
3+
@inject IJSRuntime JSRuntime
4+
@inject Synapse.Api.Client.Services.ISynapseApiClient Api
5+
@inject IJsonSerializer Serializer
6+
7+
@namespace Synapse.Dashboard.Components
8+
9+
<div class="card mb-3">
10+
<h5 class="card-header">Metadata</h5>
11+
<div class="card-body">
12+
<table class="table table-striped mb-3">
13+
<tbody>
14+
<tr>
15+
<td>Name</td>
16+
<td>@workflowInstance?.GetName()</td>
17+
</tr>
18+
<tr>
19+
<td>Namespace</td>
20+
<td>@workflowInstance?.GetNamespace()</td>
21+
</tr>
22+
<tr>
23+
<td>Created At</td>
24+
<td>@workflowInstance?.Metadata.CreationTimestamp?.ToString("R")</td>
25+
</tr>
26+
<tr>
27+
<td>Generation</td>
28+
<td>@workflowInstance?.Metadata.Generation</td>
29+
</tr>
30+
<tr>
31+
<td>Labels</td>
32+
<td>
33+
@if (workflowInstance?.Metadata.Labels?.Any() == true)
34+
{
35+
foreach (var label in workflowInstance.Metadata.Labels)
36+
{
37+
<span class="badge bg-primary text-dark m-1">@label.Key: @label.Value</span>
38+
}
39+
}
40+
</td>
41+
</tr>
42+
</tbody>
43+
</table>
44+
</div>
45+
</div>
46+
47+
<div class="card mb-3">
48+
<h5 class="card-header">Spec</h5>
49+
<div class="card-body">
50+
<table class="table table-striped mb-3">
51+
<tbody>
52+
<tr>
53+
<td>Definition</td>
54+
<td><a href="#" @onclick="OnNavigateToDefinition">@workflowInstance!.Definition.ToString()</a></td>
55+
</tr>
56+
</tbody>
57+
</table>
58+
</div>
59+
</div>
60+
61+
<div class="card mb-3">
62+
<h5 class="card-header">Status</h5>
63+
<div class="card-body">
64+
<table class="table table-striped mb-3">
65+
<tbody>
66+
<tr>
67+
<td>Phase</td>
68+
<td><span class="badge rounded-pill badge rounded-pill border @GetStatusClass()">@workflowInstance.Status?.Phase</span></td>
69+
</tr>
70+
<tr>
71+
<td>Context Data</td>
72+
<td><a href="#" @onclick:preventDefault>Load</a></td>
73+
</tr>
74+
<tr>
75+
<td>Output</td>
76+
<td><a href="#" @onclick="LoadOutputAsync" @onclick:preventDefault>Load</a></td>
77+
</tr>
78+
<tr>
79+
<td colspan="2">
80+
<PreferredLanguageSelector PreferedLanguageChange="ToggleTextBasedEditorLanguageAsync" />
81+
<StandaloneCodeEditor @ref="textBasedEditor"
82+
ConstructionOptions="MonacoEditorHelper.GetStandaloneEditorConstructionOptions(this.textEditorValue, false, this.MonacoEditorHelper.PreferredLanguage)"
83+
OnDidInit="OnTextBasedEditorInitAsync"
84+
OnDidChangeModelContent="OnTextBasedValueChangedAsync"
85+
CssClass="h-100-px" />
86+
</td>
87+
</tr>
88+
<tr>
89+
<td>Error</td>
90+
<td>@workflowInstance.Status?.Error</td>
91+
</tr>
92+
</tbody>
93+
</table>
94+
</div>
95+
</div>
96+
97+
@code {
98+
99+
bool contextLoaded;
100+
bool outputLoaded;
101+
StandaloneCodeEditor? textBasedEditor;
102+
WorkflowInstance? workflowInstance;
103+
TextModel? textEditorModel;
104+
Subject<string> textEditorInput = new();
105+
string textEditorValue = string.Empty;
106+
bool updating = false;
107+
108+
/// <summary>
109+
/// Gets/sets the workflow instance to display details about
110+
/// </summary>
111+
[Parameter] public WorkflowInstance? WorkflowInstance { get; set; }
112+
113+
string GetStatusClass()
114+
{
115+
return this.workflowInstance?.Status?.Phase switch
116+
{
117+
WorkflowInstanceStatusPhase.Pending => "border-secondary text-secondary",
118+
WorkflowInstanceStatusPhase.Running => "border-primary text-primary",
119+
WorkflowInstanceStatusPhase.Faulted => "border-danger text-danger",
120+
WorkflowInstanceStatusPhase.Cancelled => "border-warning text-warning",
121+
WorkflowInstanceStatusPhase.Completed => "border-success text-success",
122+
_ => "border-secondary text-secondary"
123+
};
124+
}
125+
126+
void OnNavigateToDefinition()
127+
{
128+
if (this.workflowInstance == null) return;
129+
this.Navigation.NavigateTo($"/workflows/{this.workflowInstance.Spec.Definition.Namespace}/{this.workflowInstance.Spec.Definition.Name}/{this.workflowInstance.Spec.Definition.Version}");
130+
}
131+
132+
async Task LoadOutputAsync()
133+
{
134+
if (string.IsNullOrWhiteSpace(this.workflowInstance?.Status?.OutputReference)) return;
135+
var document = await this.Api.WorkflowData.GetAsync(this.workflowInstance.Status.OutputReference);
136+
this.outputLoaded = true;
137+
this.StateHasChanged();
138+
var json = this.Serializer.SerializeToText(document.Content);
139+
await this.textBasedEditor.SetValue(json);
140+
}
141+
142+
async Task SetTextEditorValueAsync()
143+
{
144+
if (this.textBasedEditor != null)
145+
{
146+
var editorText = await this.textBasedEditor.GetValue();
147+
if (this.textEditorValue != editorText) await this.textBasedEditor.SetValue(this.textEditorValue);
148+
}
149+
}
150+
151+
async Task SetTextBasedEditorLanguageAsync()
152+
{
153+
if (this.textBasedEditor != null && this.textEditorModel != null)
154+
{
155+
await Global.SetModelLanguage(this.JSRuntime, this.textEditorModel, this.MonacoEditorHelper.PreferredLanguage);
156+
}
157+
}
158+
159+
async Task ToggleTextBasedEditorLanguageAsync(string language)
160+
{
161+
var model = await this.textBasedEditor!.GetModel();
162+
var editorLanguage = await model.GetLanguageId();
163+
if (editorLanguage != language)
164+
{
165+
await this.SetTextBasedEditorLanguageAsync();
166+
}
167+
}
168+
169+
async Task OnTextBasedEditorInitAsync()
170+
{
171+
var resourceUri = $"inmemory://{typeof(WorkflowInstance).Name.ToLower()}";
172+
this.textEditorModel = await Global.GetModel(this.JSRuntime, resourceUri);
173+
if (this.textEditorModel == null)
174+
{
175+
this.textEditorModel = await Global.CreateModel(this.JSRuntime, this.textEditorValue, this.MonacoEditorHelper.PreferredLanguage, resourceUri);
176+
await this.textBasedEditor!.SetModel(this.textEditorModel);
177+
}
178+
else
179+
{
180+
await this.SetTextEditorValueAsync();
181+
await this.SetTextBasedEditorLanguageAsync();
182+
}
183+
this.StateHasChanged();
184+
}
185+
186+
async Task OnTextBasedValueChangedAsync(ModelContentChangedEvent e)
187+
{
188+
if (!this.updating && this.textBasedEditor != null && this.textEditorInput != null)
189+
{
190+
var text = await this.textBasedEditor.GetValue();
191+
this.textEditorInput.OnNext(text);
192+
}
193+
}
194+
195+
/// <inheritdoc/>
196+
protected override Task OnParametersSetAsync()
197+
{
198+
if (this.workflowInstance != this.WorkflowInstance) this.workflowInstance = this.WorkflowInstance;
199+
return base.OnParametersSetAsync();
200+
}
201+
202+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.output {
2+
height: 200px;
3+
max-height: 200px;
4+
overflow-y: scroll;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.output{height:200px;max-height:200px;overflow-y:scroll;}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.output {
2+
height: 200px;
3+
max-height: 200px;
4+
overflow-y: scroll;
5+
}

0 commit comments

Comments
 (0)