From 304dc98db92f6fcc23fcbf158416ed713b275430 Mon Sep 17 00:00:00 2001 From: Paul Reardon Date: Mon, 2 Dec 2024 22:09:06 +0000 Subject: [PATCH] Add Default Message Mapper --- .../ServiceCollectionBrighterBuilder.cs | 1 + .../ServiceCollectionExtensions.cs | 5 +- .../ServiceCollectionMessageMapperRegistry.cs | 54 ++++++++++++++----- .../MessageMapperRegistry.cs | 47 +++++++++------- .../MessageMappers/JsonMessageMapper.cs | 47 ++++++++++++++++ 5 files changed, 122 insertions(+), 32 deletions(-) create mode 100644 src/Paramore.Brighter/MessageMappers/JsonMessageMapper.cs diff --git a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionBrighterBuilder.cs b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionBrighterBuilder.cs index 56bf372d44..37612f0d27 100644 --- a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionBrighterBuilder.cs +++ b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionBrighterBuilder.cs @@ -126,6 +126,7 @@ public IBrighterBuilder MapperRegistry(Action - public class ServiceCollectionMessageMapperRegistry + public class ServiceCollectionMessageMapperRegistry( + IServiceCollection serviceCollection, + ServiceLifetime lifetime = ServiceLifetime.Singleton) { - private readonly IServiceCollection _serviceCollection; - private readonly ServiceLifetime _lifetime; - public Dictionary Mappers { get; } = new Dictionary(); + public Type DefaultMessageMapper { get; private set; } = typeof(JsonMessageMapper<>); public Dictionary AsyncMappers { get; } = new Dictionary(); - - public ServiceCollectionMessageMapperRegistry(IServiceCollection serviceCollection, ServiceLifetime lifetime = ServiceLifetime.Singleton) - { - _serviceCollection = serviceCollection; - _lifetime = lifetime; - } - + public Type DefaultMessageMapperAsync { get; private set; } = typeof(JsonMessageMapper<>); + /// /// Register a mapper with the collection (generic version) /// @@ -76,7 +72,7 @@ public void RegisterAsync() where TRequest : class, IR /// The type of the mapper public void Add(Type message, Type mapper) { - _serviceCollection.TryAdd(new ServiceDescriptor(mapper, mapper, _lifetime)); + serviceCollection.TryAdd(new ServiceDescriptor(mapper, mapper, lifetime)); Mappers.Add(message, mapper); } @@ -87,8 +83,40 @@ public void Add(Type message, Type mapper) /// The type of the mapper public void AddAsync(Type message, Type mapper) { - _serviceCollection.TryAdd(new ServiceDescriptor(mapper, mapper, _lifetime)); + serviceCollection.TryAdd(new ServiceDescriptor(mapper, mapper, lifetime)); AsyncMappers.Add(message, mapper); } + + /// + /// Set the Default Message Mapper + /// + /// Type of default Message Mapper + public void SetDefaultMessageMapper(Type defaultMapper) + { + serviceCollection.TryAdd(new ServiceDescriptor(defaultMapper, defaultMapper, lifetime)); + DefaultMessageMapper = defaultMapper; + } + + /// + /// Set the Default Async Message Mapper + /// + /// Type of default async message mapper + public void SetDefaultMessageMapperAsync(Type defaultMapper) + { + serviceCollection.TryAdd(new ServiceDescriptor(defaultMapper, defaultMapper, lifetime)); + DefaultMessageMapperAsync = defaultMapper; + } + + /// + /// Ensure that the default mappers are registered with Dependency Injection + /// + public void EnsureDefaultMessageMapperIsRegistered() + { + if(DefaultMessageMapper != null) + serviceCollection.TryAdd(new ServiceDescriptor(DefaultMessageMapper, DefaultMessageMapper, lifetime)); + if (DefaultMessageMapperAsync != null) + serviceCollection.TryAdd(new ServiceDescriptor(DefaultMessageMapperAsync, DefaultMessageMapperAsync, + lifetime)); + } } } diff --git a/src/Paramore.Brighter/MessageMapperRegistry.cs b/src/Paramore.Brighter/MessageMapperRegistry.cs index 6e1ad7d3dc..5bf294e790 100644 --- a/src/Paramore.Brighter/MessageMapperRegistry.cs +++ b/src/Paramore.Brighter/MessageMapperRegistry.cs @@ -24,6 +24,7 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; +using Paramore.Brighter.MessageMappers; namespace Paramore.Brighter { @@ -40,16 +41,24 @@ public class MessageMapperRegistry : IAmAMessageMapperRegistry, IAmAMessageMappe private readonly IAmAMessageMapperFactoryAsync? _messageMapperFactoryAsync; private readonly Dictionary _messageMappers = new Dictionary(); private readonly Dictionary _asyncMessageMappers = new Dictionary(); + private readonly Type? _defaultMessageMapper; + private readonly Type? _defaultMessageMapperAsync; /// /// Initializes a new instance of the class. /// /// The message mapper factory. /// The async message mapper factory - public MessageMapperRegistry(IAmAMessageMapperFactory? messageMapperFactory, IAmAMessageMapperFactoryAsync? messageMapperFactoryAsync) + /// The default message Mapper + /// The default Async Message Mapper + public MessageMapperRegistry(IAmAMessageMapperFactory? messageMapperFactory, + IAmAMessageMapperFactoryAsync? messageMapperFactoryAsync, Type? defaultMessageMapper = null, + Type? defaultMessageMapperAsync = null) { _messageMapperFactory = messageMapperFactory; _messageMapperFactoryAsync = messageMapperFactoryAsync; + _defaultMessageMapper = defaultMessageMapper; + _defaultMessageMapperAsync = defaultMessageMapperAsync; if (messageMapperFactory == null && messageMapperFactoryAsync == null) throw new ConfigurationException("Should have at least one factory"); @@ -62,17 +71,18 @@ public MessageMapperRegistry(IAmAMessageMapperFactory? messageMapperFactory, IAm /// IAmAMessageMapper<TRequest>. public IAmAMessageMapper? Get() where TRequest : class, IRequest { - if ( _messageMapperFactory is not null && _messageMappers.ContainsKey(typeof(TRequest))) - { - var messageMapperType = _messageMappers[typeof(TRequest)]; - return (IAmAMessageMapper)_messageMapperFactory.Create(messageMapperType); - } - else - { + if (_messageMapperFactory is null) return null; - } + + var messageMapperType = _messageMappers.ContainsKey(typeof(TRequest)) + ? _messageMappers[typeof(TRequest)] + : _defaultMessageMapper; + + if (messageMapperType is null) return null; + + return (IAmAMessageMapper)_messageMapperFactory.Create(messageMapperType); } - + /// /// Gets this instance. /// @@ -80,15 +90,16 @@ public MessageMapperRegistry(IAmAMessageMapperFactory? messageMapperFactory, IAm /// IAmAMessageMapperAsync<TRequest>. public IAmAMessageMapperAsync? GetAsync() where TRequest : class, IRequest { - if (_messageMapperFactoryAsync is not null && _asyncMessageMappers.ContainsKey(typeof(TRequest))) - { - var messageMapperType = _asyncMessageMappers[typeof(TRequest)]; - return (IAmAMessageMapperAsync)_messageMapperFactoryAsync.Create(messageMapperType); - } - else - { + if (_messageMapperFactoryAsync is null) return null; - } + + var messageMapperType = _asyncMessageMappers.ContainsKey(typeof(TRequest)) + ? _asyncMessageMappers[typeof(TRequest)] + : _defaultMessageMapperAsync; + + if (messageMapperType is null) return null; + + return (IAmAMessageMapperAsync)_messageMapperFactoryAsync.Create(messageMapperType); } /// diff --git a/src/Paramore.Brighter/MessageMappers/JsonMessageMapper.cs b/src/Paramore.Brighter/MessageMappers/JsonMessageMapper.cs new file mode 100644 index 0000000000..14292db370 --- /dev/null +++ b/src/Paramore.Brighter/MessageMappers/JsonMessageMapper.cs @@ -0,0 +1,47 @@ +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace Paramore.Brighter.MessageMappers; + +public class JsonMessageMapper(RequestContext? context) : IAmAMessageMapper, IAmAMessageMapperAsync where TRequest : class, IRequest +{ + public IRequestContext? Context { get; set; } = context; + + public Task MapToMessageAsync(TRequest request, Publication publication, + CancellationToken cancellationToken = default) + => Task.FromResult(MapToMessage(request, publication)); + + public Task MapToRequestAsync(Message message, CancellationToken cancellationToken = default) + => Task.FromResult(MapToRequest(message)); + + public Message MapToMessage(TRequest request, Publication publication) + { + MessageType messageType = request switch + { + ICommand => MessageType.MT_COMMAND, + IEvent => MessageType.MT_EVENT, + _ => throw new ArgumentException(@"This message mapper can only map Commands and Events", nameof(request)) + }; + + if(publication.Topic is null) + throw new ArgumentException($"No Topic Defined for {publication}"); + + var header = new MessageHeader(messageId: request.Id, topic: publication.Topic, messageType: messageType); + + var body = new MessageBody(JsonSerializer.Serialize(request, JsonSerialisationOptions.Options)); + var message = new Message(header, body); + return message; + } + + public TRequest MapToRequest(Message message) + { + var request = JsonSerializer.Deserialize(message.Body.Value, JsonSerialisationOptions.Options); + + if (request is null) + throw new ArgumentException($"Unable to deseralise message body for {message.Header.Topic}"); + + return request; + } +}