From 225a513b1b5f0d5aa5da0c9d1c174d7b3f23aff5 Mon Sep 17 00:00:00 2001 From: Mateus Viegas Date: Fri, 23 Aug 2024 02:03:14 +0100 Subject: [PATCH] fix: BrokerAddress property in AzureServiceBus transport (#1576) * fixed getting the broker address from service bus * fixed namespace extraction --- .../AzureServiceBusConsumerClient.cs | 3 +- .../Helpers/ServiceBusHelpers.cs | 54 +++++++++ .../ITransport.AzureServiceBus.cs | 3 +- .../Helpers/ServiceBusHelperTests.cs | 107 ++++++++++++++++++ .../ServiceBusTransportTests.cs | 20 +++- 5 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 src/DotNetCore.CAP.AzureServiceBus/Helpers/ServiceBusHelpers.cs create mode 100644 test/DotNetCore.CAP.AzureServiceBus.Test/Helpers/ServiceBusHelperTests.cs diff --git a/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs index 400b72ea6..d49e8edd4 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs +++ b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Azure.Messaging.ServiceBus; using Azure.Messaging.ServiceBus.Administration; +using DotNetCore.CAP.AzureServiceBus.Helpers; using DotNetCore.CAP.Messages; using DotNetCore.CAP.Transport; using Microsoft.Extensions.Logging; @@ -48,7 +49,7 @@ public AzureServiceBusConsumerClient( public Action? OnLogCallback { get; set; } - public BrokerAddress BrokerAddress => new("AzureServiceBus", _asbOptions.ConnectionString); + public BrokerAddress BrokerAddress => ServiceBusHelpers.GetBrokerAddress(_asbOptions.ConnectionString, _asbOptions.Namespace); public void Subscribe(IEnumerable topics) { diff --git a/src/DotNetCore.CAP.AzureServiceBus/Helpers/ServiceBusHelpers.cs b/src/DotNetCore.CAP.AzureServiceBus/Helpers/ServiceBusHelpers.cs new file mode 100644 index 000000000..801f1b8ca --- /dev/null +++ b/src/DotNetCore.CAP.AzureServiceBus/Helpers/ServiceBusHelpers.cs @@ -0,0 +1,54 @@ +using System; +using DotNetCore.CAP.Transport; + +namespace DotNetCore.CAP.AzureServiceBus.Helpers; + +public static class ServiceBusHelpers +{ + public static BrokerAddress GetBrokerAddress(string? connectionString, string? @namespace) + { + var host = (@namespace, connectionString) switch + { + _ when string.IsNullOrWhiteSpace(@namespace) && string.IsNullOrWhiteSpace(connectionString) + => throw new ArgumentException("Either connection string or namespace are required."), + _ when string.IsNullOrWhiteSpace(connectionString) + || (!string.IsNullOrWhiteSpace(@namespace) && !string.IsNullOrWhiteSpace(connectionString)) + => @namespace!, + _ when string.IsNullOrWhiteSpace(@namespace) + => TryGetEndpointFromConnectionString(connectionString, out var extractedValue) + ? extractedValue! + : throw new InvalidOperationException("Unable to extract namespace from connection string.") + }; + + return new BrokerAddress("AzureServiceBus", host); + } + + + private static bool TryGetEndpointFromConnectionString(string? connectionString, out string? @namespace) + { + @namespace = string.Empty; + + if (string.IsNullOrWhiteSpace(connectionString)) + return false; + + var keyValuePairs = connectionString.Split(';'); + + foreach (var kvp in keyValuePairs) + { + if (!kvp.StartsWith("Endpoint", StringComparison.InvariantCultureIgnoreCase)) continue; + + var endpointParts = kvp.Split('='); + + if (endpointParts.Length != 2) continue; + + var uri = new Uri(endpointParts[1]); + + // Namespace is the host part without the .servicebus.windows.net + @namespace = uri.ToString(); + + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs b/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs index 7c1a47e72..e427846c6 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs +++ b/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; +using DotNetCore.CAP.AzureServiceBus.Helpers; using DotNetCore.CAP.AzureServiceBus.Producer; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Messages; @@ -51,7 +52,7 @@ public IServiceBusProducerDescriptor CreateProducerForMessage(TransportMessage t _asbOptions.Value.TopicPath); } - public BrokerAddress BrokerAddress => new("AzureServiceBus", _asbOptions.Value.ConnectionString); + public BrokerAddress BrokerAddress => ServiceBusHelpers.GetBrokerAddress(_asbOptions.Value.ConnectionString, _asbOptions.Value.Namespace); public async Task SendAsync(TransportMessage transportMessage) { diff --git a/test/DotNetCore.CAP.AzureServiceBus.Test/Helpers/ServiceBusHelperTests.cs b/test/DotNetCore.CAP.AzureServiceBus.Test/Helpers/ServiceBusHelperTests.cs new file mode 100644 index 000000000..31bbe010c --- /dev/null +++ b/test/DotNetCore.CAP.AzureServiceBus.Test/Helpers/ServiceBusHelperTests.cs @@ -0,0 +1,107 @@ +using System; +using DotNetCore.CAP.AzureServiceBus.Helpers; +using Xunit; + +namespace DotNetCore.CAP.AzureServiceBus.Test.Helpers; + +public class ServiceBusHelpersTests +{ + [Fact] + public void GetBrokerAddress_ShouldThrowArgumentException_WhenBothInputsAreNull() + { + // Arrange + string? connectionString = null; + string? @namespace = null; + + // Act & Assert + var ex = Assert.Throws(() => ServiceBusHelpers.GetBrokerAddress(connectionString, @namespace)); + Assert.Equal("Either connection string or namespace are required.", ex.Message); + } + + [Fact] + public void GetBrokerAddress_ShouldReturnNamespace_WhenConnectionStringIsNull() + { + // Arrange + string? connectionString = null; + string? @namespace = "sb://mynamespace.servicebus.windows.net/"; + + // Act + var result = ServiceBusHelpers.GetBrokerAddress(connectionString, @namespace); + + // Assert + Assert.Equal("AzureServiceBus", result.Name); + Assert.Equal("sb://mynamespace.servicebus.windows.net/", result.Endpoint); + } + + [Fact] + public void GetBrokerAddress_ShouldReturnExtractedNamespace_WhenNamespaceIsNull() + { + // Arrange + string? connectionString = "Endpoint=sb://mynamespace.servicebus.windows.net/;SharedAccessKeyName=myPolicy;SharedAccessKey=myKey"; + string? @namespace = null; + + // Act + var result = ServiceBusHelpers.GetBrokerAddress(connectionString, @namespace); + + // Assert + Assert.Equal("AzureServiceBus", result.Name); + Assert.Equal("sb://mynamespace.servicebus.windows.net/", result.Endpoint); + } + + [Fact] + public void GetBrokerAddress_ShouldThrowInvalidOperationException_WhenNamespaceExtractionFails() + { + // Arrange + string? connectionString = "InvalidConnectionString"; + string? @namespace = null; + + // Act & Assert + var ex = Assert.Throws(() => ServiceBusHelpers.GetBrokerAddress(connectionString, @namespace)); + Assert.Equal("Unable to extract namespace from connection string.", ex.Message); + } + + [Fact] + public void GetBrokerAddress_ShouldReturnNamespace_WhenBothNamespaceAndConnectionStringAreProvided() + { + // Arrange + string? connectionString = "Endpoint=sb://mynamespace.servicebus.windows.net/;SharedAccessKeyName=myPolicy;SharedAccessKey=myKey"; + string? @namespace = "anothernamespace"; + + // Act + var result = ServiceBusHelpers.GetBrokerAddress(connectionString, @namespace); + + // Assert + Assert.Equal("AzureServiceBus", result.Name); + Assert.Equal("anothernamespace", result.Endpoint); + } + + [Fact] + public void GetBrokerAddress_ShouldReturnExtractedNamespace_WhenConnectionStringIsValidAndNamespaceIsEmpty() + { + // Arrange + string? connectionString = "Endpoint=sb://mynamespace.servicebus.windows.net/;SharedAccessKeyName=myPolicy;SharedAccessKey=myKey"; + string? @namespace = ""; + + // Act + var result = ServiceBusHelpers.GetBrokerAddress(connectionString, @namespace); + + // Assert + Assert.Equal("AzureServiceBus", result.Name); + Assert.Equal("sb://mynamespace.servicebus.windows.net/", result.Endpoint); + } + + [Fact] + public void GetBrokerAddress_ShouldReturnNamespace_WhenConnectionStringIsEmpty() + { + // Arrange + string? connectionString = ""; + string? @namespace = "sb://mynamespace.servicebus.windows.net/"; + + // Act + var result = ServiceBusHelpers.GetBrokerAddress(connectionString, @namespace); + + // Assert + Assert.Equal("AzureServiceBus", result.Name); + Assert.Equal("sb://mynamespace.servicebus.windows.net/", result.Endpoint); + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.AzureServiceBus.Test/ServiceBusTransportTests.cs b/test/DotNetCore.CAP.AzureServiceBus.Test/ServiceBusTransportTests.cs index c2b788657..692b1b810 100644 --- a/test/DotNetCore.CAP.AzureServiceBus.Test/ServiceBusTransportTests.cs +++ b/test/DotNetCore.CAP.AzureServiceBus.Test/ServiceBusTransportTests.cs @@ -17,14 +17,28 @@ public class ServiceBusTransportTests public ServiceBusTransportTests() { - var config = new AzureServiceBusOptions(); + var config = new AzureServiceBusOptions() + { + ConnectionString = "Endpoint=sb://mynamespace.servicebus.windows.net/;SharedAccessKeyName=myPolicy;SharedAccessKey=myKey" + }; + config.ConfigureCustomProducer(cfg => cfg.UseTopic("entity-created").WithSubscription()); _options = Options.Create(config); } [Fact] - public void Custom_Producer_Should_Have_Custom_Topic() + public void Transport_ShouldHaveCorrectBrokerAddress() + { + // Given, When + var transport = new AzureServiceBusTransport(NullLogger.Instance, _options); + + // Then + transport.BrokerAddress.Endpoint.ShouldBe("sb://mynamespace.servicebus.windows.net/"); + } + + [Fact] + public void CustomProducer_ShouldHaveCustomTopic() { // Given var transport = new AzureServiceBusTransport(NullLogger.Instance, _options); @@ -45,7 +59,7 @@ public void Custom_Producer_Should_Have_Custom_Topic() } [Fact] - public void Default_Producer_Should_Have_Default_Topic() + public void DefaultProducer_ShouldHaveDefaultTopic() { // Given var transport = new AzureServiceBusTransport(NullLogger.Instance, _options);