Skip to content

Commit 59e1962

Browse files
committed
feat(Solution): Added the CLI application to the solution (WIP)
Signed-off-by: Charles d'Avernas <charles.davernas@neuroglia.io>
1 parent dd2c375 commit 59e1962

31 files changed

+837
-42
lines changed

Synapse.sln

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Containeriz
5353
EndProject
5454
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runner", "src\runner\Synapse.Runner\Synapse.Runner.csproj", "{E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018}"
5555
EndProject
56-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Synapse.Dashboard.StateManagement", "src\dashboard\Synapse.Dashboard.StateManagement\Synapse.Dashboard.StateManagement.csproj", "{91EF9F64-4997-407C-B353-C26B1421D0FB}"
56+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Dashboard.StateManagement", "src\dashboard\Synapse.Dashboard.StateManagement\Synapse.Dashboard.StateManagement.csproj", "{91EF9F64-4997-407C-B353-C26B1421D0FB}"
5757
EndProject
5858
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Operator", "src\operator\Synapse.Operator\Synapse.Operator.csproj", "{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}"
5959
EndProject
60+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Synapse.Cli", "src\cli\Synapse.Cli\Synapse.Cli.csproj", "{C86F6C8B-5946-433D-9E09-2C0269CE6372}"
61+
EndProject
6062
Global
6163
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6264
Debug|Any CPU = Debug|Any CPU
@@ -131,6 +133,10 @@ Global
131133
{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
132134
{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
133135
{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Release|Any CPU.Build.0 = Release|Any CPU
136+
{C86F6C8B-5946-433D-9E09-2C0269CE6372}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
137+
{C86F6C8B-5946-433D-9E09-2C0269CE6372}.Debug|Any CPU.Build.0 = Debug|Any CPU
138+
{C86F6C8B-5946-433D-9E09-2C0269CE6372}.Release|Any CPU.ActiveCfg = Release|Any CPU
139+
{C86F6C8B-5946-433D-9E09-2C0269CE6372}.Release|Any CPU.Build.0 = Release|Any CPU
134140
EndGlobalSection
135141
GlobalSection(SolutionProperties) = preSolution
136142
HideSolutionNode = FALSE
@@ -160,6 +166,7 @@ Global
160166
{E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018} = {1DA47E5F-B23A-4D3C-96AA-4BD2662AB946}
161167
{91EF9F64-4997-407C-B353-C26B1421D0FB} = {7DF998B8-0FB1-470E-8ED0-EA1CC7B16901}
162168
{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0} = {32EAD165-3D99-42CD-B3AF-05136DCC7F35}
169+
{C86F6C8B-5946-433D-9E09-2C0269CE6372} = {D3B3B95D-B598-4B13-B754-4A7E530405A6}
163170
EndGlobalSection
164171
GlobalSection(ExtensibilityGlobals) = postSolution
165172
SolutionGuid = {2A6C03D6-355A-4B39-9F2B-D0FDE429C0E2}

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

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

14-
using Microsoft.AspNetCore.SignalR.Client;
15-
using Microsoft.Extensions.DependencyInjection;
16-
using Microsoft.Extensions.DependencyInjection.Extensions;
17-
using Microsoft.Extensions.Options;
18-
using Synapse.Api.Client.Http.Configuration;
19-
using Synapse.Api.Client.Services;
20-
2114
namespace Synapse.Api.Client;
2215

2316
/// <summary>

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

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

14-
using Neuroglia.Data.Infrastructure.ResourceOriented;
15-
using Synapse.Api.Client.Services;
16-
1714
namespace Synapse.Api.Client;
1815

1916
/// <summary>

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

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

14-
using Microsoft.Extensions.Logging;
15-
using Neuroglia;
16-
using Neuroglia.Serialization;
17-
using System.Net.Mime;
18-
using System.Text;
19-
2014
namespace Synapse.Api.Client.Services;
2115

2216
/// <summary>

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

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

14-
using Microsoft.Extensions.Logging;
15-
using Neuroglia;
16-
using Neuroglia.Data;
17-
using Neuroglia.Data.Infrastructure.ResourceOriented;
18-
using Neuroglia.Serialization;
19-
using System.Net.Mime;
20-
using System.Text;
21-
2214
namespace Synapse.Api.Client.Services;
2315

2416
/// <summary>
@@ -229,7 +221,7 @@ public virtual async Task DeleteAsync(string name, string @namespace, Cancellati
229221
var resource = new TResource();
230222
var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}/{name}";
231223
using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Delete, uri), cancellationToken).ConfigureAwait(false);
232-
await ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
224+
await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
233225
}
234226

235227
/// <summary>

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

Lines changed: 0 additions & 2 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 Neuroglia.Data.Infrastructure.ResourceOriented;
15-
1614
namespace Synapse.Api.Client.Services;
1715

1816
/// <summary>

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

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

14-
using Microsoft.AspNetCore.SignalR.Client;
15-
using Neuroglia.Data.Infrastructure.ResourceOriented;
16-
using Neuroglia.Eventing.CloudEvents;
17-
using System.Reactive.Linq;
18-
using System.Reactive.Subjects;
19-
2014
namespace Synapse.Api.Client.Services;
2115

2216
/// <summary>

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

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

14-
using Microsoft.Extensions.DependencyInjection;
15-
using Microsoft.Extensions.Logging;
16-
using Neuroglia;
17-
using Neuroglia.Data.Infrastructure.ResourceOriented;
18-
using Neuroglia.Serialization;
19-
2014
namespace Synapse.Api.Client.Services;
2115

2216
/// <summary>

src/api/Synapse.Api.Client.Http/Usings.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,20 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14+
global using Microsoft.AspNetCore.SignalR.Client;
15+
global using Microsoft.Extensions.DependencyInjection;
16+
global using Microsoft.Extensions.DependencyInjection.Extensions;
17+
global using Microsoft.Extensions.Logging;
18+
global using Microsoft.Extensions.Options;
19+
global using Neuroglia;
20+
global using Neuroglia.Data;
21+
global using Neuroglia.Data.Infrastructure.ResourceOriented;
22+
global using Neuroglia.Eventing.CloudEvents;
23+
global using Neuroglia.Serialization;
24+
global using Synapse.Api.Client.Http.Configuration;
25+
global using Synapse.Api.Client.Services;
1426
global using Synapse.Resources;
27+
global using System.Net.Mime;
28+
global using System.Reactive.Linq;
29+
global using System.Reactive.Subjects;
30+
global using System.Text;

src/cli/Synapse.Cli/CliConstants.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace Synapse.Cli;
2+
3+
/// <summary>
4+
/// Exposes constants and statics used by the CLI
5+
/// </summary>
6+
internal static class CliConstants
7+
{
8+
9+
/// <summary>
10+
/// Gets the name of the CLI configuration file
11+
/// </summary>
12+
public const string ConfigurationFileName = "config.yaml";
13+
14+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
namespace Synapse.Cli.Commands;
2+
3+
/// <summary>
4+
/// Represents the base class for all <see cref="System.CommandLine.Command"/> implementations
5+
/// </summary>
6+
public abstract class Command
7+
: System.CommandLine.Command
8+
{
9+
10+
/// <summary>
11+
/// Initializes a new <see cref="Command"/>
12+
/// </summary>
13+
/// <param name="serviceProvider">The current <see cref="IServiceProvider"/></param>
14+
/// <param name="loggerFactory">The service used to create <see cref="ILogger"/>s</param>
15+
/// <param name="api">The service used to interact with the remote Synapse API</param>
16+
/// <param name="name">The <see cref="Command"/>'s name</param>
17+
/// <param name="description">The <see cref="Command"/>'s description</param>
18+
protected Command(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, string name, string description)
19+
: base(name, description)
20+
{
21+
this.ServiceProvider = serviceProvider;
22+
this.Logger = loggerFactory.CreateLogger(this.GetType());
23+
this.Api = api;
24+
}
25+
26+
/// <summary>
27+
/// Gets the current <see cref="IServiceProvider"/>
28+
/// </summary>
29+
protected IServiceProvider ServiceProvider { get; }
30+
31+
/// <summary>
32+
/// Gets the service used to perform logging
33+
/// </summary>
34+
protected ILogger Logger { get; }
35+
36+
/// <summary>
37+
/// Gets the service used to interact with the remote Synapse API
38+
/// </summary>
39+
protected ISynapseApiClient Api { get; }
40+
41+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using Microsoft.Extensions.Options;
2+
using Synapse.Cli.Configuration;
3+
using Synapse.Cli.Services;
4+
5+
namespace Synapse.Cli.Commands.Config;
6+
7+
/// <summary>
8+
/// Represents the <see cref="Command"/> used to configure the CLI to delete the specified API configuration
9+
/// </summary>
10+
internal class DeleteApiCommand
11+
: Command
12+
{
13+
14+
/// <summary>
15+
/// Gets the <see cref="UseApiCommand"/>'s name
16+
/// </summary>
17+
public const string CommandName = "delete-api";
18+
/// <summary>
19+
/// Gets the <see cref="UseApiCommand"/>'s description
20+
/// </summary>
21+
public const string CommandDescription = "Deletes the API configuration with the specified name.";
22+
23+
/// <inheritdoc/>
24+
public DeleteApiCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IOptionsManager optionsManager, IOptionsMonitor<ApplicationOptions> applicationOptions)
25+
: base(serviceProvider, loggerFactory, api, CommandName, CommandDescription)
26+
{
27+
this.OptionsManager = optionsManager;
28+
this.ApplicationOptions = applicationOptions;
29+
this.Add(new Argument<string>("name") { Description = "The name of the API configuration to delete." });
30+
this.Handler = CommandHandler.Create<string>(HandleAsync);
31+
}
32+
33+
/// <summary>
34+
/// Gets the service used to manage the application's options
35+
/// </summary>
36+
protected IOptionsManager OptionsManager { get; }
37+
38+
/// <summary>
39+
/// Gets the current <see cref="ApplicationOptions"/>
40+
/// </summary>
41+
protected IOptionsMonitor<ApplicationOptions> ApplicationOptions { get; }
42+
43+
/// <summary>
44+
/// Handles the <see cref="DeleteApiCommand"/>
45+
/// </summary>
46+
/// <param name="name">The name of the API configuration to use</param>
47+
/// <returns>A new awaitable <see cref="Task"/></returns>
48+
public async Task HandleAsync(string name)
49+
{
50+
ArgumentException.ThrowIfNullOrWhiteSpace(name);
51+
if (this.ApplicationOptions.CurrentValue.Api.Current == name) throw new NotSupportedException($"Failed to delete the API configuration with name '{name}' because it is the API currently in use.");
52+
if (!this.ApplicationOptions.CurrentValue.Api.Configurations.Remove(name)) throw new NullReferenceException($"Failed to find a configured API with name '{name}'.");
53+
await this.OptionsManager.UpdateOptionsAsync(this.ApplicationOptions.CurrentValue);
54+
}
55+
56+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using Microsoft.Extensions.Options;
2+
using Synapse.Cli.Configuration;
3+
using Synapse.Cli.Services;
4+
using Synapse.Resources;
5+
6+
namespace Synapse.Cli.Commands.Config;
7+
8+
/// <summary>
9+
/// Represents the <see cref="Command"/> used to configure the API used by the Synapse CLI
10+
/// </summary>
11+
internal class GetApisCommand
12+
: Command
13+
{
14+
15+
/// <summary>
16+
/// Gets the <see cref="GetApisCommand"/>'s name
17+
/// </summary>
18+
public const string CommandName = "get-apis";
19+
/// <summary>
20+
/// Gets the <see cref="GetApisCommand"/>'s description
21+
/// </summary>
22+
public const string CommandDescription = "Retrieves all configured APIs";
23+
24+
/// <inheritdoc/>
25+
public GetApisCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IOptionsManager optionsManager, IOptionsMonitor<ApplicationOptions> applicationOptions)
26+
: base(serviceProvider, loggerFactory, api, CommandName, CommandDescription)
27+
{
28+
this.OptionsManager = optionsManager;
29+
this.ApplicationOptions = applicationOptions;
30+
this.Handler = CommandHandler.Create(HandleAsync);
31+
}
32+
33+
/// <summary>
34+
/// Gets the service used to manage the application's options
35+
/// </summary>
36+
protected IOptionsManager OptionsManager { get; }
37+
38+
/// <summary>
39+
/// Gets the current <see cref="ApplicationOptions"/>
40+
/// </summary>
41+
protected IOptionsMonitor<ApplicationOptions> ApplicationOptions { get; }
42+
43+
/// <summary>
44+
/// Handles the <see cref="GetApisCommand"/>
45+
/// </summary>
46+
/// <returns>A new awaitable <see cref="Task"/></returns>
47+
public async Task HandleAsync()
48+
{
49+
var table = new Table();
50+
table.Border(TableBorder.None);
51+
table.AddColumn("CURRENT");
52+
table.AddColumn("NAME");
53+
foreach (var apiConfig in this.ApplicationOptions.CurrentValue.Api.Configurations)
54+
{
55+
table.AddRow
56+
(
57+
this.ApplicationOptions.CurrentValue.Api.Current == apiConfig.Key || this.ApplicationOptions.CurrentValue.Api.Configurations.Count == 1 ? "*" : string.Empty,
58+
apiConfig.Key
59+
);
60+
}
61+
AnsiConsole.Write(table);
62+
await Task.CompletedTask;
63+
}
64+
65+
static class CommandOptions
66+
{
67+
68+
public static Option<Uri> Server => new(["-s", "--server"], "The address of the API server to use");
69+
70+
}
71+
72+
}

0 commit comments

Comments
 (0)