diff --git a/src/Components/Aspire.Azure.Messaging.EventHubs/EventHubsComponent.cs b/src/Components/Aspire.Azure.Messaging.EventHubs/EventHubsComponent.cs index 30cee2f2f8..3e33e1f1ed 100644 --- a/src/Components/Aspire.Azure.Messaging.EventHubs/EventHubsComponent.cs +++ b/src/Components/Aspire.Azure.Messaging.EventHubs/EventHubsComponent.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Security.Cryptography; using Aspire.Azure.Common; using Aspire.Azure.Messaging.EventHubs; using Azure.Core; @@ -56,15 +55,15 @@ protected static string GetNamespaceFromSettings(AzureMessagingEventHubsSettings : new Uri(settings.FullyQualifiedNamespace).Host; // This is likely to be similar to {yournamespace}.servicebus.windows.net or {yournamespace}.servicebus.chinacloudapi.cn - if (ns.Contains(".servicebus", StringComparison.OrdinalIgnoreCase)) + var serviceBusIndex = ns.IndexOf(".servicebus", StringComparison.OrdinalIgnoreCase); + if (serviceBusIndex != -1) { - ns = ns[..ns.IndexOf(".servicebus")]; + ns = ns[..serviceBusIndex]; } else { - // Use a random prefix if no meaningful name is found e.g., "localhost", "127.0.0.1". - // This is used to create blob containers names that are unique in the referenced storage account. - RandomNumberGenerator.GetHexString(12, true); + // sanitize the namespace if it's not a servicebus namespace + ns = ns.Replace(".", "-"); } } catch (Exception ex) when (ex is FormatException or IndexOutOfRangeException) diff --git a/src/Components/Aspire.Azure.Messaging.EventHubs/EventProcessorClientComponent.cs b/src/Components/Aspire.Azure.Messaging.EventHubs/EventProcessorClientComponent.cs index 6700975ff8..3384ecc3ad 100644 --- a/src/Components/Aspire.Azure.Messaging.EventHubs/EventProcessorClientComponent.cs +++ b/src/Components/Aspire.Azure.Messaging.EventHubs/EventProcessorClientComponent.cs @@ -97,17 +97,14 @@ private static BlobContainerClient GetBlobContainerClient( // name specified in the settings. bool shouldTryCreateIfNotExists = false; + // Do we have a container name provided in the settings? if (string.IsNullOrWhiteSpace(settings.BlobContainerName)) { + // If not, we'll create a container name based on the namespace, event hub name and consumer group var ns = GetNamespaceFromSettings(settings); - // Do we have a container name provided in the settings? - if (string.IsNullOrWhiteSpace(settings.BlobContainerName)) - { - // If not, we'll create a container name based on the namespace, event hub name and consumer group - settings.BlobContainerName = $"{ns}-{settings.EventHubName}-{consumerGroup}"; - shouldTryCreateIfNotExists = true; - } + settings.BlobContainerName = $"{ns}-{settings.EventHubName}-{consumerGroup}"; + shouldTryCreateIfNotExists = true; } var containerClient = blobClient.GetBlobContainerClient(settings.BlobContainerName); diff --git a/tests/Aspire.Azure.Messaging.EventHubs.Tests/Aspire.Azure.Messaging.EventHubs.Tests.csproj b/tests/Aspire.Azure.Messaging.EventHubs.Tests/Aspire.Azure.Messaging.EventHubs.Tests.csproj index ace3db5c39..28bf694c86 100644 --- a/tests/Aspire.Azure.Messaging.EventHubs.Tests/Aspire.Azure.Messaging.EventHubs.Tests.csproj +++ b/tests/Aspire.Azure.Messaging.EventHubs.Tests/Aspire.Azure.Messaging.EventHubs.Tests.csproj @@ -11,6 +11,8 @@ + + diff --git a/tests/Aspire.Azure.Messaging.EventHubs.Tests/AspireEventHubsExtensionsTests.cs b/tests/Aspire.Azure.Messaging.EventHubs.Tests/AspireEventHubsExtensionsTests.cs index fda1d43424..08272a039b 100644 --- a/tests/Aspire.Azure.Messaging.EventHubs.Tests/AspireEventHubsExtensionsTests.cs +++ b/tests/Aspire.Azure.Messaging.EventHubs.Tests/AspireEventHubsExtensionsTests.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text; +using Azure.Core; using Azure.Identity; using Azure.Messaging.EventHubs; using Azure.Messaging.EventHubs.Consumer; @@ -476,7 +478,7 @@ private static void AssertFullyQualifiedNamespace(string expectedNamespace, obje EventHubConsumerClient consumer => consumer.FullyQualifiedNamespace, EventProcessorClient processor => processor.FullyQualifiedNamespace, PartitionReceiver receiver => receiver.FullyQualifiedNamespace, - EventHubBufferedProducerClient producer => producer.FullyQualifiedNamespace, + EventHubBufferedProducerClient producer => producer.FullyQualifiedNamespace, _ => throw new InvalidOperationException() }); } @@ -580,4 +582,48 @@ public void CanAddMultipleKeyedServices(int clientIndex) public static string CreateConfigKey(string prefix, string? key, string suffix) => string.IsNullOrEmpty(key) ? $"{prefix}:{suffix}" : $"{prefix}:{key}:{suffix}"; + + /// + /// Tests that the BlobContainerName defaults correctly when the connection string doesn't contain ".servicebus" and + /// contains invalid container name characters. + /// + [Fact] + public void ProcessorBlobContainerNameDefaultsCorrectly() + { + var builder = Host.CreateEmptyApplicationBuilder(null); + builder.Configuration.AddInMemoryCollection([ + new KeyValuePair("ConnectionStrings:eh1", "Endpoint=sb://127.0.0.1:53589;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;"), + new KeyValuePair("Aspire:Azure:Messaging:EventHubs:EventProcessorClient:EventHubName", "MyHub"), + ]); + + var mockTransport = new MockTransport( + CreateResponse("""{}""")); + var blobClient = new BlobServiceClient(new Uri(BlobsConnectionString), new BlobClientOptions() { Transport = mockTransport }); + builder.Services.AddSingleton(blobClient); + + builder.AddAzureEventProcessorClient("eh1"); + + using var host = builder.Build(); + + var client = host.Services.GetRequiredService(); + Assert.NotNull(client); + + Assert.Single(mockTransport.Requests); + // the container name should be based on the Endpoint, EventHubName, and ConsumerGroup + Assert.Equal("https://fake.blob.core.windows.net/127-0-0-1-MyHub-default?restype=container", mockTransport.Requests[0].Uri.ToString()); + } + + private static MockResponse CreateResponse(string content) + { + var buffer = Encoding.UTF8.GetBytes(content); + var response = new MockResponse(201) + { + ClientRequestId = Guid.NewGuid().ToString(), + ContentStream = new MemoryStream(buffer), + }; + + response.AddHeader(new HttpHeader("Content-Type", "application/json; charset=utf-8")); + + return response; + } }