diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus.Tests/AzureServiceBusTransportTests.cs b/src/Transports/Azure/Wolverine.AzureServiceBus.Tests/AzureServiceBusTransportTests.cs index d0b7890e9..cf51e9954 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus.Tests/AzureServiceBusTransportTests.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus.Tests/AzureServiceBusTransportTests.cs @@ -36,6 +36,56 @@ public void find_subscription_by_uri() subscription.SubscriptionName.ShouldBe("red"); } + [Fact] + public void find_topic_with_hierarchical_name_by_uri() + { + var transport = new AzureServiceBusTransport(); + var topic = transport.GetOrCreateEndpoint( + new Uri("asb://topic/" + Uri.EscapeDataString("szrmgr/myevent"))) + .ShouldBeOfType(); + + topic.TopicName.ShouldBe("szrmgr/myevent"); + } + + [Fact] + public void find_subscription_on_hierarchical_topic_by_uri() + { + var transport = new AzureServiceBusTransport(); + var subscription = transport.GetOrCreateEndpoint( + new Uri("asb://topic/" + Uri.EscapeDataString("szrmgr/myevent") + "/sub1")) + .ShouldBeOfType(); + + subscription.SubscriptionName.ShouldBe("sub1"); + subscription.Topic.TopicName.ShouldBe("szrmgr/myevent"); + } + + [Fact] + public void hierarchical_topic_creates_correct_uri() + { + var transport = new AzureServiceBusTransport(); + var topic = new AzureServiceBusTopic(transport, "szrmgr/myevent"); + + // URI should encode the slash so it's not confused with a path separator + topic.Uri.Host.ShouldBe("topic"); + // Round-trip: resolving by URI should return a topic, not a subscription + transport.GetOrCreateEndpoint(topic.Uri) + .ShouldBeOfType() + .TopicName.ShouldBe("szrmgr/myevent"); + } + + [Fact] + public void hierarchical_topic_subscription_creates_correct_uri() + { + var transport = new AzureServiceBusTransport(); + var topic = new AzureServiceBusTopic(transport, "szrmgr/myevent"); + var subscription = new AzureServiceBusSubscription(transport, topic, "sub1"); + + // Round-trip: resolving by URI should return the subscription + transport.GetOrCreateEndpoint(subscription.Uri) + .ShouldBeOfType() + .SubscriptionName.ShouldBe("sub1"); + } + [Fact] public void retry_and_response_queues_are_enabled_by_default() { diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusTransport.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusTransport.cs index a718e9e1d..d136425fe 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusTransport.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusTransport.cs @@ -254,10 +254,10 @@ protected override AzureServiceBusEndpoint findEndpointByUri(Uri uri) switch (uri.Host) { case "queue": - return Queues[uri.Segments[1]]; + return Queues[Uri.UnescapeDataString(uri.Segments[1])]; case "topic": - var topicName = uri.Segments[1].TrimEnd('/'); + var topicName = Uri.UnescapeDataString(uri.Segments[1].TrimEnd('/')); if (uri.Segments.Length == 3) { var subscription = Subscriptions.FirstOrDefault(x => x.Uri == uri); @@ -266,7 +266,7 @@ protected override AzureServiceBusEndpoint findEndpointByUri(Uri uri) return subscription; } - var subscriptionName = uri.Segments.Last().TrimEnd('/'); + var subscriptionName = Uri.UnescapeDataString(uri.Segments.Last().TrimEnd('/')); var topic = Topics[topicName]; subscription = new AzureServiceBusSubscription(this, topic, subscriptionName); Subscriptions.Add(subscription); diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusQueue.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusQueue.cs index 2e936ca1e..92814d428 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusQueue.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusQueue.cs @@ -22,7 +22,7 @@ public class AzureServiceBusQueue : AzureServiceBusEndpoint, IBrokerQueue, IMass public AzureServiceBusQueue(AzureServiceBusTransport parent, string queueName, EndpointRole role = EndpointRole.Application) : base(parent, - new Uri($"{parent.Protocol}://queue/{queueName}"), role) + new Uri($"{parent.Protocol}://queue/{Uri.EscapeDataString(queueName)}"), role) { if (parent == null) { diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusSubscription.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusSubscription.cs index a2fbbfb94..fa7ad3c75 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusSubscription.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusSubscription.cs @@ -16,7 +16,7 @@ public class AzureServiceBusSubscription : AzureServiceBusEndpoint, IBrokerQueue public AzureServiceBusSubscription(AzureServiceBusTransport parent, AzureServiceBusTopic topic, string subscriptionName) : base(parent, - new Uri($"{parent.Protocol}://topic/{topic.TopicName}/{subscriptionName}"), + new Uri($"{parent.Protocol}://topic/{Uri.EscapeDataString(topic.TopicName)}/{subscriptionName}"), EndpointRole.Application) { if (parent == null) diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusTopic.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusTopic.cs index 432f74b15..7c565f668 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusTopic.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusTopic.cs @@ -14,7 +14,7 @@ public class AzureServiceBusTopic : AzureServiceBusEndpoint private bool _hasInitialized; public AzureServiceBusTopic(AzureServiceBusTransport parent, string topicName) : base(parent, - new Uri($"{parent.Protocol}://topic/{topicName}"), EndpointRole.Application) + new Uri($"{parent.Protocol}://topic/{Uri.EscapeDataString(topicName)}"), EndpointRole.Application) { if (parent == null) {