-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rename project and package prefix #2
- Loading branch information
Showing
11 changed files
with
450 additions
and
0 deletions.
There are no files selected for viewing
67 changes: 67 additions & 0 deletions
67
Src/Nivot.Aspire.Hosting.ProjectCommander/DistributedApplicationBuilderExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using Aspire.Hosting; | ||
using Aspire.Hosting.ApplicationModel; | ||
using Aspire.Hosting.Lifecycle; | ||
|
||
namespace CommunityToolkit.Aspire.Hosting.ProjectCommander; | ||
|
||
/// <summary> | ||
/// Extension methods for configuring the Aspire Project Commander resource. | ||
/// </summary> | ||
public static class DistributedApplicationBuilderExtensions | ||
{ | ||
/// <summary> | ||
/// Adds the Aspire Project Commander resource to the application model. | ||
/// </summary> | ||
/// <param name="builder"></param> | ||
/// <returns></returns> | ||
public static IResourceBuilder<ProjectCommanderHubResource> AddAspireProjectCommander(this IDistributedApplicationBuilder builder) | ||
{ | ||
return AddAspireProjectCommander(builder, new ProjectCommanderHubOptions()); | ||
} | ||
|
||
/// <summary> | ||
/// Adds the Aspire Project Commander resource to the application model. | ||
/// </summary> | ||
/// <param name="builder"></param> | ||
/// <param name="options"></param> | ||
/// <returns></returns> | ||
/// <exception cref="InvalidOperationException"></exception> | ||
/// <exception cref="ArgumentNullException"></exception> | ||
/// <exception cref="ArgumentOutOfRangeException"></exception> | ||
/// <exception cref="ArgumentException"></exception> | ||
public static IResourceBuilder<ProjectCommanderHubResource> AddAspireProjectCommander(this IDistributedApplicationBuilder builder, ProjectCommanderHubOptions options) | ||
{ | ||
if (builder.Resources.Any(r => r.Name == "project-commander")) | ||
{ | ||
throw new InvalidOperationException("project-commander resource already exists in the application model"); | ||
} | ||
|
||
if (options == null) throw new ArgumentNullException(nameof(options)); | ||
|
||
// ensure options.HubPort is > 1024 and < 65535 | ||
if (options.HubPort < 1024 || options.HubPort > 65535) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(options.HubPort), "HubPort must be > 1024 and < 65535"); | ||
} | ||
|
||
if (string.IsNullOrWhiteSpace(options.HubPath) || options.HubPath.Length < 2) | ||
{ | ||
throw new ArgumentException("HubPath must be a valid path", nameof(options.HubPath)); | ||
} | ||
|
||
builder.Services.TryAddLifecycleHook<ProjectCommanderHubLifecycleHook>(); | ||
|
||
var resource = new ProjectCommanderHubResource("project-commander", options); | ||
|
||
return builder.AddResource(resource) | ||
.WithInitialState(new() | ||
{ | ||
ResourceType = "ProjectCommander", | ||
State = "Stopped", | ||
Properties = [ | ||
new(CustomResourceKnownProperties.Source, "Project Commander"), | ||
] | ||
}) | ||
.ExcludeFromManifest(); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
Src/Nivot.Aspire.Hosting.ProjectCommander/Nivot.Aspire.Hosting.ProjectCommander.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<Title>Aspire Project Commander Hosting</Title> | ||
<PackageIcon>icon.png</PackageIcon> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<FrameworkReference Include="Microsoft.AspNetCore.App" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Include="..\icon.png"> | ||
<Pack>True</Pack> | ||
<PackagePath>\</PackagePath> | ||
</None> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<PackageReference Include="Aspire.Hosting" Version="9.0.0" /> | ||
<PackageReference Include="DotNet.ReproducibleBuilds" Version="1.1.1"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
</Project> |
14 changes: 14 additions & 0 deletions
14
Src/Nivot.Aspire.Hosting.ProjectCommander/ProjectCommanderHub.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Microsoft.AspNetCore.SignalR; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace CommunityToolkit.Aspire.Hosting.ProjectCommander; | ||
|
||
internal sealed class ProjectCommanderHub(ILogger logger) : Hub | ||
{ | ||
public async Task Identify(string resourceName) | ||
{ | ||
logger.LogInformation("{ResourceName} connected to Aspire Project Commander Hub", resourceName); | ||
|
||
await Groups.AddToGroupAsync(Context.ConnectionId, resourceName); | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
Src/Nivot.Aspire.Hosting.ProjectCommander/ProjectCommanderHubLifecycleHook.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using Aspire.Hosting.ApplicationModel; | ||
using Aspire.Hosting.Lifecycle; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace CommunityToolkit.Aspire.Hosting.ProjectCommander; | ||
|
||
internal sealed class ProjectCommanderHubLifecycleHook(ResourceNotificationService notificationService, ResourceLoggerService loggerService) : IDistributedApplicationLifecycleHook | ||
{ | ||
public async Task BeforeStartAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default) | ||
{ | ||
var hubResource = appModel.Resources.OfType<ProjectCommanderHubResource>().Single(); | ||
|
||
var logger = loggerService.GetLogger(hubResource); | ||
hubResource.SetLogger(logger); | ||
|
||
await notificationService.PublishUpdateAsync(hubResource, state => state with | ||
{ | ||
State = KnownResourceStates.Starting, | ||
CreationTimeStamp = DateTime.Now | ||
}); | ||
|
||
try | ||
{ | ||
await hubResource.StartHubAsync(); | ||
|
||
var hubUrl = await hubResource.ConnectionStringExpression.GetValueAsync(cancellationToken); | ||
|
||
await notificationService.PublishUpdateAsync(hubResource, state => state with | ||
{ | ||
State = KnownResourceStates.Running, | ||
StartTimeStamp = DateTime.Now, | ||
Properties = [.. state.Properties, new("HubUrl", hubUrl)] | ||
}); | ||
} | ||
catch (Exception ex) | ||
{ | ||
logger.LogError(ex, "Failed to start Project Commands Hub: {Message}", ex.Message); | ||
|
||
await notificationService.PublishUpdateAsync(hubResource, state => state with | ||
{ | ||
State = KnownResourceStates.FailedToStart | ||
}); | ||
} | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
Src/Nivot.Aspire.Hosting.ProjectCommander/ProjectCommanderHubOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
namespace CommunityToolkit.Aspire.Hosting.ProjectCommander; | ||
|
||
/// <summary> | ||
/// Options for configuring the ProjectCommanderHub | ||
/// </summary> | ||
public class ProjectCommanderHubOptions | ||
{ | ||
/// <summary> | ||
/// Default port the hub will listen on. | ||
/// </summary> | ||
public const int DefaultHubPort = 27960; | ||
/// <summary> | ||
/// Default path the hub will listen on. | ||
/// </summary> | ||
public const string DefaultHubPath = "/projectcommander"; | ||
|
||
/// <summary> | ||
/// Gets or sets the port the hub will listen on. Defaults to 27960. | ||
/// </summary> | ||
public int HubPort { get; set; } = DefaultHubPort; | ||
/// <summary> | ||
/// Gets or sets the path the hub will listen on. Defaults to "/projectcommander". | ||
/// </summary> | ||
public string? HubPath { get; set; } = DefaultHubPath; | ||
/// <summary> | ||
/// Gets or sets whether to use HTTPS for the hub. Defaults to true. | ||
/// </summary> | ||
public bool UseHttps { get; set; } = true; | ||
} |
79 changes: 79 additions & 0 deletions
79
Src/Nivot.Aspire.Hosting.ProjectCommander/ProjectCommanderHubResource.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
using System.Diagnostics; | ||
using Aspire.Hosting.ApplicationModel; | ||
using Microsoft.AspNetCore.Builder; | ||
using Microsoft.AspNetCore.Hosting; | ||
using Microsoft.AspNetCore.SignalR; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace CommunityToolkit.Aspire.Hosting.ProjectCommander; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <param name="name"></param> | ||
/// <param name="options"></param> | ||
public sealed class ProjectCommanderHubResource([ResourceName] string name, ProjectCommanderHubOptions options) | ||
: Resource(name), IResourceWithConnectionString, IAsyncDisposable | ||
{ | ||
private WebApplication? _web; | ||
private ILogger? _logger; | ||
|
||
internal async Task StartHubAsync() | ||
{ | ||
Hub = BuildHub(); | ||
|
||
await (_web!.StartAsync()).ConfigureAwait(false); | ||
|
||
_logger?.LogInformation("Aspire Project Commander Hub started"); | ||
} | ||
|
||
internal void SetLogger(ILogger logger) => _logger = logger; | ||
|
||
internal IHubContext<ProjectCommanderHub>? Hub { get; set; } | ||
|
||
private IHubContext<ProjectCommanderHub> BuildHub() | ||
{ | ||
// we need the logger to be set before building the hub so we can inject it | ||
Debug.Assert(_logger != null, "Logger must be set before building hub"); | ||
|
||
_logger?.LogInformation("Building SignalR Hub"); | ||
|
||
// signalr project command host setup | ||
var host = WebApplication.CreateBuilder(); | ||
|
||
// proxy logging to AppHost logger | ||
host.Services.AddSingleton(_logger!); | ||
|
||
host.WebHost.UseUrls($"{(options.UseHttps ? "https" : "http")}://localhost:{options.HubPort}"); | ||
|
||
host.Services.AddSignalR(); | ||
|
||
_web = host.Build(); | ||
_web.UseRouting(); | ||
_web.MapGet("/", () => "Aspire Project Commander Host 1.0, powered by SignalR."); | ||
_web.MapHub<ProjectCommanderHub>(options.HubPath!); | ||
|
||
var hub = _web.Services.GetRequiredService<IHubContext<ProjectCommanderHub>>(); | ||
|
||
_logger?.LogInformation("SignalR Hub built"); | ||
|
||
return hub; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the connection string expression for the SignalR Hub endpoint | ||
/// </summary> | ||
public ReferenceExpression ConnectionStringExpression => | ||
ReferenceExpression.Create( | ||
$"{(options.UseHttps ? "https" : "http")}://localhost:{options.HubPort.ToString()}/{options.HubPath!.TrimStart('/')}"); | ||
|
||
/// <summary> | ||
/// Disposes hosted resources | ||
/// </summary> | ||
/// <returns></returns> | ||
public async ValueTask DisposeAsync() | ||
{ | ||
if (_web != null) await _web.DisposeAsync(); | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
Src/Nivot.Aspire.Hosting.ProjectCommander/ResourceBuilderProjectCommanderExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using Aspire.Hosting.ApplicationModel; | ||
using CommunityToolkit.Aspire.Hosting.ProjectCommander; | ||
using Microsoft.AspNetCore.SignalR; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
// ReSharper disable once CheckNamespace | ||
namespace Aspire.Hosting; | ||
|
||
/// <summary> | ||
/// Extension methods for configuring the Aspire Project Commander. | ||
/// </summary> | ||
public static class ResourceBuilderProjectCommanderExtensions | ||
{ | ||
/// <summary> | ||
/// Adds project commands to a project resource. | ||
/// </summary> | ||
/// <typeparam name="T"></typeparam> | ||
/// <param name="builder"></param> | ||
/// <param name="commands"></param> | ||
/// <returns></returns> | ||
/// <exception cref="ArgumentException"></exception> | ||
public static IResourceBuilder<T> WithProjectCommands<T>( | ||
this IResourceBuilder<T> builder, params (string Name, string DisplayName)[] commands) | ||
where T : ProjectResource | ||
{ | ||
if (commands.Length == 0) | ||
{ | ||
throw new ArgumentException("You must supply at least one command."); | ||
} | ||
|
||
foreach (var command in commands) | ||
{ | ||
builder.WithCommand(command.Name, command.DisplayName, async (context) => | ||
{ | ||
bool success = false; | ||
string errorMessage = string.Empty; | ||
|
||
try | ||
{ | ||
var model = context.ServiceProvider.GetRequiredService<DistributedApplicationModel>(); | ||
var hub = model.Resources.OfType<ProjectCommanderHubResource>().Single().Hub!; | ||
|
||
var groupName = context.ResourceName; | ||
await hub.Clients.Group(groupName).SendAsync("ReceiveCommand", command.Name, context.CancellationToken); | ||
|
||
success = true; | ||
} | ||
catch (Exception ex) | ||
{ | ||
errorMessage = ex.Message; | ||
} | ||
return new ExecuteCommandResult() { Success = success, ErrorMessage = errorMessage }; | ||
}, iconName: "DesktopSignal", iconVariant: IconVariant.Regular); | ||
} | ||
|
||
return builder; | ||
} | ||
} |
Oops, something went wrong.