From b1ce9c58f0236806cfb1cf161682f2b2c55958ab Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Mon, 18 Sep 2023 12:19:16 -0500 Subject: [PATCH] Azure Service Bus interop. Closes GH-429 --- .../azureservicebus/interoperability.md | 12 +++- .../Samples.cs | 57 ++++++++++++++++++- ...ureServiceBusQueueListenerConfiguration.cs | 11 ++++ ...eServiceBusQueueSubscriberConfiguration.cs | 11 ++++ ...iceBusSubscriptionListenerConfiguration.cs | 11 ++++ ...eServiceBusTopicSubscriberConfiguration.cs | 11 ++++ .../IAzureServiceBusEnvelopeMapper.cs | 9 +++ .../Internal/AzureServiceBusEndpoint.cs | 10 +++- .../Internal/AzureServiceBusEnvelopeMapper.cs | 2 +- 9 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 src/Transports/Azure/Wolverine.AzureServiceBus/IAzureServiceBusEnvelopeMapper.cs diff --git a/docs/guide/messaging/transports/azureservicebus/interoperability.md b/docs/guide/messaging/transports/azureservicebus/interoperability.md index 17e99e481..814e14022 100644 --- a/docs/guide/messaging/transports/azureservicebus/interoperability.md +++ b/docs/guide/messaging/transports/azureservicebus/interoperability.md @@ -1,7 +1,13 @@ # Interoperability -Wolverine does not yet have the API mechanisms to enable interoperability with non-Wolverine systems, but don't fret, -that is planned! +Hey, it's a complicated world and Wolverine is a relative newcomer, so it's somewhat likely you'll find yourself needing to make a Wolverine application talk via Azure Service Bus to +a non-Wolverine application. Not to worry (too much), Wolverine has you covered with the ability to customize Wolverine to Azure Service Bus mapping. -You can follow the [backlog item for that potential work here](https://github.com/JasperFx/wolverine/issues/399). +You can create interoperability with non-Wolverine applications by writing a custom `IAzureServiceBusEnvelopeMapper` +as shown in the following sample: +snippet: sample_custom_azure_service_bus_mapper + +To apply that mapper to specific endpoints, use this syntax on any type of Azure Service Bus endpoint: + +snippet: sample_configuring_custom_envelope_mapper_for_azure_service_bus~~~~ \ No newline at end of file diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus.Tests/Samples.cs b/src/Transports/Azure/Wolverine.AzureServiceBus.Tests/Samples.cs index 61d3799e8..8a55dcd03 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus.Tests/Samples.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus.Tests/Samples.cs @@ -1,7 +1,10 @@ +using Azure.Messaging.ServiceBus; using JasperFx.Core; using Microsoft.Extensions.Hosting; using Oakton.Resources; using TestMessages; +using Wolverine.AzureServiceBus.Internal; +using Wolverine.Util; namespace Wolverine.AzureServiceBus.Tests; @@ -95,4 +98,56 @@ public static async Task configure_auto_purge() #endregion } -} \ No newline at end of file + + public static async Task configure_custom_mappers() + { + #region sample_configuring_custom_envelope_mapper_for_azure_service_bus + + using var host = await Host.CreateDefaultBuilder() + .UseWolverine(opts => + { + opts.UseAzureServiceBus("some connection string") + .UseConventionalRouting() + + .ConfigureListeners(l => l.InteropWith(new CustomAzureServiceBusMapper())) + + .ConfigureSenders(s => s.InteropWith(new CustomAzureServiceBusMapper())); + }).StartAsync(); + + #endregion + } +} + + + +#region sample_custom_azure_service_bus_mapper + +public class CustomAzureServiceBusMapper : IAzureServiceBusEnvelopeMapper +{ + public void MapEnvelopeToOutgoing(Envelope envelope, ServiceBusMessage outgoing) + { + outgoing.Body = new BinaryData(envelope.Data); + if (envelope.DeliverWithin != null) + { + outgoing.TimeToLive = envelope.DeliverWithin.Value; + } + } + + public void MapIncomingToEnvelope(Envelope envelope, ServiceBusReceivedMessage incoming) + { + envelope.Data = incoming.Body.ToArray(); + + // You will have to help Wolverine out by either telling Wolverine + // what the message type is, or by reading the actual message object, + // or by telling Wolverine separately what the default message type + // is for a listening endpoint + envelope.MessageType = typeof(Message1).ToMessageTypeName(); + } + + public IEnumerable AllHeaders() + { + yield break; + } +} + +#endregion \ No newline at end of file diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusQueueListenerConfiguration.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusQueueListenerConfiguration.cs index 95deb9616..35568f0f8 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusQueueListenerConfiguration.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusQueueListenerConfiguration.cs @@ -118,4 +118,15 @@ public AzureServiceBusQueueListenerConfiguration RequireSessions(int? listenerCo return this; } + + /// + /// Utilize custom envelope mapping for Amazon Service Bus interoperability with external non-Wolverine systems + /// + /// + /// + public AzureServiceBusQueueListenerConfiguration InteropWith(IAzureServiceBusEnvelopeMapper mapper) + { + add(e => e.Mapper = mapper); + return this; + } } \ No newline at end of file diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusQueueSubscriberConfiguration.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusQueueSubscriberConfiguration.cs index 76687f59c..595501b95 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusQueueSubscriberConfiguration.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusQueueSubscriberConfiguration.cs @@ -42,4 +42,15 @@ public AzureServiceBusQueueSubscriberConfiguration RequireSessions(int? listener return this; } + + /// + /// Utilize custom envelope mapping for Amazon Service Bus interoperability with external non-Wolverine systems + /// + /// + /// + public AzureServiceBusQueueSubscriberConfiguration InteropWith(IAzureServiceBusEnvelopeMapper mapper) + { + add(e => e.Mapper = mapper); + return this; + } } \ No newline at end of file diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusSubscriptionListenerConfiguration.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusSubscriptionListenerConfiguration.cs index 448f2ef6b..94c36b159 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusSubscriptionListenerConfiguration.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusSubscriptionListenerConfiguration.cs @@ -92,4 +92,15 @@ public AzureServiceBusSubscriptionListenerConfiguration RequireSessions(int? lis return this; } + + /// + /// Utilize custom envelope mapping for Amazon Service Bus interoperability with external non-Wolverine systems + /// + /// + /// + public AzureServiceBusSubscriptionListenerConfiguration InteropWith(IAzureServiceBusEnvelopeMapper mapper) + { + add(e => e.Mapper = mapper); + return this; + } } \ No newline at end of file diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusTopicSubscriberConfiguration.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusTopicSubscriberConfiguration.cs index 9df449015..428481afc 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusTopicSubscriberConfiguration.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/AzureServiceBusTopicSubscriberConfiguration.cs @@ -23,4 +23,15 @@ public AzureServiceBusTopicSubscriberConfiguration ConfigureTopic(Action configure(e.Options)); return this; } + + /// + /// Utilize custom envelope mapping for Amazon Service Bus interoperability with external non-Wolverine systems + /// + /// + /// + public AzureServiceBusTopicSubscriberConfiguration InteropWith(IAzureServiceBusEnvelopeMapper mapper) + { + add(e => e.Mapper = mapper); + return this; + } } \ No newline at end of file diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/IAzureServiceBusEnvelopeMapper.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/IAzureServiceBusEnvelopeMapper.cs new file mode 100644 index 000000000..16d3a148c --- /dev/null +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/IAzureServiceBusEnvelopeMapper.cs @@ -0,0 +1,9 @@ +using Azure.Messaging.ServiceBus; +using Wolverine.Transports; + +namespace Wolverine.AzureServiceBus; + +public interface IAzureServiceBusEnvelopeMapper : IEnvelopeMapper +{ + +} \ No newline at end of file diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusEndpoint.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusEndpoint.cs index 393d31b8f..5704eeb79 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusEndpoint.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusEndpoint.cs @@ -57,12 +57,20 @@ protected override bool supportsMode(EndpointMode mode) } - internal IEnvelopeMapper BuildMapper(IWolverineRuntime runtime) + internal IAzureServiceBusEnvelopeMapper BuildMapper(IWolverineRuntime runtime) { + if (Mapper != null) return Mapper; + var mapper = new AzureServiceBusEnvelopeMapper(this, runtime); return mapper; } public abstract Task AcceptNextSessionAsync(CancellationToken cancellationToken); + + + /// + /// If specified, applies a custom envelope mapper to this endp[oint + /// + public IAzureServiceBusEnvelopeMapper? Mapper { get; set; } } \ No newline at end of file diff --git a/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusEnvelopeMapper.cs b/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusEnvelopeMapper.cs index 66ff575bf..57254a7cc 100644 --- a/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusEnvelopeMapper.cs +++ b/src/Transports/Azure/Wolverine.AzureServiceBus/Internal/AzureServiceBusEnvelopeMapper.cs @@ -5,7 +5,7 @@ namespace Wolverine.AzureServiceBus.Internal; -internal class AzureServiceBusEnvelopeMapper : EnvelopeMapper +internal class AzureServiceBusEnvelopeMapper : EnvelopeMapper, IAzureServiceBusEnvelopeMapper { public AzureServiceBusEnvelopeMapper(Endpoint endpoint, IWolverineRuntime runtime) : base(endpoint) {