Skip to content

Commit a0f29de

Browse files
committed
feat(Dashboard): Setting up dashboard (WIP)
Signed-off-by: Charles d'Avernas <charles.davernas@neuroglia.io>
1 parent 6658979 commit a0f29de

File tree

2,295 files changed

+74425
-51
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

2,295 files changed

+74425
-51
lines changed

Synapse.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Containeriz
5555
EndProject
5656
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runner", "src\runner\Synapse.Runner\Synapse.Runner.csproj", "{E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018}"
5757
EndProject
58+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Synapse.Dashboard.StateManagement", "src\dashboard\Synapse.Dashboard.StateManagement\Synapse.Dashboard.StateManagement.csproj", "{91EF9F64-4997-407C-B353-C26B1421D0FB}"
59+
EndProject
5860
Global
5961
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6062
Debug|Any CPU = Debug|Any CPU
@@ -125,6 +127,10 @@ Global
125127
{E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018}.Debug|Any CPU.Build.0 = Debug|Any CPU
126128
{E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018}.Release|Any CPU.ActiveCfg = Release|Any CPU
127129
{E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018}.Release|Any CPU.Build.0 = Release|Any CPU
130+
{91EF9F64-4997-407C-B353-C26B1421D0FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
131+
{91EF9F64-4997-407C-B353-C26B1421D0FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
132+
{91EF9F64-4997-407C-B353-C26B1421D0FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
133+
{91EF9F64-4997-407C-B353-C26B1421D0FB}.Release|Any CPU.Build.0 = Release|Any CPU
128134
EndGlobalSection
129135
GlobalSection(SolutionProperties) = preSolution
130136
HideSolutionNode = FALSE
@@ -153,6 +159,7 @@ Global
153159
{DC24E506-602F-4FD9-B8C0-CEA6B2AD8888} = {175CE1C5-FE17-4C8B-8823-E812BAD4E527}
154160
{F327B8F1-9A13-4924-AE1B-E69788AC73E7} = {175CE1C5-FE17-4C8B-8823-E812BAD4E527}
155161
{E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018} = {1DA47E5F-B23A-4D3C-96AA-4BD2662AB946}
162+
{91EF9F64-4997-407C-B353-C26B1421D0FB} = {7DF998B8-0FB1-470E-8ED0-EA1CC7B16901}
156163
EndGlobalSection
157164
GlobalSection(ExtensibilityGlobals) = postSolution
158165
SolutionGuid = {2A6C03D6-355A-4B39-9F2B-D0FDE429C0E2}

src/api/Synapse.Api.Client.Core/Services/IClusterResourceApiClient.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ public interface IClusterResourceApiClient<TResource>
1111
where TResource : class, IResource, new()
1212
{
1313

14+
/// <summary>
15+
/// Lists <see cref="IResource"/>s
16+
/// </summary>
17+
/// <param name="labelSelectors">An <see cref="IEnumerable{T}"/> containing the <see cref="LabelSelector"/>s used to select the <see cref="IResource"/>s to list by, if any</param>
18+
/// <param name="cancellationToken">A <see cref="CancellationToken"/></param>
19+
/// <returns>A new <see cref="IAsyncEnumerable{T}"/> used to asynchronously enumerate resulting <see cref="IResource"/>s</returns>
20+
Task<IAsyncEnumerable<TResource>> ListAsync(IEnumerable<LabelSelector>? labelSelectors = null, CancellationToken cancellationToken = default);
21+
1422
/// <summary>
1523
/// Gets the resource with the specified name
1624
/// </summary>
@@ -45,4 +53,4 @@ public interface IClusterResourceApiClient<TResource>
4553
/// <returns>A new awaitable <see cref="Task"/></returns>
4654
Task DeleteAsync(string name, CancellationToken cancellationToken = default);
4755

48-
}
56+
}

src/api/Synapse.Api.Client.Core/Services/IDocumentApiClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public interface IDocumentApiClient
1313
/// <param name="content">The <see cref="Document"/>'s content</param>
1414
/// <param name="cancellationToken">A <see cref="CancellationToken"/></param>
1515
/// <returns>The newly created <see cref="Document"/></returns>
16-
Task<Document> CreateAsync(string name, object? content, CancellationToken cancellationToken = default);
16+
Task<Document> CreateAsync(string name, object content, CancellationToken cancellationToken = default);
1717

1818
/// <summary>
1919
/// Gets the <see cref="Document"/> with the specified id

src/api/Synapse.Api.Client.Core/Services/INamespacedResourceApiClient.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ public interface INamespacedResourceApiClient<TResource>
1111
where TResource : class, IResource, new()
1212
{
1313

14+
/// <summary>
15+
/// Lists <see cref="IResource"/>s
16+
/// </summary>
17+
/// <param name="namespace">The namespace the <see cref="IResource"/>s to list belong to</param>
18+
/// <param name="labelSelectors">An <see cref="IEnumerable{T}"/> containing the <see cref="LabelSelector"/>s used to select the <see cref="IResource"/>s to list by, if any</param>
19+
/// <param name="cancellationToken">A <see cref="CancellationToken"/></param>
20+
/// <returns>A new <see cref="IAsyncEnumerable{T}"/> used to asynchronously enumerate resulting <see cref="IResource"/>s</returns>
21+
Task<IAsyncEnumerable<TResource>> ListAsync(string @namespace, IEnumerable<LabelSelector>? labelSelectors = null, CancellationToken cancellationToken = default);
22+
1423
/// <summary>
1524
/// Gets the resource with the specified name
1625
/// </summary>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace Synapse.Api.Client.Services;
2+
3+
/// <summary>
4+
/// Defines the fundamentals of a service used by clients to watch resource-related events
5+
/// </summary>
6+
public interface IResourceEventWatchHub
7+
{
8+
9+
/// <summary>
10+
/// Subscribes to events produced by resources of the specified type
11+
/// </summary>
12+
/// <param name="resourceDefinition">The type of resources to watch</param>
13+
/// <param name="namespace">The namespace the resources to watch belong to, if any</param>
14+
/// <returns>A new awaitable <see cref="Task"/></returns>
15+
Task Watch(ResourceDefinitionInfo resourceDefinition, string? @namespace = null);
16+
17+
/// <summary>
18+
/// Unsubscribes from events produced by resources of the specified type
19+
/// </summary>
20+
/// <param name="resourceDefinition">The type of resources to stop watching</param>
21+
/// <param name="namespace">The namespace the resources to stop watching belong to, if any</param>
22+
/// <returns>A new awaitable <see cref="Task"/></returns>
23+
Task StopWatching(ResourceDefinitionInfo resourceDefinition, string? @namespace = null);
24+
25+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace Synapse.Api.Client.Services;
2+
3+
/// <summary>
4+
/// Defines the fundamentals of a service used by clients to watch resource-related events
5+
/// </summary>
6+
public interface IResourceEventWatchHubClient
7+
{
8+
9+
/// <summary>
10+
/// Notifies clients about a resource-related event
11+
/// </summary>
12+
/// <param name="e">The <see cref="ResourceWatchEvent"/> that has been produced</param>
13+
/// <returns>A new awaitable <see cref="Task"/></returns>
14+
Task ResourceWatchEvent(object e);
15+
16+
}

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,4 @@
1313
<ProjectReference Include="..\..\core\Synapse.Core\Synapse.Core.csproj" />
1414
</ItemGroup>
1515

16-
<ItemGroup>
17-
<Folder Include="Services\Interfaces\" />
18-
</ItemGroup>
19-
2016
</Project>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace Synapse.Api.Client.Http.Configuration;
2+
3+
/// <summary>
4+
/// Represents the options used to configure a Synapse HTTP API client
5+
/// </summary>
6+
public class SynapseHttpApiClientOptions
7+
{
8+
9+
/// <summary>
10+
/// Gets/sets the base address of the Cloud Streams API to connect to
11+
/// </summary>
12+
public required virtual Uri BaseAddress { get; set; }
13+
14+
}

src/api/Synapse.Api.Client.Http/Extensions/IServiceCollectionExtensions.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
using Microsoft.Extensions.DependencyInjection;
1+
using Microsoft.AspNetCore.SignalR.Client;
2+
using Microsoft.Extensions.DependencyInjection;
23
using Microsoft.Extensions.DependencyInjection.Extensions;
4+
using Microsoft.Extensions.Options;
5+
using Synapse.Api.Client.Http.Configuration;
36
using Synapse.Api.Client.Services;
47

58
namespace Synapse.Api.Client;
@@ -14,12 +17,25 @@ public static class IServiceCollectionExtensions
1417
/// Adds and configures a client for the Synapse HTTP API
1518
/// </summary>
1619
/// <param name="services">The <see cref="IServiceCollection"/> to configure</param>
17-
/// <param name="httpClientSetup">An <see cref="Action{T}"/> used to configure the <see cref="HttpClient"/> to use</param>
20+
/// <param name="setup">An <see cref="Action{T}"/> used to configure the <see cref="SynapseHttpApiClientOptions"/> to use</param>
1821
/// <returns>The configured <see cref="IServiceCollection"/></returns>
19-
public static IServiceCollection AddSynapseHttpApiClient(this IServiceCollection services, Action<HttpClient> httpClientSetup)
22+
public static IServiceCollection AddSynapseHttpApiClient(this IServiceCollection services, Action<SynapseHttpApiClientOptions> setup)
2023
{
21-
services.AddHttpClient(typeof(SynapseHttpApiClient).Name, http => httpClientSetup(http));
22-
services.TryAddSingleton<ISynapseApiClient, SynapseHttpApiClient>();
24+
services.Configure(setup);
25+
services.AddHttpClient(typeof(SynapseHttpApiClient).Name, (provider, http) =>
26+
{
27+
http.BaseAddress = provider.GetRequiredService<IOptions<SynapseHttpApiClientOptions>>().Value.BaseAddress;
28+
});
29+
services.TryAddScoped<ISynapseApiClient, SynapseHttpApiClient>();
30+
services.TryAddSingleton(provider =>
31+
{
32+
var options = provider.GetRequiredService<IOptions<SynapseHttpApiClientOptions>>().Value;
33+
var connection = new HubConnectionBuilder()
34+
.WithUrl(new Uri(options.BaseAddress, "api/ws/resources/watch"))
35+
.WithAutomaticReconnect()
36+
.Build();
37+
return new ResourceWatchEventHubClient(connection);
38+
});
2339
return services;
2440
}
2541

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright © 2024-Present The Cloud Streams Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"),
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
using Neuroglia.Data.Infrastructure.ResourceOriented;
15+
using Synapse.Api.Client.Services;
16+
17+
namespace Synapse.Api.Client;
18+
19+
/// <summary>
20+
/// Defines extensions for <see cref="ISynapseApiClient"/>s
21+
/// </summary>
22+
public static class ISynapseApiClientExtensions
23+
{
24+
25+
/// <summary>
26+
/// Gets the <see cref="IResourceApiClient{TResource}"/> for the specified <see cref="IResource"/> type
27+
/// </summary>
28+
/// <typeparam name="TResource">The type of <see cref="IResource"/> to get the <see cref="IResourceApiClient{TResource}"/> for</typeparam>
29+
/// <param name="client">The extended <see cref="ISynapseApiClient"/></param>
30+
/// <returns>The <see cref="IResourceApiClient{TResource}"/> for the specified <see cref="IResource"/> type</returns>
31+
public static INamespacedResourceApiClient<TResource> ManageNamespaced<TResource>(this ISynapseApiClient client)
32+
where TResource : class, IResource, new()
33+
{
34+
var apiProperty = client.GetType().GetProperties().SingleOrDefault(p => p.CanRead && typeof(INamespacedResourceApiClient<>).MakeGenericType(typeof(TResource)).IsAssignableFrom(p.PropertyType)) ?? throw new NullReferenceException($"Failed to find a management API for the specified resource type '{new TResource().Definition}'");
35+
return (INamespacedResourceApiClient<TResource>)apiProperty.GetValue(client)!;
36+
}
37+
38+
/// <summary>
39+
/// Gets the <see cref="IResourceApiClient{TResource}"/> for the specified <see cref="IResource"/> type
40+
/// </summary>
41+
/// <typeparam name="TResource">The type of <see cref="IResource"/> to get the <see cref="IResourceApiClient{TResource}"/> for</typeparam>
42+
/// <param name="client">The extended <see cref="ISynapseApiClient"/></param>
43+
/// <returns>The <see cref="IResourceApiClient{TResource}"/> for the specified <see cref="IResource"/> type</returns>
44+
public static IClusterResourceApiClient<TResource> ManageCluster<TResource>(this ISynapseApiClient client)
45+
where TResource : class, IResource, new()
46+
{
47+
var apiProperty = client.GetType().GetProperties().SingleOrDefault(p => p.CanRead && typeof(IClusterResourceApiClient<>).MakeGenericType(typeof(TResource)).IsAssignableFrom(p.PropertyType)) ?? throw new NullReferenceException($"Failed to find a management API for the specified resource type '{new TResource().Definition}'");
48+
return (IClusterResourceApiClient<TResource>)apiProperty.GetValue(client)!;
49+
}
50+
51+
}

0 commit comments

Comments
 (0)