diff --git a/src/CommunityToolkit.Aspire.Hosting.Dapr/DaprPolyglotOptions.cs b/src/CommunityToolkit.Aspire.Hosting.Dapr/DaprPolyglotOptions.cs index a759a1c50..202fe8bcd 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Dapr/DaprPolyglotOptions.cs +++ b/src/CommunityToolkit.Aspire.Hosting.Dapr/DaprPolyglotOptions.cs @@ -56,6 +56,7 @@ internal sealed record DaprSidecarExportOptions public string? RuntimePath { get; init; } public string? SchedulerHostAddress { get; init; } public string? UnixDomainSocket { get; init; } + public string? SidecarName { get; init; } public DaprSidecarOptions ToDaprSidecarOptions() { @@ -93,7 +94,8 @@ public DaprSidecarOptions ToDaprSidecarOptions() RunFile = RunFile, RuntimePath = RuntimePath, SchedulerHostAddress = SchedulerHostAddress, - UnixDomainSocket = UnixDomainSocket + UnixDomainSocket = UnixDomainSocket, + SidecarName = SidecarName }; #pragma warning restore CS0618 } diff --git a/src/CommunityToolkit.Aspire.Hosting.Dapr/DaprSidecarOptions.cs b/src/CommunityToolkit.Aspire.Hosting.Dapr/DaprSidecarOptions.cs index 3ef4bca3d..95116c150 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Dapr/DaprSidecarOptions.cs +++ b/src/CommunityToolkit.Aspire.Hosting.Dapr/DaprSidecarOptions.cs @@ -241,4 +241,12 @@ public string? DaprReadBufferSize /// If specified, the Dapr sidecar will use Unix Domain Sockets for API calls. /// public string? UnixDomainSocket { get; init; } + + /// + /// Gets or sets the name of the Dapr sidecar resource as it appears in the Aspire dashboard. + /// + /// + /// When null or whitespace, the default name {resourceName}-dapr is used. + /// + public string? SidecarName { get; init; } } diff --git a/src/CommunityToolkit.Aspire.Hosting.Dapr/IDistributedApplicationComponentBuilderExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.Dapr/IDistributedApplicationComponentBuilderExtensions.cs index 423775c63..89b237e56 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Dapr/IDistributedApplicationComponentBuilderExtensions.cs +++ b/src/CommunityToolkit.Aspire.Hosting.Dapr/IDistributedApplicationComponentBuilderExtensions.cs @@ -36,7 +36,10 @@ public static IResourceBuilder WithDaprSidecar(this IResourceBuilder bu [AspireExportIgnore(Reason = "Use the exported DTO-based overload instead to avoid ambiguous polyglot wrapper generation.")] public static IResourceBuilder WithDaprSidecar(this IResourceBuilder builder, DaprSidecarOptions? options = null) where T : IResource { - return builder.WithDaprSidecar( + var sidecarName = !string.IsNullOrWhiteSpace(options?.SidecarName) ? options.SidecarName : null; + + return builder.WithDaprSidecarCore( + sidecarName, sidecarBuilder => { if (options is not null) @@ -61,11 +64,18 @@ internal static IResourceBuilder WithDaprSidecarExport(this IResourceBuild /// The resource builder instance. [AspireExport("configureDaprSidecar", MethodName = "configureDaprSidecar", Description = "Adds a Dapr sidecar to the resource and exposes it for callback configuration")] public static IResourceBuilder WithDaprSidecar(this IResourceBuilder builder, Action> configureSidecar) where T : IResource + { + return builder.WithDaprSidecarCore(null, configureSidecar); + } + + private static IResourceBuilder WithDaprSidecarCore(this IResourceBuilder builder, string? sidecarName, Action> configureSidecar) where T : IResource { // Add Dapr is idempotent, so we can call it multiple times. builder.ApplicationBuilder.AddDapr(); - var sidecarBuilder = builder.ApplicationBuilder.AddResource(new DaprSidecarResource($"{builder.Resource.Name}-dapr")) + var name = !string.IsNullOrWhiteSpace(sidecarName) ? sidecarName : $"{builder.Resource.Name}-dapr"; + + var sidecarBuilder = builder.ApplicationBuilder.AddResource(new DaprSidecarResource(name)) .WithInitialState(new() { Properties = [], diff --git a/tests/CommunityToolkit.Aspire.Hosting.Dapr.Tests/WithDaprSidecarTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Dapr.Tests/WithDaprSidecarTests.cs index 5d3b39a59..a433644a4 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.Dapr.Tests/WithDaprSidecarTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.Dapr.Tests/WithDaprSidecarTests.cs @@ -154,6 +154,63 @@ public void ResourceWithWaitAnnotationAndDaprSidecar_SetsUpCorrectDependencies() Assert.Equal(sidecarResource, sidecarAnnotation.Sidecar); } + [Fact] + public void SidecarNameCanBeSetViaOptions() + { + var builder = DistributedApplication.CreateBuilder(); + + builder.AddProject("test") + .WithDaprSidecar(new DaprSidecarOptions { SidecarName = "my-custom-sidecar" }); + + var resource = Assert.Single(builder.Resources.OfType()); + Assert.Equal("my-custom-sidecar", resource.Name); + } + + [Fact] + public void SidecarNameDefaultsToResourceNameWhenNotSet() + { + var builder = DistributedApplication.CreateBuilder(); + + builder.AddProject("test") + .WithDaprSidecar(); + + var resource = Assert.Single(builder.Resources.OfType()); + Assert.Equal("test-dapr", resource.Name); + } + + [Fact] + public void SidecarNameDefaultsToResourceNameWhenSetToWhitespace() + { + var builder = DistributedApplication.CreateBuilder(); + + builder.AddProject("test") + .WithDaprSidecar(new DaprSidecarOptions { SidecarName = " " }); + + var resource = Assert.Single(builder.Resources.OfType()); + Assert.Equal("test-dapr", resource.Name); + } + + [Fact] + public void SidecarNameCanBeSetWithAppId() + { + var builder = DistributedApplication.CreateBuilder(); + + builder.AddProject("my-service") + .WithDaprSidecar(new DaprSidecarOptions + { + SidecarName = "my-custom-sidecar", + AppId = "my-service" + }); + + var resource = Assert.Single(builder.Resources.OfType()); + Assert.Equal("my-custom-sidecar", resource.Name); + + var annotation = Assert.Single(resource.Annotations.OfType()); + Assert.Equal("my-service", annotation.Options.AppId); + Assert.Equal("my-custom-sidecar", annotation.Options.SidecarName); + } + + [Fact] public void ResourceWithMultipleWaitAnnotationsAndDaprSidecar_HasAllWaitDependencies() {