From 70675a23b78c5dbc81cd7032490acef5ffde3ad7 Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Fri, 1 Dec 2023 15:45:50 +0000 Subject: [PATCH 01/15] Fix the tests and wrapper lines; broken as command processor and dispatcher don't use yet. --- .../ServiceCollectionExtensions.cs | 7 +- .../ServiceProviderTransformerFactory.cs | 6 +- .../ServiceProviderTransformerFactoryAsync.cs | 71 +++++ .../ControlBusReceiverBuilder.cs | 8 +- .../MessagePump.cs | 2 +- src/Paramore.Brighter/CommandProcessor.cs | 3 +- .../ControlBusSenderFactory.cs | 4 +- .../IAmAMessageMapperAsync.cs | 57 ++++ .../IAmAMessageMapperFactoryAsync.cs | 46 +++ .../IAmAMessageMapperRegistry.cs | 10 + .../IAmAMessageMapperRegistryAsync.cs | 59 ++++ src/Paramore.Brighter/IAmAMessageTransform.cs | 72 +++++ .../IAmAMessageTransformerFactory.cs | 4 +- .../IAmAMessageTransformerFactoryAsync.cs | 50 ++++ .../IAmATransformLifetime.cs | 2 +- .../IAmATransformLifetimeAsync.cs | 9 + .../MessageMapperRegistry.cs | 76 ++++- .../SimpleMessageMapperFactoryAsync.cs | 57 ++++ .../SimpleMessageTransformerFactory.cs | 8 +- .../SimpleMessageTransformerFactoryAsync.cs | 53 ++++ .../TransformLifetimeScope.cs | 4 +- .../TransformLifetimeScopeAsync.cs | 46 +++ src/Paramore.Brighter/TransformPipeline.cs | 2 +- .../TransformPipelineAsync.cs | 34 +++ .../TransformPipelineBuilder.cs | 4 +- .../TransformPipelineBuilderAsync.cs | 262 ++++++++++++++++++ src/Paramore.Brighter/TransformerFactory.cs | 2 +- .../TransformerFactoryAsync.cs | 50 ++++ src/Paramore.Brighter/UnwrapPipeline.cs | 12 +- src/Paramore.Brighter/UnwrapPipelineAsync.cs | 84 ++++++ src/Paramore.Brighter/WrapPipeline.cs | 6 +- src/Paramore.Brighter/WrapPipelineAsync.cs | 87 ++++++ ...n_throwing_defer_action_respect_redrive.cs | 3 +- .../When_unwrapping_a_large_message.cs | 16 +- .../When_wrapping_a_large_message.cs | 19 +- .../When_unwrapping_a_large_message.cs | 16 +- .../When_wrapping_a_large_message.cs | 16 +- .../Claims/When_unwrapping_a_large_message.cs | 14 +- .../Claims/When_wrapping_a_large_message.cs | 17 +- ..._PostBox_On_The_Command_Processor_Async.cs | 4 +- ...ling_A_Server_Via_The_Command_Processor.cs | 2 +- ...The_Command_Processor_With_No_In_Mapper.cs | 2 +- ...he_Command_Processor_With_No_Out_Mapper.cs | 2 +- ...a_The_Command_Processor_With_No_Timeout.cs | 2 +- ...PostBox_On_The_Command_Processor _Async.cs | 4 +- ...ng_The_PostBox_On_The_Command_Processor.cs | 4 +- ...positing_A_Message_In_The_Message_Store.cs | 4 +- ...ing_A_Message_In_The_Message_StoreAsync.cs | 4 +- ..._Message_In_The_Message_StoreAsync_Bulk.cs | 2 +- ...ing_A_Message_In_The_Message_Store_Bulk.cs | 2 +- ...ng_The_PostBox_On_The_Command_Processor.cs | 4 +- ..._PostBox_On_The_Command_Processor_Async.cs | 4 +- ...And_There_Is_No_Message_Mapper_Registry.cs | 4 +- ...ere_Is_No_Message_Mapper_Registry_Async.cs | 4 +- ...essage_And_There_Is_No_Message_Producer.cs | 4 +- ...ting_A_Message_To_The_Command_Processor.cs | 4 +- ..._Message_To_The_Command_Processor_Async.cs | 4 +- ..._Limit_Total_Writes_To_OutBox_In_Window.cs | 4 +- .../When_Posting_Via_A_Control_Bus_Sender.cs | 4 +- ..._Posting_Via_A_Control_Bus_Sender_Async.cs | 4 +- .../When_Posting_With_A_Default_Policy.cs | 4 +- ...Posting_With_An_In_Memory_Message_Store.cs | 4 +- ...g_With_An_In_Memory_Message_Store_Async.cs | 4 +- ...d_retry_until_connection_re_established.cs | 3 +- ...d_retry_until_connection_re_established.cs | 3 +- ...Then_message_is_requeued_until_rejected.cs | 3 +- ...message_is_requeued_until_rejectedAsync.cs | 3 +- ...handled_exception_Then_message_is_acked.cs | 3 +- ...d_exception_Then_message_is_acked_async.cs | 3 +- ..._to_connect_a_channel_and_handler_async.cs | 4 +- ...as_a_new_connection_added_while_running.cs | 4 +- ..._asked_to_connect_a_channel_and_handler.cs | 4 +- ...essage_dispatcher_restarts_a_connection.cs | 4 +- ...tion_after_all_connections_have_stopped.cs | 4 +- ...a_message_dispatcher_shuts_a_connection.cs | 4 +- ...er_starts_different_types_of_performers.cs | 4 +- ...e_dispatcher_starts_multiple_performers.cs | 4 +- ...message_fails_to_be_mapped_to_a_request.cs | 3 +- ...e_unacceptable_message_limit_is_reached.cs | 3 +- ...is_dispatched_it_should_reach_a_handler.cs | 4 +- ...patched_it_should_reach_a_handler_async.cs | 3 +- ...threshold_for_commands_has_been_reached.cs | 3 +- ...t_threshold_for_events_has_been_reached.cs | 3 +- ..._requeue_of_command_exception_is_thrown.cs | 3 +- ..._a_requeue_of_event_exception_is_thrown.cs | 4 +- ...Then_message_is_requeued_until_rejected.cs | 3 +- ...message_is_requeued_until_rejectedAsync.cs | 3 +- ...handled_exception_Then_message_is_acked.cs | 3 +- ...d_exception_Then_message_is_acked_async.cs | 3 +- ...hen_an_unacceptable_message_is_recieved.cs | 3 +- ...n_unacceptable_message_limit_is_reached.cs | 3 +- ...a_channel_pump_out_to_command_processor.cs | 3 +- ...pump_on_a_thread_should_be_able_to_stop.cs | 3 +- .../Test Doubles/MyParameterizedTransform.cs | 40 +++ ...arameterizedTransformMessageMapperAsync.cs | 28 ++ .../Test Doubles/MySimpleTransform.cs | 25 ++ ...yTransformableCommandMessageMapperAsync.cs | 27 ++ .../MyVanillaCommandMessageMapperAsync.cs | 25 ++ .../Test Doubles/Transform.cs | 35 +++ ...e_Mapper_Map_To_Message_Has_A_Transform.cs | 10 +- ...per_Map_To_Message_Has_A_TransformAsync.cs | 45 +++ ..._Mapper_Map_To_Message_Has_No_Transform.cs | 8 +- ...er_Map_To_Message_Has_No_TransformAsync.cs | 45 +++ ...e_Mapper_Map_To_Request_Has_A_Transform.cs | 11 +- ...per_Map_To_Request_Has_A_TransformAsync.cs | 46 +++ ..._Mapper_Map_To_Request_Has_No_Transform.cs | 8 +- ...g_A_Pipeline_Builder_Without_A_Registry.cs | 2 +- ...peline_Builder_Without_A_Registry_Async.cs | 27 ++ .../When_Creating_A_Wrap_Without_A_Factory.cs | 14 +- ...Creating_A_Wrap_Without_A_Factory_Async.cs | 57 ++++ ...en_Creating_An_Unwrap_Without_A_Factory.cs | 12 +- ...ating_An_Unwrap_Without_A_Factory_Async.cs | 61 ++++ .../When_Unwrapping_A_Message_Mapper.cs | 15 +- .../When_Unwrapping_A_Message_MapperAsync.cs | 51 ++++ ...age_Mapper_But_Not_In_Transform_Factory.cs | 8 +- ...apping_A_Message_Mapper_With_Parameters.cs | 12 +- ...hen_Unwrapping_A_Vanilla_Message_Mapper.cs | 12 +- ...nwrapping_A_Vanilla_Message_MapperAsync.cs | 51 ++++ ...hen_Unwrapping_But_No_Registered_Mapper.cs | 8 +- ...nwrapping_But_No_Registered_MapperAsync.cs | 49 ++++ .../When_Wrapping_A_Message_Mapper.cs | 12 +- .../When_Wrapping_A_Message_MapperAsync.cs | 44 +++ ...age_Mapper_But_Not_In_Transform_Factory.cs | 6 +- ...pper_But_Not_In_Transform_Factory_Async.cs | 37 +++ ...apping_A_Message_Mapper_With_Parameters.cs | 13 +- ..._A_Message_Mapper_With_Parameters_Async.cs | 45 +++ .../When_Wrapping_A_Vanilla_Message_Mapper.cs | 10 +- ..._Wrapping_A_Vanilla_Message_MapperAsync.cs | 43 +++ .../When_Wrapping_But_No_Registered_Mapper.cs | 14 +- ..._Wrapping_But_No_Registered_MapperAsync.cs | 39 +++ .../When_Wrapping_Clean_Up_The_Pipeline.cs | 16 +- ...hen_Wrapping_Clean_Up_The_PipelineAsync.cs | 62 +++++ ..._Clearing_The_Outbox_A_Span_Is_Exported.cs | 4 +- ...ing_The_Outbox_async_A_Span_Is_Exported.cs | 4 +- .../When_building_a_dispatcher.cs | 4 +- ...uilding_a_dispatcher_with_named_gateway.cs | 5 +- ...try_limits_force_a_message_onto_the_DLQ.cs | 3 +- 137 files changed, 2347 insertions(+), 228 deletions(-) create mode 100644 src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderTransformerFactoryAsync.cs create mode 100644 src/Paramore.Brighter/IAmAMessageMapperAsync.cs create mode 100644 src/Paramore.Brighter/IAmAMessageMapperFactoryAsync.cs create mode 100644 src/Paramore.Brighter/IAmAMessageMapperRegistryAsync.cs create mode 100644 src/Paramore.Brighter/IAmAMessageTransform.cs create mode 100644 src/Paramore.Brighter/IAmAMessageTransformerFactoryAsync.cs create mode 100644 src/Paramore.Brighter/IAmATransformLifetimeAsync.cs create mode 100644 src/Paramore.Brighter/SimpleMessageMapperFactoryAsync.cs create mode 100644 src/Paramore.Brighter/SimpleMessageTransformerFactoryAsync.cs create mode 100644 src/Paramore.Brighter/TransformLifetimeScopeAsync.cs create mode 100644 src/Paramore.Brighter/TransformPipelineAsync.cs create mode 100644 src/Paramore.Brighter/TransformPipelineBuilderAsync.cs create mode 100644 src/Paramore.Brighter/TransformerFactoryAsync.cs create mode 100644 src/Paramore.Brighter/UnwrapPipelineAsync.cs create mode 100644 src/Paramore.Brighter/WrapPipelineAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyParameterizedTransform.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyParameterizedTransformMessageMapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MySimpleTransform.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyTransformableCommandMessageMapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyVanillaCommandMessageMapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/Transform.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_TransformAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_TransformAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_TransformAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Pipeline_Builder_Without_A_Registry_Async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory_Async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory_Async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_MapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_MapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_MapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_MapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_But_Not_In_Transform_Factory_Async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_With_Parameters_Async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_MapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_But_No_Registered_MapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_PipelineAsync.cs diff --git a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index bcf76d72b8..89d5aa35c3 100644 --- a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -354,11 +354,14 @@ public static MessageMapperRegistry MessageMapperRegistry(IServiceProvider provi { var serviceCollectionMessageMapperRegistry = provider.GetService(); - var messageMapperRegistry = new MessageMapperRegistry(new ServiceProviderMapperFactory(provider)); + var messageMapperRegistry = new MessageMapperRegistry( + new ServiceProviderMapperFactory(provider), + null + ); foreach (var messageMapper in serviceCollectionMessageMapperRegistry) { - messageMapperRegistry.Add(messageMapper.Key, messageMapper.Value); + messageMapperRegistry.Register(messageMapper.Key, messageMapper.Value); } return messageMapperRegistry; diff --git a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderTransformerFactory.cs b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderTransformerFactory.cs index ea9cd99c1b..5c23a47113 100644 --- a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderTransformerFactory.cs +++ b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderTransformerFactory.cs @@ -51,16 +51,16 @@ public ServiceProviderTransformerFactory(IServiceProvider serviceProvider) /// /// The type of transformer to create /// - public IAmAMessageTransformAsync Create(Type transformerType) + public IAmAMessageTransform Create(Type transformerType) { - return (IAmAMessageTransformAsync) _serviceProvider.GetService(transformerType); + return (IAmAMessageTransform) _serviceProvider.GetService(transformerType); } /// /// If the transform was scoped as transient, we release it when the pipeline is finished /// /// - public void Release(IAmAMessageTransformAsync transformer) + public void Release(IAmAMessageTransform transformer) { if (!_isTransient) return; diff --git a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderTransformerFactoryAsync.cs b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderTransformerFactoryAsync.cs new file mode 100644 index 0000000000..b18603869e --- /dev/null +++ b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderTransformerFactoryAsync.cs @@ -0,0 +1,71 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using Microsoft.Extensions.DependencyInjection; + +namespace Paramore.Brighter.Extensions.DependencyInjection +{ + /// + /// A factory for creating transformers, backed by the .NET Service Collection + /// + public class ServiceProviderTransformerFactoryAsync : IAmAMessageTransformerFactoryAsync + { + private readonly IServiceProvider _serviceProvider; + private readonly bool _isTransient; + + /// + /// Constructs a transformer factory + /// + /// The IoC container we use to satisfy requests for transforms + public ServiceProviderTransformerFactoryAsync(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + var options = serviceProvider.GetRequiredService(); + if (options == null) _isTransient = false; else _isTransient = options.HandlerLifetime == ServiceLifetime.Transient; + } + + /// + /// Creates a specific transformer on demand + /// + /// The type of transformer to create + /// + public IAmAMessageTransformAsync Create(Type transformerType) + { + return (IAmAMessageTransformAsync) _serviceProvider.GetService(transformerType); + } + + /// + /// If the transform was scoped as transient, we release it when the pipeline is finished + /// + /// + public void Release(IAmAMessageTransformAsync transformer) + { + if (!_isTransient) return; + + var disposal = transformer as IDisposable; + disposal?.Dispose(); + } + } +} diff --git a/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs b/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs index aaba2543ce..9b0d572c66 100644 --- a/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs +++ b/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs @@ -139,11 +139,15 @@ public Dispatcher Build(string hostName) subscriberRegistry.Register(); subscriberRegistry.Register(); - var incomingMessageMapperRegistry = new MessageMapperRegistry(new ControlBusMessageMapperFactory()); + var incomingMessageMapperRegistry = new MessageMapperRegistry( + new ControlBusMessageMapperFactory(), null + ); incomingMessageMapperRegistry.Register(); incomingMessageMapperRegistry.Register(); - var outgoingMessageMapperRegistry = new MessageMapperRegistry(new ControlBusMessageMapperFactory()); + var outgoingMessageMapperRegistry = new MessageMapperRegistry( + new ControlBusMessageMapperFactory(), null + ); outgoingMessageMapperRegistry.Register(); var producerRegistry = _producerRegistryFactory.Create(); diff --git a/src/Paramore.Brighter.ServiceActivator/MessagePump.cs b/src/Paramore.Brighter.ServiceActivator/MessagePump.cs index a0655f0575..900d47e657 100644 --- a/src/Paramore.Brighter.ServiceActivator/MessagePump.cs +++ b/src/Paramore.Brighter.ServiceActivator/MessagePump.cs @@ -339,7 +339,7 @@ private TRequest TranslateMessage(Message message) try { - request = _unwrapPipeline.UnwrapAsync(message).GetAwaiter().GetResult(); + request = _unwrapPipeline.Unwrap(message); } catch (ConfigurationException) { diff --git a/src/Paramore.Brighter/CommandProcessor.cs b/src/Paramore.Brighter/CommandProcessor.cs index 332bc4e52a..141ecd3ec5 100644 --- a/src/Paramore.Brighter/CommandProcessor.cs +++ b/src/Paramore.Brighter/CommandProcessor.cs @@ -825,8 +825,9 @@ public TResponse Call(T request, int timeOutInMilliseconds) { s_logger.LogDebug("Reply received from {ChannelName}", channelName); //map to request is map to a response, but it is a request from consumer point of view. Confusing, but... + //TODO: this only handles a synchronous unwrap pipeline, we need to handle async too var inUnwrapPipeline = _transformPipelineBuilder.BuildUnwrapPipeline(); - response = inUnwrapPipeline.UnwrapAsync(responseMessage).GetAwaiter().GetResult(); + response = inUnwrapPipeline.Unwrap(responseMessage); Send(response); } diff --git a/src/Paramore.Brighter/ControlBusSenderFactory.cs b/src/Paramore.Brighter/ControlBusSenderFactory.cs index e69a4b2f2a..bf2791d5f5 100644 --- a/src/Paramore.Brighter/ControlBusSenderFactory.cs +++ b/src/Paramore.Brighter/ControlBusSenderFactory.cs @@ -43,7 +43,9 @@ public class ControlBusSenderFactory : IAmAControlBusSenderFactory public IAmAControlBusSender Create(IAmAnOutbox outbox, IAmAProducerRegistry producerRegistry) where T : Message { - var mapper = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MonitorEventMessageMapper())); + var mapper = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MonitorEventMessageMapper()), + null); mapper.Register(); var busConfiguration = new ExternalBusConfiguration(); diff --git a/src/Paramore.Brighter/IAmAMessageMapperAsync.cs b/src/Paramore.Brighter/IAmAMessageMapperAsync.cs new file mode 100644 index 0000000000..cba47f1cf6 --- /dev/null +++ b/src/Paramore.Brighter/IAmAMessageMapperAsync.cs @@ -0,0 +1,57 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System.Threading.Tasks; + +namespace Paramore.Brighter +{ + /// + /// Interface IAmAMessageMapperAsync + /// Map between a or an and a . You must implement this for each Command or Message you intend to send over + /// a Task Queue + /// + public interface IAmAMessageMapperAsync { } + + /// + /// Interface IAmAMessageMapperAsync + /// Map between a or an and a . You must implement this for each Command or Message you intend to send over + /// a Task Queue + /// + /// The type of the t request. + public interface IAmAMessageMapperAsync : IAmAMessageMapperAsync where TRequest : class, IRequest + { + /// + /// Maps to message. + /// + /// The request. + /// Message. + Task MapToMessage(TRequest request); + /// + /// Maps to request. + /// + /// The message. + /// TRequest. + Task MapToRequest(Message message); + } +} diff --git a/src/Paramore.Brighter/IAmAMessageMapperFactoryAsync.cs b/src/Paramore.Brighter/IAmAMessageMapperFactoryAsync.cs new file mode 100644 index 0000000000..61c21ce780 --- /dev/null +++ b/src/Paramore.Brighter/IAmAMessageMapperFactoryAsync.cs @@ -0,0 +1,46 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; + +namespace Paramore.Brighter +{ + /// + /// Interface IAmAMessageMapperFactoryAsync + /// In order to use a Task Queue approach we require you to provide + /// a to map between or and a registered via + /// We then call the instance of the factory which the client provides to create instances of that . You will need to implement the + /// to use the Task Queue approach, and provide the instance of your mapper on request. Typically you might use an IoC container + /// to implement this. + /// + public interface IAmAMessageMapperFactoryAsync + { + /// + /// Creates the specified message mapper type. + /// + /// Type of the message mapper. + /// IAmAMessageMapper. + IAmAMessageMapperAsync Create(Type messageMapperType); + } +} diff --git a/src/Paramore.Brighter/IAmAMessageMapperRegistry.cs b/src/Paramore.Brighter/IAmAMessageMapperRegistry.cs index 0c1363c512..f60e611686 100644 --- a/src/Paramore.Brighter/IAmAMessageMapperRegistry.cs +++ b/src/Paramore.Brighter/IAmAMessageMapperRegistry.cs @@ -22,6 +22,8 @@ THE SOFTWARE. */ #endregion +using System; + namespace Paramore.Brighter { /// @@ -45,5 +47,13 @@ public interface IAmAMessageMapperRegistry /// The type of the t request. /// The type of the t message mapper. void Register() where TRequest : class, IRequest where TMessageMapper : class, IAmAMessageMapper; + + /// + /// Registers this instance. + /// + /// The type of the request to map + /// The type of the mapper for this request + /// + void Register(Type request, Type mapper); } } diff --git a/src/Paramore.Brighter/IAmAMessageMapperRegistryAsync.cs b/src/Paramore.Brighter/IAmAMessageMapperRegistryAsync.cs new file mode 100644 index 0000000000..35cb9013b8 --- /dev/null +++ b/src/Paramore.Brighter/IAmAMessageMapperRegistryAsync.cs @@ -0,0 +1,59 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; + +namespace Paramore.Brighter +{ + /// + /// Interface IAmAMessageMapperRegistryAsync + /// In order to use a Task Queue approach we require you to provide + /// a to map between or and a + /// registered via + /// The default implementation is suitable for most purposes and the interface is provided for testing + /// + public interface IAmAMessageMapperRegistryAsync + { + /// + /// Gets this instance. + /// + /// + /// IAmAMessageMapperAsync<T>. + IAmAMessageMapperAsync GetAsync() where T : class, IRequest; + /// + /// Registers this instance. + /// + /// The type of the t request. + /// The type of the t message mapper. + void RegisterAsync() where TRequest : class, IRequest where TMessageMapper : class, IAmAMessageMapperAsync; + + /// + /// Registers this instance. + /// + /// The type of the request to map + /// The type of the mapper for this request + /// + void RegisterAsync(Type request, Type mapper); + } +} diff --git a/src/Paramore.Brighter/IAmAMessageTransform.cs b/src/Paramore.Brighter/IAmAMessageTransform.cs new file mode 100644 index 0000000000..5546ebe2e7 --- /dev/null +++ b/src/Paramore.Brighter/IAmAMessageTransform.cs @@ -0,0 +1,72 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ +#endregion + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Paramore.Brighter +{ + /// + /// interface IAmAMessageTransformAsync + /// Derive from this class to provide a transform that modifies a message. It is intended to support re-usable behaviors for a + /// without using inheritance or DI, and in a consistent fashion. + /// On Wrap, we assume that you are modifying an outgoing message. + /// On an Unwrap, we assume that you are modifying an incoming message + /// A typical usage is the Claim Check pattern see https://www.enterpriseintegrationpatterns.com/patterns/messaging/StoreInLibrary.html + /// + public interface IAmAMessageTransform : IDisposable + { + /// + /// Initializes from the wrap attribute parameters. Use when you need to provide parameter information from the + /// attribute to the transform. Note that the attribute implementation might include types other than primitives that you intend to pass across, but + /// the attribute itself can only use primitives. + /// + /// The initializer list. + void InitializeWrapFromAttributeParams(params object[] initializerList); + + /// + /// Initializes from the unwrap attribute parameters. Use when you need to provide parameter information from the + /// attribute to the transform. Note that the attribute implementation might include types other than primitives that you intend to pass across, but + /// the attribute itself can only use primitives. + /// + /// The initializer list. + void InitializeUnwrapFromAttributeParams(params object[] initializerList); + + /// + /// A Wrap modifies an outgoing message by altering its header or body + /// A Wrap always runs after you map the to a + /// + /// The original message + /// The modified message + Message Wrap(Message message); + + /// + /// An Unwrap modifies an incoming message by altering its header or body + /// An Unwrap always runs before you map the to a + /// + /// The original message + /// The modified message + Message Unwrap(Message message); + } +} diff --git a/src/Paramore.Brighter/IAmAMessageTransformerFactory.cs b/src/Paramore.Brighter/IAmAMessageTransformerFactory.cs index f15394e795..7349ab1372 100644 --- a/src/Paramore.Brighter/IAmAMessageTransformerFactory.cs +++ b/src/Paramore.Brighter/IAmAMessageTransformerFactory.cs @@ -40,11 +40,11 @@ public interface IAmAMessageTransformerFactory /// /// Type of the handler. /// IAmAMessageTransformAsync - IAmAMessageTransformAsync Create(Type transformerType); + IAmAMessageTransform Create(Type transformerType); /// /// Releases the specified transformer. /// /// The transformer - void Release(IAmAMessageTransformAsync transformer); + void Release(IAmAMessageTransform transformer); } } diff --git a/src/Paramore.Brighter/IAmAMessageTransformerFactoryAsync.cs b/src/Paramore.Brighter/IAmAMessageTransformerFactoryAsync.cs new file mode 100644 index 0000000000..c1b960d6da --- /dev/null +++ b/src/Paramore.Brighter/IAmAMessageTransformerFactoryAsync.cs @@ -0,0 +1,50 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ +#endregion + +using System; + +namespace Paramore.Brighter +{ + /// + /// Interface IAmAMessageTransformerFactory + /// We do not know how to create instances of implemented by your application, but need to create instances to instantiate a pipeline. + /// To achieve this we require clients of the Paramore.Brighter library need to implement to provide + /// instances of their types. You need to provide a Handler Factory to support all + /// referred to by a or a . + /// Typically you would use an IoC container to implement the Transformer Factory. + /// + public interface IAmAMessageTransformerFactoryAsync + { + /// + /// Creates the specified transformer type. + /// + /// Type of the handler. + /// IAmAMessageTransformAsync + IAmAMessageTransformAsync Create(Type transformerType); + /// + /// Releases the specified transformer. + /// + /// The transformer + void Release(IAmAMessageTransformAsync transformer); + } +} diff --git a/src/Paramore.Brighter/IAmATransformLifetime.cs b/src/Paramore.Brighter/IAmATransformLifetime.cs index fae1fcf85e..349c702b6e 100644 --- a/src/Paramore.Brighter/IAmATransformLifetime.cs +++ b/src/Paramore.Brighter/IAmATransformLifetime.cs @@ -4,6 +4,6 @@ namespace Paramore.Brighter { internal interface IAmATransformLifetime : IDisposable { - void Add(IAmAMessageTransformAsync transform); + void Add(IAmAMessageTransform transform); } } diff --git a/src/Paramore.Brighter/IAmATransformLifetimeAsync.cs b/src/Paramore.Brighter/IAmATransformLifetimeAsync.cs new file mode 100644 index 0000000000..93b9cc6c68 --- /dev/null +++ b/src/Paramore.Brighter/IAmATransformLifetimeAsync.cs @@ -0,0 +1,9 @@ +using System; + +namespace Paramore.Brighter +{ + internal interface IAmATransformLifetimeAsync : IDisposable + { + void Add(IAmAMessageTransformAsync transform); + } +} diff --git a/src/Paramore.Brighter/MessageMapperRegistry.cs b/src/Paramore.Brighter/MessageMapperRegistry.cs index 2c2032afb0..cb4c12952f 100644 --- a/src/Paramore.Brighter/MessageMapperRegistry.cs +++ b/src/Paramore.Brighter/MessageMapperRegistry.cs @@ -35,18 +35,25 @@ namespace Paramore.Brighter /// registered via /// This is a default implementation of which is suitable for most usages, the interface is provided mainly for testing /// - public class MessageMapperRegistry : IAmAMessageMapperRegistry, IEnumerable> + public class MessageMapperRegistry : IAmAMessageMapperRegistry, IAmAMessageMapperRegistryAsync, IEnumerable> { private readonly IAmAMessageMapperFactory _messageMapperFactory; + private readonly IAmAMessageMapperFactoryAsync _messageMapperFactoryAsync; private readonly Dictionary _messageMappers = new Dictionary(); + private readonly Dictionary _asyncMessageMappers = new Dictionary(); /// /// Initializes a new instance of the class. /// /// The message mapper factory. - public MessageMapperRegistry(IAmAMessageMapperFactory messageMapperFactory) + /// The async message mapper factory + public MessageMapperRegistry(IAmAMessageMapperFactory messageMapperFactory, IAmAMessageMapperFactoryAsync messageMapperFactoryAsync) { _messageMapperFactory = messageMapperFactory; + _messageMapperFactoryAsync = messageMapperFactoryAsync; + + if (messageMapperFactory == null && messageMapperFactoryAsync == null) + throw new ConfigurationException("Should have at least one factory"); } /// @@ -66,16 +73,23 @@ public IAmAMessageMapper Get() where TRequest : class, IRequ return null; } } - - //support object initializer + /// - /// Adds the specified message type. + /// Gets this instance. /// - /// Type of the message. - /// The message mapper. - public void Add(Type messageType, Type messageMapper) + /// The type of the t request. + /// IAmAMessageMapperAsync<TRequest>. + public IAmAMessageMapperAsync GetAsync() where TRequest : class, IRequest { - _messageMappers.Add(messageType, messageMapper); + if (_asyncMessageMappers.ContainsKey(typeof(TRequest))) + { + var messageMapperType = _messageMappers[typeof(TRequest)]; + return (IAmAMessageMapperAsync)_messageMapperFactoryAsync.Create(messageMapperType); + } + else + { + return null; + } } /// @@ -92,6 +106,49 @@ public void Register() where TRequest : class, IReques _messageMappers.Add(typeof(TRequest), typeof(TMessageMapper)); } + /// + /// Registers this instance. + /// + /// The type of the request to map + /// The type of the mapper for this request + /// + public void Register(Type request, Type mapper) + { + if (_messageMappers.ContainsKey(request)) + throw new ArgumentException(string.Format("Message type {0} already has a mapper; only one mapper can be registered per type", request.Name)); + + _messageMappers.Add(request, mapper); + } + + /// + /// Registers this instance. + /// + /// The type of the t request. + /// The type of the t message mapper. + /// + public void RegisterAsync() where TRequest : class, IRequest where TMessageMapper : class, IAmAMessageMapperAsync + { + if (_asyncMessageMappers.ContainsKey(typeof(TRequest))) + throw new ArgumentException(string.Format("Message type {0} already has a mapper; only one mapper can be registered per type", typeof(TRequest).Name)); + + _asyncMessageMappers.Add(typeof(TRequest), typeof(TMessageMapper)); + + } + + /// + /// Registers this instance. + /// + /// The type of the request to map + /// The type of the mapper for this request + /// + public void RegisterAsync(Type request, Type mapper) + { + if (_asyncMessageMappers.ContainsKey(request)) + throw new ArgumentException(string.Format("Message type {0} already has a mapper; only one mapper can be registered per type", request.Name)); + + _asyncMessageMappers.Add(request, mapper); + } + /// /// Returns an enumerator that iterates through the collection. /// @@ -109,5 +166,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + } } diff --git a/src/Paramore.Brighter/SimpleMessageMapperFactoryAsync.cs b/src/Paramore.Brighter/SimpleMessageMapperFactoryAsync.cs new file mode 100644 index 0000000000..d4c8107a5f --- /dev/null +++ b/src/Paramore.Brighter/SimpleMessageMapperFactoryAsync.cs @@ -0,0 +1,57 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; + +namespace Paramore.Brighter +{ + /// + /// Class SimpleMessageMapperFactory. + /// This allows you to return a simple function that finds a given message mapper. Intended for lightweight message mapping, + /// such as with a ControlBusSender. We recommend you wrap your IoC container for heavyweight mapping. + /// + public class SimpleMessageMapperFactoryAsync : IAmAMessageMapperFactoryAsync + { + private readonly Func _factoryMethod; + + /// + /// Initializes a new instance of the class. + /// + /// The factory method. + public SimpleMessageMapperFactoryAsync(Func factoryMethod) + { + _factoryMethod = factoryMethod; + } + + /// + /// Creates the specified message mapper type. + /// + /// Type of the message mapper. + /// IAmAMessageMapper. + public IAmAMessageMapperAsync Create(Type messageMapperType) + { + return _factoryMethod(messageMapperType); + } + } +} diff --git a/src/Paramore.Brighter/SimpleMessageTransformerFactory.cs b/src/Paramore.Brighter/SimpleMessageTransformerFactory.cs index 3d772c0e25..5fb3c290aa 100644 --- a/src/Paramore.Brighter/SimpleMessageTransformerFactory.cs +++ b/src/Paramore.Brighter/SimpleMessageTransformerFactory.cs @@ -33,19 +33,19 @@ namespace Paramore.Brighter /// public class SimpleMessageTransformerFactory : IAmAMessageTransformerFactory { - private readonly Func _factoryMethod; + private readonly Func _factoryMethod; - public SimpleMessageTransformerFactory(Func factoryMethod) + public SimpleMessageTransformerFactory(Func factoryMethod) { _factoryMethod = factoryMethod; } - public IAmAMessageTransformAsync Create(Type transformerType) + public IAmAMessageTransform Create(Type transformerType) { return _factoryMethod(transformerType); } - public void Release(IAmAMessageTransformAsync transformer) + public void Release(IAmAMessageTransform transformer) { return; } diff --git a/src/Paramore.Brighter/SimpleMessageTransformerFactoryAsync.cs b/src/Paramore.Brighter/SimpleMessageTransformerFactoryAsync.cs new file mode 100644 index 0000000000..754cd9ab88 --- /dev/null +++ b/src/Paramore.Brighter/SimpleMessageTransformerFactoryAsync.cs @@ -0,0 +1,53 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; + +namespace Paramore.Brighter +{ + /// + /// Class SimpleMessageTransformerFactory. + /// This class allows you to return a simple function that finds a transformer. Intended for lightweight transformer pipelines. + /// We recommend you wrap your IoC container for heavyweight mapping. + /// + public class SimpleMessageTransformerFactoryAsync : IAmAMessageTransformerFactoryAsync + { + private readonly Func _factoryMethod; + + public SimpleMessageTransformerFactoryAsync(Func factoryMethod) + { + _factoryMethod = factoryMethod; + } + + public IAmAMessageTransformAsync Create(Type transformerType) + { + return _factoryMethod(transformerType); + } + + public void Release(IAmAMessageTransformAsync transformer) + { + return; + } + } +} diff --git a/src/Paramore.Brighter/TransformLifetimeScope.cs b/src/Paramore.Brighter/TransformLifetimeScope.cs index 80eb290bbc..f4081c8705 100644 --- a/src/Paramore.Brighter/TransformLifetimeScope.cs +++ b/src/Paramore.Brighter/TransformLifetimeScope.cs @@ -10,7 +10,7 @@ public class TransformLifetimeScope : IAmATransformLifetime { private static readonly ILogger s_logger= ApplicationLogging.CreateLogger(); private readonly IAmAMessageTransformerFactory _factory; - private readonly IList _trackedObjects = new List(); + private readonly IList _trackedObjects = new List(); public TransformLifetimeScope(IAmAMessageTransformerFactory factory) { @@ -28,7 +28,7 @@ public void Dispose() ReleaseTrackedObjects(); } - public void Add(IAmAMessageTransformAsync instance) + public void Add(IAmAMessageTransform instance) { _trackedObjects.Add(instance); s_logger.LogDebug("Tracking instance {InstanceHashCode} of type {HandlerType}", instance.GetHashCode(), instance.GetType()); diff --git a/src/Paramore.Brighter/TransformLifetimeScopeAsync.cs b/src/Paramore.Brighter/TransformLifetimeScopeAsync.cs new file mode 100644 index 0000000000..b331d82e23 --- /dev/null +++ b/src/Paramore.Brighter/TransformLifetimeScopeAsync.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Paramore.Brighter.Extensions; +using Paramore.Brighter.Logging; + +namespace Paramore.Brighter +{ + public class TransformLifetimeScopeAsync : IAmATransformLifetimeAsync + { + private static readonly ILogger s_logger= ApplicationLogging.CreateLogger(); + private readonly IAmAMessageTransformerFactoryAsync _factory; + private readonly IList _trackedObjects = new List(); + + public TransformLifetimeScopeAsync(IAmAMessageTransformerFactoryAsync factory) + { + _factory = factory; + } + + public void Dispose() + { + ReleaseTrackedObjects(); + GC.SuppressFinalize(this); + } + + ~TransformLifetimeScopeAsync() + { + ReleaseTrackedObjects(); + } + + public void Add(IAmAMessageTransformAsync instance) + { + _trackedObjects.Add(instance); + s_logger.LogDebug("Tracking instance {InstanceHashCode} of type {HandlerType}", instance.GetHashCode(), instance.GetType()); + } + + private void ReleaseTrackedObjects() + { + _trackedObjects.Each((trackedItem) => + { + _factory.Release(trackedItem); + s_logger.LogDebug("Releasing handler instance {InstanceHashCode} of type {HandlerType}", trackedItem.GetHashCode(), trackedItem.GetType()); + }); + } + } +} diff --git a/src/Paramore.Brighter/TransformPipeline.cs b/src/Paramore.Brighter/TransformPipeline.cs index fa5d962ef8..851e434f1a 100644 --- a/src/Paramore.Brighter/TransformPipeline.cs +++ b/src/Paramore.Brighter/TransformPipeline.cs @@ -6,7 +6,7 @@ namespace Paramore.Brighter public abstract class TransformPipeline : IDisposable where TRequest : class, IRequest { protected IAmAMessageMapper MessageMapper; - protected IEnumerable Transforms; + protected IEnumerable Transforms; protected TransformLifetimeScope InstanceScope; /// diff --git a/src/Paramore.Brighter/TransformPipelineAsync.cs b/src/Paramore.Brighter/TransformPipelineAsync.cs new file mode 100644 index 0000000000..19baa67e56 --- /dev/null +++ b/src/Paramore.Brighter/TransformPipelineAsync.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; + +namespace Paramore.Brighter +{ + public abstract class TransformPipelineAsync : IDisposable where TRequest : class, IRequest + { + protected IAmAMessageMapperAsync MessageMapper; + protected IEnumerable Transforms; + protected TransformLifetimeScopeAsync InstanceScope; + + /// + /// Disposes a pipeline builder, which will call release on the factory for any transforms generated for the pipeline + /// + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + /// + /// Disposes a pipeline builder, which will call release on the factory for any transforms generated for the pipeline + /// + ~TransformPipelineAsync() + { + ReleaseUnmanagedResources(); + } + + private void ReleaseUnmanagedResources() + { + InstanceScope?.Dispose(); + } + } +} diff --git a/src/Paramore.Brighter/TransformPipelineBuilder.cs b/src/Paramore.Brighter/TransformPipelineBuilder.cs index d69b66377c..d10f5a973c 100644 --- a/src/Paramore.Brighter/TransformPipelineBuilder.cs +++ b/src/Paramore.Brighter/TransformPipelineBuilder.cs @@ -147,10 +147,10 @@ public UnwrapPipeline BuildUnwrapPipeline() where TRequest : } } - private IEnumerable BuildTransformPipeline(IEnumerable transformAttributes) + private IEnumerable BuildTransformPipeline(IEnumerable transformAttributes) where TRequest : class, IRequest { - var transforms = new List(); + var transforms = new List(); //Allowed to be null to avoid breaking v9 interfaces if (_messageTransformerFactory == null) diff --git a/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs b/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs new file mode 100644 index 0000000000..5754f0f24f --- /dev/null +++ b/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs @@ -0,0 +1,262 @@ +#region Licence + +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.Extensions.Logging; +using Paramore.Brighter.Extensions; +using Paramore.Brighter.Logging; + +namespace Paramore.Brighter +{ + /// + /// We use attributes to allow you to mix in behaviours when message mapping such as offloading large payloads via a claim check or + /// encrypting PII content. Because CSharp has single-inheritance, we can't build these in a re-usable fashion via inheritance, and + /// instead of forcing you to use DI to pull in those behaviors, we offer the option to use an attribute to mark up your message transform + /// with suitable changes. + /// We run a after the message mapper converts from an . + /// We run a before the message mapper converts to a . + /// You handle translation between and in your + /// + public class TransformPipelineBuilderAsync + { + private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly IAmAMessageMapperRegistryAsync _mapperRegistryAsync; + + private readonly IAmAMessageTransformerFactoryAsync _messageTransformerFactoryAsync; + + //GLOBAL! Cache of message mapper transform attributes. This will not be recalculated post start up. Method to clear cache below (if a broken test brought you here). + private static readonly ConcurrentDictionary> s_wrapTransformsMemento = + new ConcurrentDictionary>(); + + private static readonly ConcurrentDictionary> s_unWrapTransformsMemento = + new ConcurrentDictionary>(); + + /// + /// Creates an instance of a transform pipeline builder. + /// To avoid introducing a breaking interface for v9 we allow the transform factory to be optional, + /// so that it can be optional in a CommandProcessor constructor, and not make a breaking change to the interface. + /// In this case, transform pipelines mimic v9 behaviour and just run the mapper and not any transforms + /// To avoid silent failure, we warn on this. + /// + /// The message mapper registry, cannot be null + /// The transform factory, can be null + /// Throws a configuration exception on a null mapperRegistry + public TransformPipelineBuilderAsync(IAmAMessageMapperRegistryAsync mapperRegistryAsync, IAmAMessageTransformerFactoryAsync messageTransformerFactoryAsync) + { + _mapperRegistryAsync = mapperRegistryAsync ?? + throw new ConfigurationException("TransformPipelineBuilder expected a Message Mapper Registry but none supplied"); + + _messageTransformerFactoryAsync = messageTransformerFactoryAsync; + } + + /// + /// Builds a pipeline. + /// Anything marked with will run before the + /// Anything marked with + /// + /// + /// + /// + public WrapPipelineAsync BuildWrapPipeline() where TRequest : class, IRequest + { + try + { + var messageMapper = FindMessageMapper(); + + var transforms = BuildTransformPipeline(FindWrapTransforms(messageMapper)); + + var pipeline = new WrapPipelineAsync(messageMapper, _messageTransformerFactoryAsync, transforms); + + s_logger.LogDebug( + "New wrap pipeline created for: {message} of {pipeline}", typeof(TRequest).Name, + TraceWrapPipeline(pipeline) + ); + + var unwraps = FindUnwrapTransforms(messageMapper); + if (unwraps.Any()) + { + s_logger.LogDebug( + "Unwrap attributes on MapToMessage method for mapper of: {message} in {pipeline}, will be ignored", typeof(TRequest).Name, + TraceWrapPipeline(pipeline) + ); + } + + return pipeline; + } + catch (Exception e) + { + throw new ConfigurationException("Error building wrap pipeline for outgoing message, see inner exception for details", e); + } + } + + public UnwrapPipelineAsync BuildUnwrapPipeline() where TRequest : class, IRequest + { + try + { + var messageMapper = FindMessageMapper(); + + var transforms = BuildTransformPipeline(FindUnwrapTransforms(messageMapper)); + + var pipeline = new UnwrapPipelineAsync(transforms, _messageTransformerFactoryAsync, messageMapper); + + s_logger.LogDebug( + "New unwrap pipeline created for: {message} of {pipeline}", typeof(TRequest).Name, + TraceUnwrapPipeline(pipeline) + ); + + var wraps = FindWrapTransforms(messageMapper); + if (wraps.Any()) + { + s_logger.LogDebug( + "Wrap attributes on MapToRequest method for mapper of: {message} in {pipeline}, will be ignored", typeof(TRequest).Name, + TraceUnwrapPipeline(pipeline) + ); + } + + return pipeline; + } + catch (Exception e) + { + throw new ConfigurationException("Error building unwrap pipeline for outgoing message, see inner exception for details", e); + } + } + + private IEnumerable BuildTransformPipeline(IEnumerable transformAttributes) + where TRequest : class, IRequest + { + var transforms = new List(); + + //Allowed to be null to avoid breaking v9 interfaces + if (_messageTransformerFactoryAsync == null) + { + int i = transformAttributes.Count(); + if (i > 0) + s_logger.LogWarning( + "No message transformer factory configured, so no transforms will be created but {transformCount} configured", i); + + return transforms; + } + + transformAttributes.Each((attribute) => + { + var transformType = attribute.GetHandlerType(); + var transformer = new TransformerFactoryAsync(attribute, _messageTransformerFactoryAsync).CreateMessageTransformer(); + if (transformer == null) + { + throw new InvalidOperationException(string.Format("Message Transformer Factory could not create a transform of type {0}", + transformType.Name)); + } + else + { + transforms.Add(transformer); + } + }); + + return transforms; + } + + public static void ClearPipelineCache() + { + s_wrapTransformsMemento.Clear(); + s_unWrapTransformsMemento.Clear(); + } + + private IAmAMessageMapperAsync FindMessageMapper() where TRequest : class, IRequest + { + var messageMapper = _mapperRegistryAsync.GetAsync(); + if (messageMapper == null) throw new InvalidOperationException(string.Format("Could not find mapper for {0}", typeof(TRequest).Name)); + return messageMapper; + } + + private IOrderedEnumerable FindWrapTransforms(IAmAMessageMapperAsync messageMapper) where T : class, IRequest + { + var key = messageMapper.GetType().Name; + if (!s_wrapTransformsMemento.TryGetValue(key, out IOrderedEnumerable transformAttributes)) + { + transformAttributes = FindMapToMessage(messageMapper) + .GetOtherWrapsInPipeline() + .OrderByDescending(attribute => attribute.Step); + + s_wrapTransformsMemento.TryAdd(key, transformAttributes); + } + + return transformAttributes; + } + + private IOrderedEnumerable FindUnwrapTransforms(IAmAMessageMapperAsync messageMapper) where T : class, IRequest + { + var key = messageMapper.GetType().Name; + if (!s_unWrapTransformsMemento.TryGetValue(key, out IOrderedEnumerable transformAttributes)) + { + transformAttributes = FindMapToRequest(messageMapper) + .GetOtherUnwrapsInPipeline() + .OrderByDescending(attribute => attribute.Step); + + s_unWrapTransformsMemento.TryAdd(key, transformAttributes); + } + + return transformAttributes; + } + + + private MethodInfo FindMapToMessage(IAmAMessageMapperAsync messageMapper) where TRequest : class, IRequest + { + return FindMethods(messageMapper) + .Where(method => method.Name == nameof(IAmAMessageMapperAsync.MapToMessage)) + .SingleOrDefault(method => method.GetParameters().Length == 1 && method.GetParameters().Single().ParameterType == typeof(TRequest)); + } + + + private MethodInfo FindMapToRequest(IAmAMessageMapperAsync messageMapper) where TRequest : class, IRequest + { + return FindMethods(messageMapper) + .Where(method => method.Name == nameof(IAmAMessageMapperAsync.MapToRequest)) + .SingleOrDefault(method => method.GetParameters().Length == 1 && method.GetParameters().Single().ParameterType == typeof(Message)); + } + + private static MethodInfo[] FindMethods(IAmAMessageMapperAsync messageMapper) where TRequest : class, IRequest + { + return messageMapper.GetType().GetMethods(); + } + + private TransformPipelineTracer TraceWrapPipeline(WrapPipelineAsync pipeline) where TRequest : class, IRequest + { + var pipelineTracer = new TransformPipelineTracer(); + pipeline.DescribePath(pipelineTracer); + return pipelineTracer; + } + + private TransformPipelineTracer TraceUnwrapPipeline(UnwrapPipelineAsync pipeline) where TRequest : class, IRequest + { + var pipelineTracer = new TransformPipelineTracer(); + pipeline.DescribePath(pipelineTracer); + return pipelineTracer; + } + } +} diff --git a/src/Paramore.Brighter/TransformerFactory.cs b/src/Paramore.Brighter/TransformerFactory.cs index f49cdf9153..3b289799f8 100644 --- a/src/Paramore.Brighter/TransformerFactory.cs +++ b/src/Paramore.Brighter/TransformerFactory.cs @@ -38,7 +38,7 @@ public TransformerFactory(TransformAttribute attribute, IAmAMessageTransformerFa _messageType = typeof(TRequest); } - public IAmAMessageTransformAsync CreateMessageTransformer() + public IAmAMessageTransform CreateMessageTransformer() { var transformerType = _attribute.GetHandlerType(); var transformer = _factory.Create(transformerType); diff --git a/src/Paramore.Brighter/TransformerFactoryAsync.cs b/src/Paramore.Brighter/TransformerFactoryAsync.cs new file mode 100644 index 0000000000..1074bf6ea7 --- /dev/null +++ b/src/Paramore.Brighter/TransformerFactoryAsync.cs @@ -0,0 +1,50 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ +#endregion + +using System; + +namespace Paramore.Brighter +{ + internal class TransformerFactoryAsync where TRequest: class, IRequest + { + private readonly TransformAttribute _attribute; + private readonly IAmAMessageTransformerFactoryAsync _factory; + private readonly Type _messageType; + + public TransformerFactoryAsync(TransformAttribute attribute, IAmAMessageTransformerFactoryAsync factory) + { + _attribute = attribute; + _factory = factory; + _messageType = typeof(TRequest); + } + + public IAmAMessageTransformAsync CreateMessageTransformer() + { + var transformerType = _attribute.GetHandlerType(); + var transformer = _factory.Create(transformerType); + if (_attribute is WrapWithAttribute) transformer.InitializeWrapFromAttributeParams(_attribute.InitializerParams()); + if (_attribute is UnwrapWithAttribute) transformer.InitializeUnwrapFromAttributeParams(_attribute.InitializerParams()); + return transformer; + } + } +} diff --git a/src/Paramore.Brighter/UnwrapPipeline.cs b/src/Paramore.Brighter/UnwrapPipeline.cs index d9e6402773..c9a25b31c4 100644 --- a/src/Paramore.Brighter/UnwrapPipeline.cs +++ b/src/Paramore.Brighter/UnwrapPipeline.cs @@ -22,9 +22,6 @@ THE SOFTWARE. */ #endregion using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; using Paramore.Brighter.Extensions; namespace Paramore.Brighter @@ -44,7 +41,7 @@ public class UnwrapPipeline : TransformPipeline where TReque /// The factory used to create transforms /// The message mapper that forms the pipeline sink public UnwrapPipeline( - IEnumerable transforms, + IEnumerable transforms, IAmAMessageTransformerFactory messageTransformerFactory, IAmAMessageMapper messageMapper) { @@ -70,15 +67,14 @@ public void DescribePath(TransformPipelineTracer pipelineTracer) /// /// Transforms a into a - /// Applies any required to that + /// Applies any required to that /// /// The message to unwrap - /// The cancellation token /// a request - public async Task UnwrapAsync(Message message, CancellationToken cancellationToken = default) + public TRequest Unwrap(Message message) { var msg = message; - await Transforms.EachAsync(async transform => msg = await transform.UnwrapAsync(msg,cancellationToken)); + Transforms.Each(transform => msg = transform.Unwrap(msg)); return MessageMapper.MapToRequest(msg); } } diff --git a/src/Paramore.Brighter/UnwrapPipelineAsync.cs b/src/Paramore.Brighter/UnwrapPipelineAsync.cs new file mode 100644 index 0000000000..4b80383476 --- /dev/null +++ b/src/Paramore.Brighter/UnwrapPipelineAsync.cs @@ -0,0 +1,84 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ +#endregion + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Paramore.Brighter.Extensions; + +namespace Paramore.Brighter +{ + /// + /// class UnwrapPipeline + /// A pipeline with a sink of a that: + /// Takes a message and maps it to a request + /// Runs transforms on that message + /// + public class UnwrapPipelineAsync : TransformPipelineAsync where TRequest: class, IRequest + { + /// + /// Constructs an instance of an Unwrap pipeline + /// + /// The transforms that run before the mapper + /// The factory used to create transforms + /// The message mapper that forms the pipeline sink + public UnwrapPipelineAsync( + IEnumerable transforms, + IAmAMessageTransformerFactoryAsync messageTransformerFactory, + IAmAMessageMapperAsync messageMapperAsync) + { + MessageMapper = messageMapperAsync; + Transforms = transforms; + if (messageTransformerFactory != null) + { + InstanceScope = new TransformLifetimeScopeAsync(messageTransformerFactory); + Transforms.Each(transform => InstanceScope.Add(transform)); + } + } + + /// + /// Lists the unwrap pipeline: filter transforms and message mapper that will be executed + /// Used for pipeline verification + /// + /// + public void DescribePath(TransformPipelineTracer pipelineTracer) + { + Transforms.Each(t => pipelineTracer.AddTransform(t.GetType().Name)); + pipelineTracer.AddTransform(MessageMapper.GetType().Name); + } + + /// + /// Transforms a into a + /// Applies any required to that + /// + /// The message to unwrap + /// The cancellation token + /// a request + public async Task UnwrapAsync(Message message, CancellationToken cancellationToken = default) + { + var msg = message; + await Transforms.EachAsync(async transform => msg = await transform.UnwrapAsync(msg,cancellationToken)); + return await MessageMapper.MapToRequest(msg); + } + } +} diff --git a/src/Paramore.Brighter/WrapPipeline.cs b/src/Paramore.Brighter/WrapPipeline.cs index 8f37f3540c..a8e564b8e2 100644 --- a/src/Paramore.Brighter/WrapPipeline.cs +++ b/src/Paramore.Brighter/WrapPipeline.cs @@ -47,7 +47,7 @@ public class WrapPipeline : TransformPipeline where TRequest public WrapPipeline( IAmAMessageMapper messageMapper, IAmAMessageTransformerFactory messageTransformerFactory, - IEnumerable transforms) + IEnumerable transforms) { MessageMapper = messageMapper; Transforms = transforms; @@ -77,10 +77,10 @@ public void DescribePath(TransformPipelineTracer pipelineTracer) /// The request to wrap /// The cancellation token /// - public async Task WrapAsync(TRequest request, CancellationToken cancellationToken = default) + public Message Wrap(TRequest request) { var message = MessageMapper.MapToMessage(request); - await Transforms.EachAsync(async transform => message = await transform.WrapAsync(message, cancellationToken)); + Transforms.Each(transform => message = transform.Wrap(message)); return message; } } diff --git a/src/Paramore.Brighter/WrapPipelineAsync.cs b/src/Paramore.Brighter/WrapPipelineAsync.cs new file mode 100644 index 0000000000..7f3d437e11 --- /dev/null +++ b/src/Paramore.Brighter/WrapPipelineAsync.cs @@ -0,0 +1,87 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Paramore.Brighter.Extensions; + +namespace Paramore.Brighter +{ + /// + /// class WrapPipeline + /// A pipeline with a source of a that: + /// Takes a request and maps it to a message + /// Runs transforms on that message + /// + public class WrapPipelineAsync : TransformPipelineAsync where TRequest: class, IRequest + { + /// + /// Constructs an instance of a wrap pipeline + /// + /// The message mapper that forms the pipeline source + /// Factory for transforms, required to release + /// The transforms applied after the message mapper + public WrapPipelineAsync( + IAmAMessageMapperAsync messageMapperAsync, + IAmAMessageTransformerFactoryAsync messageTransformerFactoryAsync, + IEnumerable transforms) + { + MessageMapper = messageMapperAsync; + Transforms = transforms; + if (messageTransformerFactoryAsync != null) + { + InstanceScope = new TransformLifetimeScopeAsync(messageTransformerFactoryAsync); + Transforms.Each(transform => InstanceScope.Add(transform)); + } + + } + + /// + /// Lists the wrap pipeline: message mapper and filter transforms that will be executed + /// Used for pipeline verification + /// + /// + public void DescribePath(TransformPipelineTracer pipelineTracer) + { + pipelineTracer.AddTransform(MessageMapper.GetType().Name); + Transforms.Each(t => pipelineTracer.AddTransform(t.GetType().Name)); + } + + /// + /// Transforms a into a + /// Applies any required to that + /// + /// The request to wrap + /// The cancellation token + /// + public async Task WrapAsync(TRequest request, CancellationToken cancellationToken = default) + { + var message = await MessageMapper.MapToMessage(request); + await Transforms.EachAsync(async transform => message = await transform.WrapAsync(message, cancellationToken)); + return message; + } + } +} diff --git a/tests/Paramore.Brighter.AWS.Tests/MessagingGateway/When_throwing_defer_action_respect_redrive.cs b/tests/Paramore.Brighter.AWS.Tests/MessagingGateway/When_throwing_defer_action_respect_redrive.cs index fd2bab6732..463ffacb09 100644 --- a/tests/Paramore.Brighter.AWS.Tests/MessagingGateway/When_throwing_defer_action_respect_redrive.cs +++ b/tests/Paramore.Brighter.AWS.Tests/MessagingGateway/When_throwing_defer_action_respect_redrive.cs @@ -91,7 +91,8 @@ public SnsReDrivePolicySDlqTests() ); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyDeferredCommandMessageMapper(_topicName)) + new SimpleMessageMapperFactory(_ => new MyDeferredCommandMessageMapper(_topicName)), + null ); messageMapperRegistry.Register(); diff --git a/tests/Paramore.Brighter.AWS.Tests/Transformers/When_unwrapping_a_large_message.cs b/tests/Paramore.Brighter.AWS.Tests/Transformers/When_unwrapping_a_large_message.cs index dd1c30390d..15716537cc 100644 --- a/tests/Paramore.Brighter.AWS.Tests/Transformers/When_unwrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.AWS.Tests/Transformers/When_unwrapping_a_large_message.cs @@ -23,7 +23,7 @@ namespace Paramore.Brighter.AWS.Tests.Transformers [Trait("Fragile", "CI")] public class LargeMessagePaylodUnwrapTests : IDisposable { - private readonly TransformPipelineBuilder _pipelineBuilder; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; private readonly AmazonS3Client _client; private readonly string _bucketName; private readonly S3LuggageStore _luggageStore; @@ -33,10 +33,12 @@ public LargeMessagePaylodUnwrapTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper())) - { - { typeof(MyLargeCommand), typeof(MyLargeCommandMessageMapper) } - }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper()), + null + ); + + mapperRegistry.Register(); (AWSCredentials credentials, RegionEndpoint region) = CredentialsChain.GetAwsCredentials(); @@ -67,9 +69,9 @@ public LargeMessagePaylodUnwrapTests() .GetAwaiter() .GetResult(); - var messageTransformerFactory = new SimpleMessageTransformerFactory(_ => new ClaimCheckTransformer(_luggageStore)); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformer(_luggageStore)); - _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); } [Fact] diff --git a/tests/Paramore.Brighter.AWS.Tests/Transformers/When_wrapping_a_large_message.cs b/tests/Paramore.Brighter.AWS.Tests/Transformers/When_wrapping_a_large_message.cs index 5d1150c55d..363cd63ab2 100644 --- a/tests/Paramore.Brighter.AWS.Tests/Transformers/When_wrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.AWS.Tests/Transformers/When_wrapping_a_large_message.cs @@ -19,8 +19,8 @@ namespace Paramore.Brighter.AWS.Tests.Transformers { public class LargeMessagePayloadWrapTests : IDisposable { - private WrapPipeline _transformPipeline; - private readonly TransformPipelineBuilder _pipelineBuilder; + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; private readonly MyLargeCommand _myCommand; private readonly S3LuggageStore _luggageStore; private readonly AmazonS3Client _client; @@ -32,11 +32,12 @@ public LargeMessagePayloadWrapTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper())) - { - { typeof(MyLargeCommand), typeof(MyLargeCommandMessageMapper) } - }; - + var mapperRegistry = + new MessageMapperRegistry(new SimpleMessageMapperFactory( + _ => new MyLargeCommandMessageMapper()), + null); + mapperRegistry.Register(); + _myCommand = new MyLargeCommand(6000); (AWSCredentials credentials, RegionEndpoint region) = CredentialsChain.GetAwsCredentials(); @@ -68,9 +69,9 @@ public LargeMessagePayloadWrapTests() .GetAwaiter() .GetResult(); - var messageTransformerFactory = new SimpleMessageTransformerFactory(_ => new ClaimCheckTransformer(_luggageStore)); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformer(_luggageStore)); - _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); } [Fact] diff --git a/tests/Paramore.Brighter.Azure.Tests/Transformers/When_unwrapping_a_large_message.cs b/tests/Paramore.Brighter.Azure.Tests/Transformers/When_unwrapping_a_large_message.cs index c572062896..98e0202c9d 100644 --- a/tests/Paramore.Brighter.Azure.Tests/Transformers/When_unwrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.Azure.Tests/Transformers/When_unwrapping_a_large_message.cs @@ -18,7 +18,7 @@ public class LargeMessagePaylodUnwrapTests : IDisposable private readonly string _bucketName; private Uri _bucketUrl; private AzureBlobLuggageStore _luggageStore; - private readonly TransformPipelineBuilder _pipelineBuilder; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; public LargeMessagePaylodUnwrapTests() { @@ -32,15 +32,15 @@ public LargeMessagePaylodUnwrapTests() TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper())) - { - { typeof(MyLargeCommand), typeof(MyLargeCommandMessageMapper) } - }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper()), + null); + mapperRegistry.Register(); - var messageTransformerFactory = new SimpleMessageTransformerFactory(_ => new ClaimCheckTransformer(_luggageStore)); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformer(_luggageStore)); - _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); - } + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + } [Test] public async Task When_unwrapping_a_large_message() diff --git a/tests/Paramore.Brighter.Azure.Tests/Transformers/When_wrapping_a_large_message.cs b/tests/Paramore.Brighter.Azure.Tests/Transformers/When_wrapping_a_large_message.cs index 3ea0990b9e..81054d06b8 100644 --- a/tests/Paramore.Brighter.Azure.Tests/Transformers/When_wrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.Azure.Tests/Transformers/When_wrapping_a_large_message.cs @@ -12,8 +12,8 @@ namespace Paramore.Brighter.Azure.Tests.Transformers; [Property("Fragile", "CI")] public class LargeMessagePayloadWrapTests : IDisposable { - private WrapPipeline _transformPipeline; - private readonly TransformPipelineBuilder _pipelineBuilder; + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; private readonly MyLargeCommand _myCommand; private readonly AzureBlobLuggageStore _luggageStore; private readonly BlobContainerClient _client; @@ -26,10 +26,10 @@ public LargeMessagePayloadWrapTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper())) - { - { typeof(MyLargeCommand), typeof(MyLargeCommandMessageMapper) } - }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper()), + null); + mapperRegistry.Register(); _myCommand = new MyLargeCommand(6000); @@ -42,9 +42,9 @@ public LargeMessagePayloadWrapTests() _luggageStore = new AzureBlobLuggageStore(_bucketUrl, new AzureCliCredential()); - var messageTransformerFactory = new SimpleMessageTransformerFactory(_ => new ClaimCheckTransformer(_luggageStore)); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformer(_luggageStore)); - _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); _client.CreateIfNotExists(); } diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message.cs index 6c21b0080c..ada15e8dd4 100644 --- a/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message.cs @@ -13,7 +13,7 @@ namespace Paramore.Brighter.Core.Tests.Claims; public class LargeMessagePaylodUnwrapTests { - private readonly TransformPipelineBuilder _pipelineBuilder; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; private readonly InMemoryStorageProviderAsync _inMemoryStorageProviderAsync; public LargeMessagePaylodUnwrapTests() @@ -21,15 +21,15 @@ public LargeMessagePaylodUnwrapTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper())) - { - { typeof(MyLargeCommand), typeof(MyLargeCommandMessageMapper) } - }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper()), + null); + mapperRegistry.Register(); _inMemoryStorageProviderAsync = new InMemoryStorageProviderAsync(); - var messageTransformerFactory = new SimpleMessageTransformerFactory(_ => new ClaimCheckTransformer(_inMemoryStorageProviderAsync)); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformer(_inMemoryStorageProviderAsync)); - _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message.cs index 4a8899fa30..641179ef2b 100644 --- a/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message.cs @@ -10,8 +10,8 @@ namespace Paramore.Brighter.Core.Tests.Claims; public class LargeMessagePayloadWrapTests { - private WrapPipeline _transformPipeline; - private readonly TransformPipelineBuilder _pipelineBuilder; + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; private readonly MyLargeCommand _myCommand; private InMemoryStorageProviderAsync _inMemoryStorageProviderAsync; @@ -20,17 +20,18 @@ public LargeMessagePayloadWrapTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper())) - { - { typeof(MyLargeCommand), typeof(MyLargeCommandMessageMapper) } - }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyLargeCommandMessageMapper()), + null); + mapperRegistry.Register(); _myCommand = new MyLargeCommand(6000); _inMemoryStorageProviderAsync = new InMemoryStorageProviderAsync(); - var messageTransformerFactory = new SimpleMessageTransformerFactory(_ => new ClaimCheckTransformer(_inMemoryStorageProviderAsync)); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync( + _ => new ClaimCheckTransformer(_inMemoryStorageProviderAsync)); - _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Bulk_Clearing_The_PostBox_On_The_Command_Processor_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Bulk_Clearing_The_PostBox_On_The_Command_Processor_Async.cs index 69e48e8323..6a523903d0 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Bulk_Clearing_The_PostBox_On_The_Command_Processor_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Bulk_Clearing_The_PostBox_On_The_Command_Processor_Async.cs @@ -68,7 +68,9 @@ public CommandProcessorPostBoxBulkClearAsyncTests() new MessageBody(JsonSerializer.Serialize(myCommand2, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor.cs index a58d904612..1bf3cb42dc 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor.cs @@ -45,7 +45,7 @@ public CommandProcessorCallTests() return new MyResponseMessageMapper(); throw new ConfigurationException($"No mapper found for {type.Name}"); - })); + }), null); messageMapperRegistry.Register(); messageMapperRegistry.Register(); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_In_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_In_Mapper.cs index ccec9b94d0..73afb1cf54 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_In_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_In_Mapper.cs @@ -27,7 +27,7 @@ public CommandProcessorNoInMapperTests() return new MyRequestMessageMapper(); throw new ConfigurationException($"No mapper found for {type.Name}"); - })); + }), null); messageMapperRegistry.Register(); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Out_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Out_Mapper.cs index 28468c7316..c1d857ce9d 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Out_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Out_Mapper.cs @@ -27,7 +27,7 @@ public CommandProcessorMissingOutMapperTests() return new MyResponseMessageMapper(); throw new ConfigurationException($"No mapper found for {type.Name}"); - })); + }), null); messageMapperRegistry.Register(); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Timeout.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Timeout.cs index 7d20d2bbf7..96c1acb55b 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Timeout.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Timeout.cs @@ -29,7 +29,7 @@ public CommandProcessorCallTestsNoTimeout() return new MyResponseMessageMapper(); throw new ConfigurationException($"No mapper found for {type.Name}"); - })); + }), null); messageMapperRegistry.Register(); messageMapperRegistry.Register(); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor _Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor _Async.cs index 0cd5dd4193..812b049ad2 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor _Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor _Async.cs @@ -57,7 +57,9 @@ public CommandProcessorPostBoxClearAsyncTests() new MessageBody(JsonSerializer.Serialize(myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor.cs index b1c517e85a..0969c12bd2 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor.cs @@ -56,7 +56,9 @@ public CommandProcessorPostBoxClearTests() new MessageBody(JsonSerializer.Serialize(myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store.cs index 3536df608a..1b0c968a47 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store.cs @@ -36,7 +36,9 @@ public CommandProcessorDepositPostTests() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync.cs index 901823462f..1ff33b2112 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync.cs @@ -36,7 +36,9 @@ public CommandProcessorDepositPostTestsAsync() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs index 2afa8af506..81517f19ed 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs @@ -61,7 +61,7 @@ public CommandProcessorBulkDepositPostTestsAsync() { return new MyEventMessageMapper(); } - })); + }), null); messageMapperRegistry.Register(); messageMapperRegistry.Register(); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store_Bulk.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store_Bulk.cs index f65e0ad837..16e83d5825 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store_Bulk.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store_Bulk.cs @@ -58,7 +58,7 @@ public CommandProcessorBulkDepositPostTests() { return new MyEventMessageMapper(); } - })); + }), null); messageMapperRegistry.Register(); messageMapperRegistry.Register(); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor.cs index 759f68eb8f..6efa595ad5 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor.cs @@ -63,7 +63,9 @@ public CommandProcessorPostBoxImplicitClearTests() new MessageBody(JsonSerializer.Serialize(myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor_Async.cs index 5eca6bab12..dd688b8943 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor_Async.cs @@ -64,7 +64,9 @@ public CommandProcessorPostBoxImplicitClearAsyncTests() new MessageBody(JsonSerializer.Serialize(myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs index 5f60709b1c..84dc7dad5a 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs @@ -58,7 +58,9 @@ public CommandProcessorNoMessageMapperTests() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); var retryPolicy = Policy .Handle() diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs index 6876b34b8d..01d61f599e 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs @@ -59,7 +59,9 @@ public CommandProcessorNoMessageMapperAsyncTests() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options))); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); var retryPolicy = Policy .Handle() diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Producer.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Producer.cs index 17e6f465bd..107d2f45e3 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Producer.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Producer.cs @@ -59,7 +59,9 @@ public CommandProcessorPostMissingMessageProducerTests() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) ); - _messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + _messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); _messageMapperRegistry.Register(); _retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor.cs index e76a7d4081..84d79b7b23 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor.cs @@ -57,7 +57,9 @@ public CommandProcessorPostCommandTests() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor_Async.cs index 997ed30a2a..ebe70150b7 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor_Async.cs @@ -58,7 +58,9 @@ public CommandProcessorPostCommandAsyncTests() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Fails_Limit_Total_Writes_To_OutBox_In_Window.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Fails_Limit_Total_Writes_To_OutBox_In_Window.cs index c26278cf46..3a451fce0b 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Fails_Limit_Total_Writes_To_OutBox_In_Window.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Fails_Limit_Total_Writes_To_OutBox_In_Window.cs @@ -46,7 +46,9 @@ public PostFailureLimitCommandTests() _fakeMessageProducer = new FakeErroringMessageProducerSync(); var messageMapperRegistry = - new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var busConfiguration = new ExternalBusConfiguration { diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender.cs index bba1e3e26e..24c57feed0 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender.cs @@ -58,7 +58,9 @@ public ControlBusSenderPostMessageTests() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender_Async.cs index 91a049877c..5187edaf3a 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender_Async.cs @@ -59,7 +59,9 @@ public ControlBusSenderPostMessageAsyncTests() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_A_Default_Policy.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_A_Default_Policy.cs index 13014ca0d2..086d947f9d 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_A_Default_Policy.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_A_Default_Policy.cs @@ -56,7 +56,9 @@ public PostCommandTests() ); var messageMapperRegistry = - new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var busConfiguration = new ExternalBusConfiguration { diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store.cs index f581311bf7..91ec2bf798 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store.cs @@ -53,7 +53,9 @@ public CommandProcessorWithInMemoryOutboxTests() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store_Async.cs index 91a562c6b3..0d49815836 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store_Async.cs @@ -57,7 +57,9 @@ public CommandProcessorWithInMemoryOutboxAscyncTests() new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_command_should_retry_until_connection_re_established.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_command_should_retry_until_connection_re_established.cs index a02f1c0e50..6bb9c516f4 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_command_should_retry_until_connection_re_established.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_command_should_retry_until_connection_re_established.cs @@ -43,7 +43,8 @@ public MessagePumpRetryCommandOnConnectionFailureTests() _commandProcessor = new SpyCommandProcessor(); var channel = new FailingChannel { NumberOfRetries = 1 }; var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) { Channel = channel, TimeoutInMilliseconds = 500, RequeueCount = -1 }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_event_should_retry_until_connection_re_established.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_event_should_retry_until_connection_re_established.cs index 0ecb3631db..95953d2e29 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_event_should_retry_until_connection_re_established.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_event_should_retry_until_connection_re_established.cs @@ -43,7 +43,8 @@ public MessagePumpRetryEventConnectionFailureTests() _commandProcessor = new SpyCommandProcessor(); var channel = new FailingChannel { NumberOfRetries = 1 }; var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs index 3aacd01544..058d038e2c 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs @@ -45,7 +45,8 @@ public MessagePumpCommandProcessingDeferMessageActionTests() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs index 1458cfa4c2..f44fc9dc8c 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs @@ -45,7 +45,8 @@ public MessagePumpCommandProcessingDeferMessageActionTestsAsync() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpAsync(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs index 5dc77e7355..536a9a61e7 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs @@ -47,7 +47,8 @@ public MessagePumpCommandProcessingExceptionTests() _commandProcessor = new SpyExceptionCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs index 4a403bffcf..5f0d47e1f5 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs @@ -48,7 +48,8 @@ public MessagePumpCommandProcessingExceptionTestsAsync() _commandProcessor = new SpyExceptionCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpAsync(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_Is_asked_to_connect_a_channel_and_handler_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_Is_asked_to_connect_a_channel_and_handler_async.cs index 3c02612d0e..84d182b14a 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_Is_asked_to_connect_a_channel_and_handler_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_Is_asked_to_connect_a_channel_and_handler_async.cs @@ -23,7 +23,9 @@ public MessageDispatcherRoutingAsyncTests() _channel = new FakeChannel(); _commandProcessor = new SpyCommandProcessor(); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); var connection = new Subscription( diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_has_a_new_connection_added_while_running.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_has_a_new_connection_added_while_running.cs index b2c49bc292..122b04fb24 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_has_a_new_connection_added_while_running.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_has_a_new_connection_added_while_running.cs @@ -48,7 +48,9 @@ public DispatcherAddNewConnectionTests() _channel = new FakeChannel(); _commandProcessor = new SpyCommandProcessor(); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _subscription = new Subscription(new SubscriptionName("test"), noOfPerformers: 1, timeoutInMilliseconds: 1000, channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_is_asked_to_connect_a_channel_and_handler.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_is_asked_to_connect_a_channel_and_handler.cs index 9b216c3123..8f70bc97c4 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_is_asked_to_connect_a_channel_and_handler.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_is_asked_to_connect_a_channel_and_handler.cs @@ -46,7 +46,9 @@ public MessageDispatcherRoutingTests() _channel = new FakeChannel(); _commandProcessor = new SpyCommandProcessor(); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); var connection = new Subscription( diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection.cs index ca891e8d1d..a415264d54 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection.cs @@ -47,7 +47,9 @@ public MessageDispatcherResetConnection() _channel = new FakeChannel(); _commandProcessor = new SpyCommandProcessor(); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _subscription = new Subscription(new SubscriptionName("test"), noOfPerformers: 1, timeoutInMilliseconds: 1000, channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection_after_all_connections_have_stopped.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection_after_all_connections_have_stopped.cs index f0d9708b6a..e40490bafd 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection_after_all_connections_have_stopped.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection_after_all_connections_have_stopped.cs @@ -48,7 +48,9 @@ public DispatcherRestartConnectionTests() _channel = new FakeChannel(); _commandProcessor = new SpyCommandProcessor(); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _subscription = new Subscription(new SubscriptionName("test"), noOfPerformers: 1, timeoutInMilliseconds: 100, channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_shuts_a_connection.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_shuts_a_connection.cs index d3266aa24a..0652478595 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_shuts_a_connection.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_shuts_a_connection.cs @@ -45,7 +45,9 @@ public MessageDispatcherShutConnectionTests() var channel = new FakeChannel(); IAmACommandProcessor commandProcessor = new SpyCommandProcessor(); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _subscription = new Subscription(new SubscriptionName("test"), noOfPerformers: 3, timeoutInMilliseconds: 1000, channelFactory: new InMemoryChannelFactory(channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_different_types_of_performers.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_different_types_of_performers.cs index e21490874e..603da47151 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_different_types_of_performers.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_different_types_of_performers.cs @@ -55,7 +55,9 @@ public MessageDispatcherMultipleConnectionTests() container.AddTransient(); container.AddTransient(); - var messageMapperRegistry = new MessageMapperRegistry(new ServiceProviderMapperFactory(container.BuildServiceProvider())); + var messageMapperRegistry = new MessageMapperRegistry( + new ServiceProviderMapperFactory(container.BuildServiceProvider()), + null); messageMapperRegistry.Register(); messageMapperRegistry.Register(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_multiple_performers.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_multiple_performers.cs index 35df6b370e..6291d84d4c 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_multiple_performers.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_multiple_performers.cs @@ -46,7 +46,9 @@ public MessageDispatcherMultiplePerformerTests() _channel = new FakeChannel(); _commandProcessor = new SpyCommandProcessor(); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); var connection = new Subscription(new SubscriptionName("test"), noOfPerformers: 3, timeoutInMilliseconds: 100, channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request.cs index 9850dce1fd..110bb59301 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request.cs @@ -20,7 +20,8 @@ public MessagePumpFailingMessageTranslationTests() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new FailingEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new FailingEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached.cs index 5511904585..b7d645a020 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached.cs @@ -43,7 +43,8 @@ public MessagePumpUnacceptableMessageLimitTests() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new FailingEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new FailingEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler.cs index dd59cd064a..d1f9d5de8d 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler.cs @@ -58,7 +58,9 @@ public MessagePumpDispatchTests() var channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory( + _ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler_async.cs index 15a84508cd..2983a0f318 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler_async.cs @@ -31,7 +31,8 @@ public MessagePumpDispatchAsyncTests() var channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpAsync(commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_commands_has_been_reached.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_commands_has_been_reached.cs index 8aaf5c1003..d3dd5805a1 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_commands_has_been_reached.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_commands_has_been_reached.cs @@ -46,7 +46,8 @@ public MessagePumpCommandRequeueCountThresholdTests() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3 }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_events_has_been_reached.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_events_has_been_reached.cs index e4df53bbc0..809a0c5813 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_events_has_been_reached.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_events_has_been_reached.cs @@ -46,7 +46,8 @@ public MessagePumpEventRequeueCountThresholdTests() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_command_exception_is_thrown.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_command_exception_is_thrown.cs index 68192badbe..69abbdf27a 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_command_exception_is_thrown.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_command_exception_is_thrown.cs @@ -45,7 +45,8 @@ public MessagePumpCommandRequeueTests() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = -1 }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_event_exception_is_thrown.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_event_exception_is_thrown.cs index ba2c135c3c..5c925d5dd4 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_event_exception_is_thrown.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_event_exception_is_thrown.cs @@ -45,8 +45,8 @@ public MessagePumpEventRequeueTests() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()) - ); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs index 446549ba89..98ceae9cec 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs @@ -45,7 +45,8 @@ public MessagePumpEventProcessingDeferMessageActionTests() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs index 209a9fdb15..51db1ea751 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs @@ -45,7 +45,8 @@ public MessagePumpEventProcessingDeferMessageActionTestsAsync() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpAsync(_commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs index 896f7e4956..0f9faa52e5 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs @@ -47,7 +47,8 @@ public MessagePumpEventProcessingExceptionTests() _commandProcessor = new SpyExceptionCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs index 06c7cdecf1..293aaf3b48 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs @@ -47,7 +47,8 @@ public MessagePumpEventProcessingExceptionTestsAsync() _commandProcessor = new SpyExceptionCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpAsync(_commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs index e82d3490da..3953993835 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs @@ -45,7 +45,8 @@ public MessagePumpUnacceptableMessageTests() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs index 678c4e6d6e..c18c6a3576 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs @@ -44,7 +44,8 @@ public MessagePumpUnacceptableMessageLimitBreachedTests() _commandProcessor = new SpyRequeueCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor.cs index 3a75165465..329517214e 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor.cs @@ -45,7 +45,8 @@ public MessagePumpToCommandProcessorTests() _commandProcessor = new SpyCommandProcessor(); _channel = new FakeChannel(); var messagerMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messagerMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messagerMapperRegistry) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop.cs index bada00127a..887579db37 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop.cs @@ -46,7 +46,8 @@ public PerformerCanStopTests() _commandProcessor = new SpyCommandProcessor(); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper())); + new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); var messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyParameterizedTransform.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyParameterizedTransform.cs new file mode 100644 index 0000000000..3a2ebc0d2d --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyParameterizedTransform.cs @@ -0,0 +1,40 @@ +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; + +public class MyParameterizedTransform : IAmAMessageTransform +{ + public static readonly string HEADER_KEY = "MyParameterizedTransformTest"; + private string _template; + private string _displayFormat; + + + public void InitializeWrapFromAttributeParams(params object[] initializerList) + { + _template = (string)initializerList[0]; + } + + public void InitializeUnwrapFromAttributeParams(params object[] initializerList) + { + _displayFormat = (string)initializerList[0]; + } + + public Message Wrap(Message message) + { + message.Header.Bag.Add(HEADER_KEY, _template); + return message; + } + + public Message Unwrap(Message message) + { + var oldCommand = JsonSerializer.Deserialize(message.Body.Value); + oldCommand.Value = string.Format(_displayFormat, oldCommand.Value); + message.Body = new MessageBody(JsonSerializer.Serialize(oldCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))); + return message; + } + + public void Dispose() { } + +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyParameterizedTransformMessageMapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyParameterizedTransformMessageMapperAsync.cs new file mode 100644 index 0000000000..ef85561f76 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyParameterizedTransformMessageMapperAsync.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; + +public class MyParameterizedTransformMessageMapperAsync: IAmAMessageMapperAsync +{ + [MyParameterizedWrapWith(0, displayFormat: "I am a format indicator {0}" )] + public Task MapToMessage(MyTransformableCommand request) + { + var tcs = new TaskCompletionSource(); + tcs.SetResult(new Message( + new MessageHeader(request.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.General))) + )); + return tcs.Task; + } + + [MyParameterizedUnwrapWith(0, template: "I am a parameterized template: {0}")] + public Task MapToRequest(Message message) + { + var tcs = new TaskCompletionSource(); + tcs.SetResult(JsonSerializer.Deserialize(message.Body.Value)); + return tcs.Task; + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MySimpleTransform.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MySimpleTransform.cs new file mode 100644 index 0000000000..d671f27f1d --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MySimpleTransform.cs @@ -0,0 +1,25 @@ +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; + +public class MySimpleTransform : Transform +{ + public static readonly string HEADER_KEY = "MySimpleTransformTest"; + public static readonly string TRANSFORM_VALUE = "I am a transformed value"; + + public override Message Wrap(Message message) + { + message.Header.Bag.Add(HEADER_KEY, TRANSFORM_VALUE); + return message; + } + + public override Message Unwrap(Message message) + { + var oldCommand = JsonSerializer.Deserialize(message.Body.Value); + oldCommand.Value = message.Header.Bag[HEADER_KEY].ToString(); + message.Body = new MessageBody(JsonSerializer.Serialize(oldCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))); + return message; + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyTransformableCommandMessageMapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyTransformableCommandMessageMapperAsync.cs new file mode 100644 index 0000000000..4aefc2a6ef --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyTransformableCommandMessageMapperAsync.cs @@ -0,0 +1,27 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; + +public class MyTransformableCommandMessageMapperAsync : IAmAMessageMapperAsync +{ + [MySimpleWrapWith(0)] + public Task MapToMessage(MyTransformableCommand request) + { + var tcs = new TaskCompletionSource(); + tcs.SetResult(new Message( + new MessageHeader(request.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.General))) + )); + return tcs.Task; + } + + [MySimpleUnwrapWith(0)] + public Task MapToRequest(Message message) + { + var tcs = new TaskCompletionSource(); + tcs.SetResult(JsonSerializer.Deserialize(message.Body.Value)); + return tcs.Task; + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyVanillaCommandMessageMapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyVanillaCommandMessageMapperAsync.cs new file mode 100644 index 0000000000..0098769d05 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/MyVanillaCommandMessageMapperAsync.cs @@ -0,0 +1,25 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; + +public class MyVanillaCommandMessageMapperAsync : IAmAMessageMapperAsync +{ + public Task MapToMessage(MyTransformableCommand request) + { + var tcs = new TaskCompletionSource(); + tcs.SetResult(new Message( + new MessageHeader(request.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.General))) + )); + return tcs.Task; + } + + public Task MapToRequest(Message message) + { + var tcs = new TaskCompletionSource(); + tcs.SetResult(JsonSerializer.Deserialize(message.Body.Value)); + return tcs.Task; + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/Transform.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/Transform.cs new file mode 100644 index 0000000000..e0b2fad7c4 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/Test Doubles/Transform.cs @@ -0,0 +1,35 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; + +/// +/// Base class for transforms +/// For simple transforms that neither have initializer params or manage resources, this base class prevents the need to implement no-op interface methods +/// +public abstract class Transform : IAmAMessageTransform +{ + /// + /// Dispose cleans up unmanaged resources + /// This base class version is a no-op + /// + public void Dispose() { } + + /// + /// Initialize from Attributes allows attribute parameters to be passed to the type associated with the attribute + /// This base class version is a no-op + /// + /// + public void InitializeWrapFromAttributeParams(params object[] initializerList) { } + + /// + /// Initialize from Attributes allows attribute parameters to be passed to the type associated with the attribute + /// This base class version is a no-op + /// + /// + public void InitializeUnwrapFromAttributeParams(params object[] initializerList) { } + + public abstract Message Wrap(Message message); + + public abstract Message Unwrap(Message message); +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_Transform.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_Transform.cs index b453889c04..7640237110 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_Transform.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_Transform.cs @@ -14,11 +14,13 @@ public MessageUnwrapPathPipelineTests() { //arrange TransformPipelineBuilder.ClearPipelineCache(); - - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; - var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransformAsync())); + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), + null); + mapperRegistry.Register(); + + var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransform())); _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_TransformAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_TransformAsync.cs new file mode 100644 index 0000000000..cb36fbd3ca --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_TransformAsync.cs @@ -0,0 +1,45 @@ +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] + public class AsyncMessageUnwrapPathPipelineTests +{ + private UnwrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + + public AsyncMessageUnwrapPathPipelineTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); + mapperRegistry.Register(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + + } + + [Fact] + public void When_A_Message_Mapper_Map_To_Message_Has_A_Transform() + { + //act + _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); + + //assert + TraceFilters().ToString().Should().Be("MySimpleTransformAsync|MyTransformableCommandMessageMapper"); + } + + private TransformPipelineTracer TraceFilters() + { + var pipelineTracer = new TransformPipelineTracer(); + _transformPipeline.DescribePath(pipelineTracer); + return pipelineTracer; + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_Transform.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_Transform.cs index 45c9781bcb..33b386b00a 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_Transform.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_Transform.cs @@ -14,9 +14,11 @@ public MessageUnwrapPathNoTransformPipelineTests() { //arrange TransformPipelineBuilder.ClearPipelineCache(); - - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyVanillaCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyVanillaCommandMessageMapper) } }; + + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyVanillaCommandMessageMapper()), + null); + mapperRegistry.Register(); var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => null)); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_TransformAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_TransformAsync.cs new file mode 100644 index 0000000000..c721d69529 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_TransformAsync.cs @@ -0,0 +1,45 @@ +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] + public class AsyncMessageUnwrapPathNoTransformPipelineTests +{ + private UnwrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + + public AsyncMessageUnwrapPathNoTransformPipelineTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyVanillaCommandMessageMapperAsync())); + mapperRegistry.Register(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => null)); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + + } + + [Fact] + public void When_A_Message_Mapper_Map_To_Message_Has_No_Transform() + { + //act + _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); + + //assert + TraceFilters().ToString().Should().Be("MyVanillaCommandMessageMapper"); + } + + private TransformPipelineTracer TraceFilters() + { + var pipelineTracer = new TransformPipelineTracer(); + _transformPipeline.DescribePath(pipelineTracer); + return pipelineTracer; + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_Transform.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_Transform.cs index 8302a47895..16af4fd42c 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_Transform.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_Transform.cs @@ -14,11 +14,14 @@ public MessageWrapPathPipelineTests() { //arrange TransformPipelineBuilder.ClearPipelineCache(); - - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; - var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransformAsync())); + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), + null + ); + mapperRegistry.Register(); + + var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransform())); _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_TransformAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_TransformAsync.cs new file mode 100644 index 0000000000..fa42a0c83d --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_TransformAsync.cs @@ -0,0 +1,46 @@ +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] + public class AsyncMessageWrapPathPipelineTests +{ + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + + public AsyncMessageWrapPathPipelineTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), + null + ); + mapperRegistry.Register(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + + } + + [Fact] + public void When_A_Message_Mapper_Map_To_Request_Has_A_Transform() + { + //act + _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); + + //assert + TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapper|MySimpleTransformAsync"); + } + + private TransformPipelineTracer TraceFilters() + { + var pipelineTracer = new TransformPipelineTracer(); + _transformPipeline.DescribePath(pipelineTracer); + return pipelineTracer; + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_No_Transform.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_No_Transform.cs index a4a2e16fc4..e02ea2b936 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_No_Transform.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_No_Transform.cs @@ -14,9 +14,11 @@ public MessageWrapPathPipelineNoTransformTests() { //arrange TransformPipelineBuilder.ClearPipelineCache(); - - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyVanillaCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyVanillaCommandMessageMapper) } }; + + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyVanillaCommandMessageMapper()), + null); + mapperRegistry.Register(); var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => null)); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Pipeline_Builder_Without_A_Registry.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Pipeline_Builder_Without_A_Registry.cs index 12d5670745..07f7416dc9 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Pipeline_Builder_Without_A_Registry.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Pipeline_Builder_Without_A_Registry.cs @@ -14,7 +14,7 @@ public void When_Creating_A_Pipeline_Builder_Without_A_Registry() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransformAsync())); + var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransform())); //act var exception = Catch.Exception(() => new TransformPipelineBuilder(null, messageTransformerFactory)); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Pipeline_Builder_Without_A_Registry_Async.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Pipeline_Builder_Without_A_Registry_Async.cs new file mode 100644 index 0000000000..72646d4440 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Pipeline_Builder_Without_A_Registry_Async.cs @@ -0,0 +1,27 @@ +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] + public class AsyncTransformPipelineMissingRegistryTests +{ + [Fact] + public void When_Creating_A_Pipeline_Builder_Without_A_Registry() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); + + //act + var exception = Catch.Exception(() => new TransformPipelineBuilderAsync(null, messageTransformerFactory)); + + //assert + exception.Should().NotBeNull(); + exception.Should().BeOfType(); + + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory.cs index ec81d0b822..53c1f6159e 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory.cs @@ -16,10 +16,12 @@ public class TransformPipelineMissingFactoryWrapTests public TransformPipelineMissingFactoryWrapTests() { //arrange - TransformPipelineBuilder.ClearPipelineCache(); - - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), + null); + mapperRegistry.Register(); _myCommand = new MyTransformableCommand(); @@ -27,7 +29,7 @@ public TransformPipelineMissingFactoryWrapTests() } [Fact] - public async Task When_Creating_A_Wrap_Without_A_Factory() + public void When_Creating_A_Wrap_Without_A_Factory() { //act _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); @@ -36,7 +38,7 @@ public async Task When_Creating_A_Wrap_Without_A_Factory() TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapper"); //wrap should just do message mapper - var message = await _transformPipeline.WrapAsync(_myCommand); + var message = _transformPipeline.Wrap(_myCommand); //assert message.Body.Value.Should().Be(JsonSerializer.Serialize(_myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General)).ToString()); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory_Async.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory_Async.cs new file mode 100644 index 0000000000..ed2dd2a920 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory_Async.cs @@ -0,0 +1,57 @@ +using System.Text.Json; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] + public class AsyncTransformPipelineMissingFactoryWrapTests +{ + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private readonly MyTransformableCommand _myCommand; + + public AsyncTransformPipelineMissingFactoryWrapTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); + mapperRegistry.Register(); + + _myCommand = new MyTransformableCommand(); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, null); + } + + [Fact] + public async Task When_Creating_A_Wrap_Without_A_Factory() + { + //act + _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); + + // If no factory we default to just them mapper + TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapper"); + + //wrap should just do message mapper + var message = await _transformPipeline.WrapAsync(_myCommand); + + //assert + message.Body.Value.Should().Be(JsonSerializer.Serialize(_myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General)).ToString()); + + //we won't run a transform + message.Header.Bag.ContainsKey(MySimpleTransformAsync.HEADER_KEY).Should().Be(false); + } + + private TransformPipelineTracer TraceFilters() + { + var pipelineTracer = new TransformPipelineTracer(); + _transformPipeline.DescribePath(pipelineTracer); + return pipelineTracer; + } + +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory.cs index d7389255aa..5755acadb6 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory.cs @@ -19,9 +19,11 @@ public TransformPipelineMissingFactoryUnwrapTests() { //arrange TransformPipelineBuilder.ClearPipelineCache(); - - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; + + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), + null); + mapperRegistry.Register(); _myCommand = new MyTransformableCommand(); @@ -34,7 +36,7 @@ public TransformPipelineMissingFactoryUnwrapTests() } [Fact] - public async Task When_Creating_An_Unwrap_Without_A_Factory() + public void When_Creating_An_Unwrap_Without_A_Factory() { //act _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); @@ -43,7 +45,7 @@ public async Task When_Creating_An_Unwrap_Without_A_Factory() TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapper"); //wrap should just do message mapper - var request = await _transformPipeline.UnwrapAsync(_message); + var request = _transformPipeline.Unwrap(_message); //assert request.Value = _myCommand.Value; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory_Async.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory_Async.cs new file mode 100644 index 0000000000..e9389d07b1 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory_Async.cs @@ -0,0 +1,61 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] + public class AsyncTransformPipelineMissingFactoryUnwrapTests +{ + private UnwrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private readonly MyTransformableCommand _myCommand; + private readonly Message _message; + + public AsyncTransformPipelineMissingFactoryUnwrapTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); + mapperRegistry.Register(); + + _myCommand = new MyTransformableCommand(); + + _message = new Message( + new MessageHeader(_myCommand.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(_myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))) + ); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, null); + } + + [Fact] + public async Task When_Creating_An_Unwrap_Without_A_Factory() + { + //act + _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); + + // If no factory we default to just them mapper + TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapper"); + + //wrap should just do message mapper + var request = await _transformPipeline.UnwrapAsync(_message); + + //assert + request.Value = _myCommand.Value; + } + + private TransformPipelineTracer TraceFilters() + { + var pipelineTracer = new TransformPipelineTracer(); + _transformPipeline.DescribePath(pipelineTracer); + return pipelineTracer; + } + +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper.cs index f0fc01ab7d..13f7df4e0c 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper.cs @@ -17,13 +17,16 @@ public MessageUnwrapRequestTests() { //arrange TransformPipelineBuilder.ClearPipelineCache(); - - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; + + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), + null + ); + mapperRegistry.Register(); MyTransformableCommand myCommand = new(); - var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransformAsync())); + var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransform())); _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); @@ -36,11 +39,11 @@ public MessageUnwrapRequestTests() } [Fact] - public async Task When_Unwrapping_A_Message_Mapper() + public void When_Unwrapping_A_Message_Mapper() { //act _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); - var request = await _transformPipeline.UnwrapAsync(_message); + var request = _transformPipeline.Unwrap(_message); //assert request.Value = MySimpleTransformAsync.HEADER_KEY; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_MapperAsync.cs new file mode 100644 index 0000000000..edd86be3b0 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_MapperAsync.cs @@ -0,0 +1,51 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class AsyncMessageUnwrapRequestTests +{ + private UnwrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private readonly Message _message; + + public AsyncMessageUnwrapRequestTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync()) + ); + mapperRegistry.RegisterAsync(); + + MyTransformableCommand myCommand = new(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + + _message = new Message( + new MessageHeader(myCommand.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))) + ); + + _message.Header.Bag[MySimpleTransformAsync.HEADER_KEY] = MySimpleTransformAsync.TRANSFORM_VALUE; + } + + [Fact] + public async Task When_Unwrapping_A_Message_Mapper() + { + //act + _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); + var request = await _transformPipeline.UnwrapAsync(_message); + + //assert + request.Value = MySimpleTransformAsync.HEADER_KEY; + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper_But_Not_In_Transform_Factory.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper_But_Not_In_Transform_Factory.cs index a9624df20f..88e5ce7281 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper_But_Not_In_Transform_Factory.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper_But_Not_In_Transform_Factory.cs @@ -16,9 +16,11 @@ public MessageUnwrapRequestMissingTransformTests() { //arrange TransformPipelineBuilder.ClearPipelineCache(); - - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; + + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), + null); + mapperRegistry.Register(); MyTransformableCommand myCommand = new(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper_With_Parameters.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper_With_Parameters.cs index caba139499..153c17dde8 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper_With_Parameters.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper_With_Parameters.cs @@ -19,13 +19,15 @@ public MessageUnwrapRequestWithAttributesTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyParameterizedTransformMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyParameterizedTransformAsync) } }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyParameterizedTransformMessageMapper()), + null); + mapperRegistry.Register(); var myCommand = new MyTransformableCommand(); myCommand.Value = "Hello World"; - var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MyParameterizedTransformAsync())); + var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MyParameterizedTransform())); _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); @@ -35,11 +37,11 @@ public MessageUnwrapRequestWithAttributesTests() } [Fact] - public async Task When_Wrapping_A_Message_Mapper_With_Attributes() + public void When_Wrapping_A_Message_Mapper_With_Attributes() { //act _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); - var request = await _transformPipeline.UnwrapAsync(_message); + var request = _transformPipeline.Unwrap(_message); //assert request.Value.Should().Be("I am a parameterized template: Hello World"); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_Mapper.cs index e2e6b92d35..8b80e4c84e 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_Mapper.cs @@ -18,9 +18,11 @@ public VanillaMessageUnwrapRequestTests() { //arrange TransformPipelineBuilder.ClearPipelineCache(); - - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyVanillaCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyVanillaCommandMessageMapper) } }; + + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyVanillaCommandMessageMapper()), + null); + mapperRegistry.Register(); _myCommand = new MyTransformableCommand(); @@ -37,11 +39,11 @@ public VanillaMessageUnwrapRequestTests() } [Fact] - public async Task When_Unwrapping_A_Vanilla_Message_Mapper() + public void When_Unwrapping_A_Vanilla_Message_Mapper() { //act _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); - var request = await _transformPipeline.UnwrapAsync(_message); + var request = _transformPipeline.Unwrap(_message); //assert request.Value = _myCommand.Value; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_MapperAsync.cs new file mode 100644 index 0000000000..c107ee49f9 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_MapperAsync.cs @@ -0,0 +1,51 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class AsyncVanillaMessageUnwrapRequestTests +{ + private UnwrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private readonly MyTransformableCommand _myCommand; + private readonly Message _message; + + public AsyncVanillaMessageUnwrapRequestTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyVanillaCommandMessageMapperAsync())); + mapperRegistry.Register(); + + _myCommand = new MyTransformableCommand(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => null)); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + + _message = new Message( + new MessageHeader(_myCommand.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(_myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))) + ); + + _message.Header.Bag[MySimpleTransformAsync.HEADER_KEY] = MySimpleTransformAsync.TRANSFORM_VALUE; + } + + [Fact] + public async Task When_Unwrapping_A_Vanilla_Message_Mapper() + { + //act + _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); + var request = await _transformPipeline.UnwrapAsync(_message); + + //assert + request.Value = _myCommand.Value; + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_Mapper.cs index 43882a8f11..95ba857250 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_Mapper.cs @@ -18,12 +18,14 @@ public MessageUnwrapRequestMissingMapperTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => null)) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => null), + null); + mapperRegistry.Register(); MyTransformableCommand myCommand = new(); - var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransformAsync())); + var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransform())); _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_MapperAsync.cs new file mode 100644 index 0000000000..6f3de4b73a --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_MapperAsync.cs @@ -0,0 +1,49 @@ +using System; +using System.Text.Json; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class AsyncMessageUnwrapRequestMissingMapperTests +{ + private UnwrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + + public AsyncMessageUnwrapRequestMissingMapperTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => null)); + mapperRegistry.RegisterAsync(); + + MyTransformableCommand myCommand = new(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + + Message message = new( + new MessageHeader(myCommand.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))) + ); + + message.Header.Bag[MySimpleTransformAsync.HEADER_KEY] = MySimpleTransformAsync.TRANSFORM_VALUE; + } + + [Fact] + public void When_Wrapping_But_No_Registered_Mapper() + { + //act + var exception = Catch.Exception(() => _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline()); + exception.Should().NotBeNull(); + exception.Should().BeOfType(); + exception.InnerException.Should().BeOfType(); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper.cs index 633e400807..31cc52dab4 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper.cs @@ -18,22 +18,24 @@ public MessageWrapRequestTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), + null); + mapperRegistry.Register(); _myCommand = new MyTransformableCommand(); - var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransformAsync())); + var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransform())); _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); } [Fact] - public async Task When_Wrapping_A_Message_Mapper() + public void When_Wrapping_A_Message_Mapper() { //act _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); - var message = await _transformPipeline.WrapAsync(_myCommand); + var message = _transformPipeline.Wrap(_myCommand); //assert message.Body.Value.Should().Be(JsonSerializer.Serialize(_myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General)).ToString()); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_MapperAsync.cs new file mode 100644 index 0000000000..69f12d477b --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_MapperAsync.cs @@ -0,0 +1,44 @@ +using System.Text.Json; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class AsyncMessageWrapRequestTests +{ + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private readonly MyTransformableCommand _myCommand; + + public AsyncMessageWrapRequestTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); + mapperRegistry.Register(); + + _myCommand = new MyTransformableCommand(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + } + + [Fact] + public async Task When_Wrapping_A_Message_Mapper() + { + //act + _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); + var message = await _transformPipeline.WrapAsync(_myCommand); + + //assert + message.Body.Value.Should().Be(JsonSerializer.Serialize(_myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General)).ToString()); + message.Header.Bag[MySimpleTransformAsync.HEADER_KEY].Should().Be(MySimpleTransformAsync.TRANSFORM_VALUE); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_But_Not_In_Transform_Factory.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_But_Not_In_Transform_Factory.cs index 8b89f3aa75..654732696d 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_But_Not_In_Transform_Factory.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_But_Not_In_Transform_Factory.cs @@ -15,8 +15,10 @@ public MessageWrapRequestMissingTransformTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), + null); + mapperRegistry.Register(); var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => null)); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_But_Not_In_Transform_Factory_Async.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_But_Not_In_Transform_Factory_Async.cs new file mode 100644 index 0000000000..55c7b89f11 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_But_Not_In_Transform_Factory_Async.cs @@ -0,0 +1,37 @@ +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class AsyncMessageWrapRequestMissingTransformTests +{ + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + + public AsyncMessageWrapRequestMissingTransformTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); + mapperRegistry.Register(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => null)); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + } + + [Fact] + public void When_Wrapping_A_Message_Mapper_But_Not_In_Transform_Factory() + { + //act + var exception = Catch.Exception(() => _pipelineBuilder.BuildWrapPipeline()); + exception.Should().NotBeNull(); + exception.Should().BeOfType(); + + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_With_Parameters.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_With_Parameters.cs index 3c17112420..f27714774a 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_With_Parameters.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_With_Parameters.cs @@ -18,22 +18,25 @@ public MessageWrapRequestWithAttributesTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyParameterizedTransformMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyParameterizedTransformAsync) } }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory( + _ => new MyParameterizedTransformMessageMapper()), + null); + mapperRegistry.Register(); _myCommand = new MyTransformableCommand(); - var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MyParameterizedTransformAsync())); + var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MyParameterizedTransform())); _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); } [Fact] - public async Task When_Wrapping_A_Message_Mapper_With_Attributes() + public void When_Wrapping_A_Message_Mapper_With_Attributes() { //act _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); - var message = await _transformPipeline.WrapAsync(_myCommand); + var message = _transformPipeline.Wrap(_myCommand); //assert message.Body.Value.Should().Be(JsonSerializer.Serialize(_myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General)).ToString()); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_With_Parameters_Async.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_With_Parameters_Async.cs new file mode 100644 index 0000000000..f8d7569b88 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_Mapper_With_Parameters_Async.cs @@ -0,0 +1,45 @@ +using System.Text.Json; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class AsyncMessageWrapRequestWithAttributesTests +{ + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private readonly MyTransformableCommand _myCommand; + + public AsyncMessageWrapRequestWithAttributesTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyParameterizedTransformMessageMapperAsync()) + ); + mapperRegistry.RegisterAsync(); + + _myCommand = new MyTransformableCommand(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MyParameterizedTransformAsync())); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + } + + [Fact] + public async Task When_Wrapping_A_Message_Mapper_With_Attributes() + { + //act + _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); + var message = await _transformPipeline.WrapAsync(_myCommand); + + //assert + message.Body.Value.Should().Be(JsonSerializer.Serialize(_myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General)).ToString()); + message.Header.Bag[MyParameterizedTransformAsync.HEADER_KEY].Should().Be("I am a format indicator {0}"); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_Mapper.cs index b835a613e0..0f9d1b8d52 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_Mapper.cs @@ -18,8 +18,10 @@ public VanillaMessageWrapRequestTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyVanillaCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyVanillaCommandMessageMapper) } }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyVanillaCommandMessageMapper()), + null); + mapperRegistry.Register(); _myCommand = new MyTransformableCommand(); @@ -29,11 +31,11 @@ public VanillaMessageWrapRequestTests() } [Fact] - public async Task When_Wrapping_A_Vanilla_Message_Mapper() + public void When_Wrapping_A_Vanilla_Message_Mapper() { //act _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); - var message = await _transformPipeline.WrapAsync(_myCommand); + var message = _transformPipeline.Wrap(_myCommand); //assert message.Body.Value.Should().Be(JsonSerializer.Serialize(_myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General)).ToString()); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_MapperAsync.cs new file mode 100644 index 0000000000..e95922f538 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_MapperAsync.cs @@ -0,0 +1,43 @@ +using System.Text.Json; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class AsyncVanillaMessageWrapRequestTests +{ + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private readonly MyTransformableCommand _myCommand; + + public AsyncVanillaMessageWrapRequestTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyVanillaCommandMessageMapperAsync())); + mapperRegistry.Register(); + + _myCommand = new MyTransformableCommand(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => null)); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + } + + [Fact] + public async Task When_Wrapping_A_Vanilla_Message_Mapper() + { + //act + _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); + var message = await _transformPipeline.WrapAsync(_myCommand); + + //assert + message.Body.Value.Should().Be(JsonSerializer.Serialize(_myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General)).ToString()); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_But_No_Registered_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_But_No_Registered_Mapper.cs index 662f2501b3..e9629ef985 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_But_No_Registered_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_But_No_Registered_Mapper.cs @@ -9,20 +9,22 @@ namespace Paramore.Brighter.Core.Tests.MessageSerialisation; [Collection("CommandProcessor")] public class MessageWrapRequestMissingMapperTests { - private WrapPipeline _transformPipeline; - private readonly TransformPipelineBuilder _pipelineBuilder; + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; public MessageWrapRequestMissingMapperTests() { //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => null)) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => null), + null); + mapperRegistry.Register(); - var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransformAsync())); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); - _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_But_No_Registered_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_But_No_Registered_MapperAsync.cs new file mode 100644 index 0000000000..c83973617c --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_But_No_Registered_MapperAsync.cs @@ -0,0 +1,39 @@ +using System; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class AsyncMessageWrapRequestMissingMapperTests +{ + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + + public AsyncMessageWrapRequestMissingMapperTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => null), + null); + mapperRegistry.Register(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + } + + [Fact] + public void When_Wrapping_But_No_Registered_Mapper() + { + //act + var exception = Catch.Exception(() => _transformPipeline = _pipelineBuilder.BuildWrapPipeline()); + exception.Should().NotBeNull(); + exception.Should().BeOfType(); + exception.InnerException.Should().BeOfType(); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_Pipeline.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_Pipeline.cs index e392812010..1d9edaffef 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_Pipeline.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_Pipeline.cs @@ -20,8 +20,10 @@ public MessageWrapCleanupTests() //arrange TransformPipelineBuilder.ClearPipelineCache(); - var mapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper())) - { { typeof(MyTransformableCommand), typeof(MyTransformableCommandMessageMapper) } }; + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), + null); + mapperRegistry.Register(); _myCommand = new MyTransformableCommand(); @@ -29,11 +31,11 @@ public MessageWrapCleanupTests() } [Fact] - public async Task When_Wrapping_Clean_Up_The_Pipeline() + public void When_Wrapping_Clean_Up_The_Pipeline() { //act _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); - var message = await _transformPipeline.WrapAsync(_myCommand); + var message = _transformPipeline.Wrap(_myCommand); _transformPipeline.Dispose(); //assert @@ -43,12 +45,12 @@ public async Task When_Wrapping_Clean_Up_The_Pipeline() private class MyReleaseTrackingTransformFactory : IAmAMessageTransformerFactory { - public IAmAMessageTransformAsync Create(Type transformerType) + public IAmAMessageTransform Create(Type transformerType) { - return new MySimpleTransformAsync(); + return new MySimpleTransform(); } - public void Release(IAmAMessageTransformAsync transformer) + public void Release(IAmAMessageTransform transformer) { var disposable = transformer as IDisposable; disposable?.Dispose(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_PipelineAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_PipelineAsync.cs new file mode 100644 index 0000000000..2e75c645c3 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_PipelineAsync.cs @@ -0,0 +1,62 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class AsyncMessageWrapCleanupTests +{ + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private readonly MyTransformableCommand _myCommand; + public static string s_released; + + public AsyncMessageWrapCleanupTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); + mapperRegistry.Register(); + + _myCommand = new MyTransformableCommand(); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, new MyReleaseTrackingTransformFactoryAsync()); + } + + [Fact] + public async Task When_Wrapping_Clean_Up_The_Pipeline() + { + //act + _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); + var message = await _transformPipeline.WrapAsync(_myCommand); + _transformPipeline.Dispose(); + + //assert + s_released.Should().Be("|MySimpleTransformAsync"); + + } + + private class MyReleaseTrackingTransformFactoryAsync : IAmAMessageTransformerFactoryAsync + { + public IAmAMessageTransformAsync Create(Type transformerType) + { + return new MySimpleTransformAsync(); + } + + public void Release(IAmAMessageTransformAsync transformer) + { + var disposable = transformer as IDisposable; + disposable?.Dispose(); + + s_released += "|" + transformer.GetType().Name; + } + } + +} diff --git a/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_A_Span_Is_Exported.cs b/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_A_Span_Is_Exported.cs index 711a9895df..4ace9a6a39 100644 --- a/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_A_Span_Is_Exported.cs +++ b/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_A_Span_Is_Exported.cs @@ -30,7 +30,9 @@ public ImplicitClearingObservabilityTests() var registry = new SubscriberRegistry(); registry.Register(); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); var container = new ServiceCollection(); diff --git a/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_async_A_Span_Is_Exported.cs b/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_async_A_Span_Is_Exported.cs index 15ea9d7596..eff3cbd9c5 100644 --- a/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_async_A_Span_Is_Exported.cs +++ b/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_async_A_Span_Is_Exported.cs @@ -31,7 +31,9 @@ public ImplicitClearingAsyncObservabilityTests() var registry = new SubscriberRegistry(); registry.Register(); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); var container = new ServiceCollection(); diff --git a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs index 9da04298f3..bf31538cf6 100644 --- a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs +++ b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs @@ -44,7 +44,9 @@ public class DispatchBuilderTests : IDisposable public DispatchBuilderTests() { - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null); messageMapperRegistry.Register(); var retryPolicy = Policy diff --git a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs index 3e001bb09b..e4cce5a681 100644 --- a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs +++ b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs @@ -43,7 +43,10 @@ public class DispatchBuilderWithNamedGateway : IDisposable public DispatchBuilderWithNamedGateway() { - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((_) => new MyEventMessageMapper())); + var messageMapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), + null + ); messageMapperRegistry.Register(); var policyRegistry = new PolicyRegistry { diff --git a/tests/Paramore.Brighter.RMQ.Tests/MessagingGateway/When_retry_limits_force_a_message_onto_the_DLQ.cs b/tests/Paramore.Brighter.RMQ.Tests/MessagingGateway/When_retry_limits_force_a_message_onto_the_DLQ.cs index 83346ecaca..880cd9719d 100644 --- a/tests/Paramore.Brighter.RMQ.Tests/MessagingGateway/When_retry_limits_force_a_message_onto_the_DLQ.cs +++ b/tests/Paramore.Brighter.RMQ.Tests/MessagingGateway/When_retry_limits_force_a_message_onto_the_DLQ.cs @@ -87,7 +87,8 @@ public RMQMessageConsumerRetryDLQTests() //pump messages from a channel to a handler - in essence we are building our own dispatcher in this test var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyDeferredCommandMessageMapper(_topicName))); + new SimpleMessageMapperFactory(_ => new MyDeferredCommandMessageMapper(_topicName)), + null); messageMapperRegistry.Register(); _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) From a53c8a89484c86bf7fb0bf27ea5661779745de37 Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Fri, 1 Dec 2023 21:30:44 +0000 Subject: [PATCH 02/15] Builds with async pipelines --- src/Paramore.Brighter/CommandProcessor.cs | 71 ++++++++++++++++--- .../TransformPipelineBuilder.cs | 5 ++ .../TransformPipelineBuilderAsync.cs | 5 ++ ..._PostBox_On_The_Command_Processor_Async.cs | 4 +- ...ling_A_Server_Via_The_Command_Processor.cs | 2 +- ...The_Command_Processor_With_No_In_Mapper.cs | 2 +- ...he_Command_Processor_With_No_Out_Mapper.cs | 2 +- ...a_The_Command_Processor_With_No_Timeout.cs | 2 +- ...PostBox_On_The_Command_Processor _Async.cs | 5 +- ...ng_The_PostBox_On_The_Command_Processor.cs | 5 +- ...positing_A_Message_In_The_Message_Store.cs | 5 +- ...ing_A_Message_In_The_Message_StoreAsync.cs | 5 +- ..._Message_In_The_Message_StoreAsync_Bulk.cs | 5 +- ...ing_A_Message_In_The_Message_Store_Bulk.cs | 4 +- ...ng_The_PostBox_On_The_Command_Processor.cs | 5 +- ..._PostBox_On_The_Command_Processor_Async.cs | 4 +- ...And_There_Is_No_Message_Mapper_Registry.cs | 5 +- ...ere_Is_No_Message_Mapper_Registry_Async.cs | 4 +- ...ting_A_Message_To_The_Command_Processor.cs | 4 +- ..._Message_To_The_Command_Processor_Async.cs | 4 +- .../When_Posting_Via_A_Control_Bus_Sender.cs | 4 +- ..._Posting_Via_A_Control_Bus_Sender_Async.cs | 5 +- ...Posting_With_An_In_Memory_Message_Store.cs | 4 +- ...g_With_An_In_Memory_Message_Store_Async.cs | 5 +- ...Then_message_is_requeued_until_rejected.cs | 3 +- ...message_is_requeued_until_rejectedAsync.cs | 3 +- ...handled_exception_Then_message_is_acked.cs | 3 +- ...d_exception_Then_message_is_acked_async.cs | 3 +- ...Then_message_is_requeued_until_rejected.cs | 2 +- ...message_is_requeued_until_rejectedAsync.cs | 3 +- ...handled_exception_Then_message_is_acked.cs | 2 +- ...d_exception_Then_message_is_acked_async.cs | 3 +- ..._Clearing_The_Outbox_A_Span_Is_Exported.cs | 4 +- ...ing_The_Outbox_async_A_Span_Is_Exported.cs | 4 +- 34 files changed, 120 insertions(+), 76 deletions(-) diff --git a/src/Paramore.Brighter/CommandProcessor.cs b/src/Paramore.Brighter/CommandProcessor.cs index 141ecd3ec5..705e1707a4 100644 --- a/src/Paramore.Brighter/CommandProcessor.cs +++ b/src/Paramore.Brighter/CommandProcessor.cs @@ -58,6 +58,7 @@ public class CommandProcessor : IAmACommandProcessor private readonly IAmAFeatureSwitchRegistry _featureSwitchRegistry; private readonly IEnumerable _replySubscriptions; private readonly TransformPipelineBuilder _transformPipelineBuilder; + private readonly TransformPipelineBuilderAsync _transformPipelineBuilderAsync; //Uses -1 to indicate no outbox and will thus force a throw on a failed publish @@ -146,11 +147,13 @@ public CommandProcessor( /// The handler factory. /// The request context factory. /// The policy registry. - /// The mapper registry. /// The external service bus that we want to send messages over + /// The mapper registry. + /// The async mapper registry /// The feature switch config provider. /// Do we want to insert an inbox handler into pipelines without the attribute. Null (default = no), yes = how to configure /// The factory used to create a transformer pipeline for a message mapper + /// The factory used to create a transformer pipeline for an async message mapper /// The Subscriptions for creating the reply queues /// If we are expecting a response, then we need a channel to listen on public CommandProcessor( @@ -158,11 +161,13 @@ public CommandProcessor( IAmAHandlerFactory handlerFactory, IAmARequestContextFactory requestContextFactory, IPolicyRegistry policyRegistry, - IAmAMessageMapperRegistry mapperRegistry, IAmAnExternalBusService bus, + IAmAMessageMapperRegistry mapperRegistry = null, + IAmAMessageMapperRegistryAsync mapperRegistryAsync = null, IAmAFeatureSwitchRegistry featureSwitchRegistry = null, InboxConfiguration inboxConfiguration = null, IAmAMessageTransformerFactory messageTransformerFactory = null, + IAmAMessageTransformerFactoryAsync messageTransformerFactoryAsync = null, IEnumerable replySubscriptions = null, IAmAChannelFactory responseChannelFactory = null ) @@ -170,7 +175,11 @@ public CommandProcessor( { _responseChannelFactory = responseChannelFactory; _replySubscriptions = replySubscriptions; - _transformPipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + + if (mapperRegistry == null && mapperRegistryAsync == null) + throw new ConfigurationException("A Command Processor with an external bus must have a message mapper registry"); + if (mapperRegistry != null) _transformPipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + if (mapperRegistryAsync != null) _transformPipelineBuilderAsync = new TransformPipelineBuilderAsync(mapperRegistryAsync, messageTransformerFactoryAsync); InitExtServiceBus(bus); } @@ -183,26 +192,34 @@ public CommandProcessor( /// The policy registry. /// The mapper registry. /// The external service bus that we want to send messages over + /// The mapper registry. /// The feature switch config provider. /// Do we want to insert an inbox handler into pipelines without the attribute. Null (default = no), yes = how to configure /// The factory used to create a transformer pipeline for a message mapper + /// The factory used to create a transformer pipeline for a message mapper< /// The Subscriptions for creating the reply queues public CommandProcessor( IAmARequestContextFactory requestContextFactory, IPolicyRegistry policyRegistry, - IAmAMessageMapperRegistry mapperRegistry, IAmAnExternalBusService bus, + IAmAMessageMapperRegistry mapperRegistry = null, + IAmAMessageMapperRegistryAsync mapperRegistryAsync = null, IAmAFeatureSwitchRegistry featureSwitchRegistry = null, InboxConfiguration inboxConfiguration = null, IAmAMessageTransformerFactory messageTransformerFactory = null, + IAmAMessageTransformerFactoryAsync messageTransformerFactoryAsync = null, IEnumerable replySubscriptions = null) { _requestContextFactory = requestContextFactory; _policyRegistry = policyRegistry; _featureSwitchRegistry = featureSwitchRegistry; _inboxConfiguration = inboxConfiguration; - _transformPipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); _replySubscriptions = replySubscriptions; + + if (mapperRegistry == null && mapperRegistryAsync == null) + throw new ConfigurationException("A Command Processor with an external bus must have a message mapper registry"); + if (mapperRegistry != null) _transformPipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + if (mapperRegistryAsync != null) _transformPipelineBuilderAsync = new TransformPipelineBuilderAsync(mapperRegistryAsync, messageTransformerFactoryAsync); InitExtServiceBus(bus); } @@ -509,14 +526,29 @@ IAmABoxTransactionProvider transactionProvider if (!bus.HasOutbox()) throw new InvalidOperationException("No outbox defined."); - var message = _transformPipelineBuilder.BuildWrapPipeline().WrapAsync(request).GetAwaiter().GetResult(); + Message message; + if (_transformPipelineBuilder.HasPipeline()) + { + message = _transformPipelineBuilder.BuildWrapPipeline().Wrap(request); + + } + else if (_transformPipelineBuilderAsync.HasPipeline()) + { + message = _transformPipelineBuilderAsync.BuildWrapPipeline().WrapAsync(request) + .GetAwaiter() + .GetResult(); + } + else + { + throw new InvalidOperationException("No message mapper defined for request"); + } AddTelemetryToMessage(message); bus.AddToOutbox(request, message, transactionProvider); return message.Id; - } + } /// /// Adds a messages into the outbox, and returns the id of the saved message. @@ -629,7 +661,24 @@ public async Task DepositPostAsync( if (!bus.HasAsyncOutbox()) throw new InvalidOperationException("No async outbox defined."); - var message = await _transformPipelineBuilder.BuildWrapPipeline().WrapAsync(request, cancellationToken); + Message message; + if (_transformPipelineBuilderAsync.HasPipeline()) + { + message = await _transformPipelineBuilderAsync + .BuildWrapPipeline() + .WrapAsync(request, cancellationToken); + } + else if (_transformPipelineBuilder.HasPipeline()) + { + message = _transformPipelineBuilder + .BuildWrapPipeline() + .Wrap(request); + + } + else + { + throw new InvalidOperationException("No message mapper defined for request"); + } AddTelemetryToMessage(message); @@ -807,7 +856,7 @@ public TResponse Call(T request, int timeOutInMilliseconds) //the channel to create the subscription, but this does not do much on a new queue _bus.Retry(() => responseChannel.Purge()); - var outMessage = outWrapPipeline.WrapAsync(request).GetAwaiter().GetResult(); + var outMessage = outWrapPipeline.Wrap(request); //We don't store the message, if we continue to fail further retry is left to the sender //s_logger.LogDebug("Sending request with routingkey {0}", routingKey); @@ -886,7 +935,7 @@ private List BulkMapMessages(IEnumerable requests) where T return requests.Select(r => { var wrapPipeline = _transformPipelineBuilder.BuildWrapPipeline(); - var message = wrapPipeline.WrapAsync((T)r).GetAwaiter().GetResult(); + var message = wrapPipeline.Wrap((T)r); AddTelemetryToMessage(message); return message; }).ToList(); @@ -898,7 +947,7 @@ private async Task> BulkMapMessagesAsync(IEnumerable var messages = new List(); foreach (var request in requests) { - var wrapPipeline = _transformPipelineBuilder.BuildWrapPipeline(); + var wrapPipeline = _transformPipelineBuilderAsync.BuildWrapPipeline(); var message = await wrapPipeline.WrapAsync((T)request, cancellationToken); AddTelemetryToMessage(message); messages.Add(message); diff --git a/src/Paramore.Brighter/TransformPipelineBuilder.cs b/src/Paramore.Brighter/TransformPipelineBuilder.cs index d10f5a973c..8ddba0ee82 100644 --- a/src/Paramore.Brighter/TransformPipelineBuilder.cs +++ b/src/Paramore.Brighter/TransformPipelineBuilder.cs @@ -147,6 +147,11 @@ public UnwrapPipeline BuildUnwrapPipeline() where TRequest : } } + public bool HasPipeline() where TRequest : class, IRequest + { + return _mapperRegistry.Get() != null; + } + private IEnumerable BuildTransformPipeline(IEnumerable transformAttributes) where TRequest : class, IRequest { diff --git a/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs b/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs index 5754f0f24f..cf10530d63 100644 --- a/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs +++ b/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs @@ -146,6 +146,11 @@ public UnwrapPipelineAsync BuildUnwrapPipeline() where TRequ throw new ConfigurationException("Error building unwrap pipeline for outgoing message, see inner exception for details", e); } } + + public bool HasPipeline() where TRequest : class, IRequest + { + return _mapperRegistryAsync.GetAsync() != null; + } private IEnumerable BuildTransformPipeline(IEnumerable transformAttributes) where TRequest : class, IRequest diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Bulk_Clearing_The_PostBox_On_The_Command_Processor_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Bulk_Clearing_The_PostBox_On_The_Command_Processor_Async.cs index 6a523903d0..e82967af4d 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Bulk_Clearing_The_PostBox_On_The_Command_Processor_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Bulk_Clearing_The_PostBox_On_The_Command_Processor_Async.cs @@ -94,8 +94,8 @@ public CommandProcessorPostBoxBulkClearAsyncTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); + bus, + messageMapperRegistry); } diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor.cs index 1bf3cb42dc..7e831ea256 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor.cs @@ -89,8 +89,8 @@ public CommandProcessorCallTests() handlerFactory, new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, bus, + messageMapperRegistry, replySubscriptions:replySubs, responseChannelFactory: inMemoryChannelFactory); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_In_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_In_Mapper.cs index 73afb1cf54..08f147a3a4 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_In_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_In_Mapper.cs @@ -64,8 +64,8 @@ public CommandProcessorNoInMapperTests() handlerFactory, new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, bus, + messageMapperRegistry, replySubscriptions:replySubscriptions, responseChannelFactory: new InMemoryChannelFactory() ); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Out_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Out_Mapper.cs index c1d857ce9d..1ae478ef06 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Out_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Out_Mapper.cs @@ -67,8 +67,8 @@ public CommandProcessorMissingOutMapperTests() handlerFactory, new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, bus, + messageMapperRegistry, replySubscriptions:replySubs, responseChannelFactory: new InMemoryChannelFactory()); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Timeout.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Timeout.cs index 96c1acb55b..f4c61a7676 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Timeout.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Calling_A_Server_Via_The_Command_Processor_With_No_Timeout.cs @@ -70,8 +70,8 @@ public CommandProcessorCallTestsNoTimeout() handlerFactory, new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, bus, + messageMapperRegistry, replySubscriptions:replySubs, responseChannelFactory: new InMemoryChannelFactory()); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor _Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor _Async.cs index 812b049ad2..d813cd1c15 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor _Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor _Async.cs @@ -88,9 +88,8 @@ public CommandProcessorPostBoxClearAsyncTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus - ); + bus, + messageMapperRegistry); PipelineBuilder.ClearPipelineCache(); } diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor.cs index 0969c12bd2..087873888a 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Clearing_The_PostBox_On_The_Command_Processor.cs @@ -87,9 +87,8 @@ public CommandProcessorPostBoxClearTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus - ); + bus, + messageMapperRegistry); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store.cs index 1b0c968a47..1781f02931 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store.cs @@ -67,9 +67,8 @@ public CommandProcessorDepositPostTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus - ); + bus, + messageMapperRegistry); } diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync.cs index 1ff33b2112..4f24ab3bbd 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync.cs @@ -66,9 +66,8 @@ public CommandProcessorDepositPostTestsAsync() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus - ); + bus, + messageMapperRegistry); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs index 81517f19ed..90291afd4e 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs @@ -91,9 +91,8 @@ public CommandProcessorBulkDepositPostTestsAsync() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus - ); + bus, + messageMapperRegistry); } diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store_Bulk.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store_Bulk.cs index 16e83d5825..a7bf4cbdc7 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store_Bulk.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_Store_Bulk.cs @@ -87,8 +87,8 @@ public CommandProcessorBulkDepositPostTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); + bus, + messageMapperRegistry); } diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor.cs index 6efa595ad5..5a2417f8e9 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor.cs @@ -93,9 +93,8 @@ public CommandProcessorPostBoxImplicitClearTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus - ); + bus, + messageMapperRegistry); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor_Async.cs index dd688b8943..ead1789153 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Implicit_Clearing_The_PostBox_On_The_Command_Processor_Async.cs @@ -94,8 +94,8 @@ public CommandProcessorPostBoxImplicitClearAsyncTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); + bus, + messageMapperRegistry); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs index 84dc7dad5a..3942a97ab4 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs @@ -87,9 +87,8 @@ public CommandProcessorNoMessageMapperTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus - ); + bus, + messageMapperRegistry); } public void When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry() diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs index 01d61f599e..307d87c059 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs @@ -78,8 +78,8 @@ public CommandProcessorNoMessageMapperAsyncTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); + bus, + messageMapperRegistry); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor.cs index 84d79b7b23..f526047d0d 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor.cs @@ -78,8 +78,8 @@ public CommandProcessorPostCommandTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); + bus, + messageMapperRegistry); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor_Async.cs index ebe70150b7..688243dbd5 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_To_The_Command_Processor_Async.cs @@ -79,8 +79,8 @@ public CommandProcessorPostCommandAsyncTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); + bus, + messageMapperRegistry); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender.cs index 24c57feed0..3a7e494554 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender.cs @@ -79,8 +79,8 @@ public ControlBusSenderPostMessageTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); + bus, + messageMapperRegistry); _controlBusSender = new ControlBusSender(_commandProcessor); } diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender_Async.cs index 5187edaf3a..039ef1979d 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_Via_A_Control_Bus_Sender_Async.cs @@ -80,9 +80,8 @@ public ControlBusSenderPostMessageAsyncTests() CommandProcessor commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus - ); + bus, + messageMapperRegistry); _controlBusSender = new ControlBusSender(commandProcessor); } diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store.cs index 91ec2bf798..428bd65857 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store.cs @@ -74,8 +74,8 @@ public CommandProcessorWithInMemoryOutboxTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); + bus, + messageMapperRegistry); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store_Async.cs index 0d49815836..ac019dc196 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_With_An_In_Memory_Message_Store_Async.cs @@ -78,9 +78,8 @@ public CommandProcessorWithInMemoryOutboxAscyncTests() _commandProcessor = new CommandProcessor( new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); - + bus, + messageMapperRegistry); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs index 058d038e2c..008fb56398 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs @@ -53,8 +53,7 @@ public MessagePumpCommandProcessingDeferMessageActionTests() var msg = new TransformPipelineBuilder(messageMapperRegistry, null) .BuildWrapPipeline() - .WrapAsync(new MyCommand()) - .GetAwaiter().GetResult(); + .Wrap(new MyCommand()); _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs index f44fc9dc8c..d664c0bd49 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs @@ -53,8 +53,7 @@ public MessagePumpCommandProcessingDeferMessageActionTestsAsync() var msg = new TransformPipelineBuilder(messageMapperRegistry, null) .BuildWrapPipeline() - .WrapAsync(new MyCommand()) - .GetAwaiter().GetResult(); + .Wrap(new MyCommand()); _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs index 536a9a61e7..c27b0160b9 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs @@ -55,8 +55,7 @@ public MessagePumpCommandProcessingExceptionTests() var msg = new TransformPipelineBuilder(messageMapperRegistry, null) .BuildWrapPipeline() - .WrapAsync(new MyCommand()) - .GetAwaiter().GetResult(); + .Wrap(new MyCommand()); _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs index 5f0d47e1f5..4c97c0dabe 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs @@ -56,8 +56,7 @@ public MessagePumpCommandProcessingExceptionTestsAsync() var msg = new TransformPipelineBuilder(messageMapperRegistry, null) .BuildWrapPipeline() - .WrapAsync(new MyCommand()) - .GetAwaiter().GetResult(); + .Wrap(new MyCommand()); _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs index 98ceae9cec..e12feb2811 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs @@ -54,7 +54,7 @@ public MessagePumpEventProcessingDeferMessageActionTests() var transformPipelineBuilder = new TransformPipelineBuilder(messageMapperRegistry, null); var msg = transformPipelineBuilder.BuildWrapPipeline() - .WrapAsync(new MyEvent()).GetAwaiter().GetResult(); + .Wrap(new MyEvent()); _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs index 51db1ea751..6905b8ceb0 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs @@ -53,8 +53,7 @@ public MessagePumpEventProcessingDeferMessageActionTestsAsync() { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; var msg = new TransformPipelineBuilder(messageMapperRegistry, null) - .BuildWrapPipeline().WrapAsync(new MyEvent()) - .GetAwaiter().GetResult(); + .BuildWrapPipeline().Wrap(new MyEvent()); _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs index 0f9faa52e5..2ceffec575 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs @@ -56,7 +56,7 @@ public MessagePumpEventProcessingExceptionTests() var transformPipelineBuilder = new TransformPipelineBuilder(messageMapperRegistry, null); var msg = transformPipelineBuilder.BuildWrapPipeline() - .WrapAsync(new MyEvent()).GetAwaiter().GetResult(); + .Wrap(new MyEvent()); _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs index 293aaf3b48..d9f8e716c2 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs @@ -57,8 +57,7 @@ public MessagePumpEventProcessingExceptionTestsAsync() }; var msg = new TransformPipelineBuilder(messageMapperRegistry, null) - .BuildWrapPipeline().WrapAsync(new MyEvent()) - .GetAwaiter().GetResult(); + .BuildWrapPipeline().Wrap(new MyEvent()); _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_A_Span_Is_Exported.cs b/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_A_Span_Is_Exported.cs index 4ace9a6a39..da9e6cd498 100644 --- a/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_A_Span_Is_Exported.cs +++ b/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_A_Span_Is_Exported.cs @@ -68,8 +68,8 @@ public ImplicitClearingObservabilityTests() handlerFactory, new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); + bus, + messageMapperRegistry); } [Fact] diff --git a/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_async_A_Span_Is_Exported.cs b/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_async_A_Span_Is_Exported.cs index eff3cbd9c5..7453539572 100644 --- a/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_async_A_Span_Is_Exported.cs +++ b/tests/Paramore.Brighter.Core.Tests/Observability/When_Implicitly_Clearing_The_Outbox_async_A_Span_Is_Exported.cs @@ -72,8 +72,8 @@ public ImplicitClearingAsyncObservabilityTests() handlerFactory, new InMemoryRequestContextFactory(), policyRegistry, - messageMapperRegistry, - bus); + bus, + messageMapperRegistry); } [Fact] From 9dbd16f8ad69638ac89c4f6a8cf52183bb7d34cd Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Thu, 21 Dec 2023 21:12:04 +0000 Subject: [PATCH 03/15] Support async message mappers --- .../ServiceCollectionBrighterBuilder.cs | 45 ++++-- .../ServiceCollectionExtensions.cs | 19 ++- .../ServiceCollectionMessageMapperRegistry.cs | 41 ++--- .../SqsMessageCreator.cs | 2 +- .../ServiceCollectionExtensions.cs | 4 +- .../ControlBusReceiverBuilder.cs | 2 +- .../DispatchBuilder.cs | 15 +- src/Paramore.Brighter/CommandProcessor.cs | 127 +++++++-------- src/Paramore.Brighter/IAmAMessageMapper.cs | 2 +- .../MessageMapperRegistry.cs | 24 +-- .../Transforms/Attributes/ClaimCheck.cs | 2 +- .../Transforms/Attributes/Compress.cs | 2 +- .../Transforms/Attributes/Decompress.cs | 2 +- .../Transforms/Attributes/RetrieveClaim.cs | 2 +- .../Transforms/Storage/IAmAStorageProvider.cs | 66 ++++++++ .../Storage/IAmAStorageProviderAsync.cs | 8 +- .../Storage/InMemoryStorageProvider.cs | 94 +++++++++++ .../Transforms/Storage/NullLuggageStore.cs | 12 +- .../Storage/NullLuggageStoreAsync.cs | 29 ++++ .../Transformers/ClaimCheckTransformer.cs | 24 ++- .../ClaimCheckTransformerAsync.cs | 143 +++++++++++++++++ .../CompressPayloadTransformer.cs | 10 +- .../CompressPayloadTransformerAsync.cs | 146 ++++++++++++++++++ .../When_unwrapping_a_large_message.cs | 4 +- .../When_wrapping_a_large_message.cs | 6 +- .../When_unwrapping_a_large_message.cs | 4 +- .../When_wrapping_a_large_message.cs | 6 +- .../MyLargeCommandMessageMapperAsync.cs | 31 ++++ .../When_a_message_is_under_the_threshold.cs | 16 +- ..._a_message_is_under_the_threshold_async.cs | 43 ++++++ .../When_a_message_unwraps_a_large_payload.cs | 26 ++-- ...a_message_unwraps_a_large_payload_async.cs | 53 +++++++ .../When_a_message_wraps_a_large_payload.cs | 10 +- ...n_a_message_wraps_a_large_payload_async.cs | 49 ++++++ ...hen_luggage_should_be_kept_in_the_store.cs | 20 +-- ...ggage_should_be_kept_in_the_store_async.cs | 52 +++++++ .../Claims/When_unwrapping_a_large_message.cs | 24 +-- .../When_unwrapping_a_large_message_async.cs | 72 +++++++++ .../Claims/When_wrapping_a_large_message.cs | 24 +-- .../When_wrapping_a_large_message_async.cs | 51 ++++++ .../MyCommandMessageMapperAsync.cs | 51 ++++++ ..._Message_In_The_Message_StoreAsync_Bulk.cs | 16 +- ...And_There_Is_No_Message_Mapper_Registry.cs | 6 +- ...ere_Is_No_Message_Mapper_Registry_Async.cs | 2 +- ...en_a_message_compresses_a_large_payload.cs | 30 ++-- ...essage_compresses_a_large_payload_async.cs | 83 ++++++++++ .../When_a_message_is_not_compressed.cs | 12 +- .../When_a_message_is_not_compressed_async.cs | 80 ++++++++++ .../When_a_message_is_under_the_threshold.cs | 4 +- ..._a_message_is_under_the_threshold_async.cs | 39 +++++ ...ompressing_a_large_payload_in_a_message.cs | 30 ++-- ...sing_a_large_payload_in_a_message_async.cs | 119 ++++++++++++++ .../TestDoubles/MyEventMessageMapperAsync.cs | 51 ++++++ ...e_Mapper_Map_To_Message_Has_A_Transform.cs | 2 +- ...per_Map_To_Message_Has_A_TransformAsync.cs | 4 +- ...er_Map_To_Message_Has_No_TransformAsync.cs | 4 +- ...e_Mapper_Map_To_Request_Has_A_Transform.cs | 2 +- ...per_Map_To_Request_Has_A_TransformAsync.cs | 8 +- ...r_Map_To_Request_Has_No_Transform_Async.cs | 45 ++++++ ...Creating_A_Wrap_Without_A_Factory_Async.cs | 4 +- ...ating_An_Unwrap_Without_A_Factory_Async.cs | 4 +- .../When_Unwrapping_A_Message_Mapper.cs | 3 +- .../When_Unwrapping_A_Message_MapperAsync.cs | 3 +- ...nwrapping_A_Vanilla_Message_MapperAsync.cs | 2 +- ...hen_Unwrapping_But_Factory_Returns_Null.cs | 49 ++++++ ...wrapping_But_Factory_Returns_Null_Async.cs | 50 ++++++ ...hen_Unwrapping_But_No_Registered_Mapper.cs | 3 +- ...nwrapping_But_No_Registered_MapperAsync.cs | 3 +- .../When_Wrapping_A_Message_MapperAsync.cs | 2 +- ..._Wrapping_A_Vanilla_Message_MapperAsync.cs | 2 +- .../When_Wrapping_Clean_Up_The_Pipeline.cs | 2 +- ...hen_Wrapping_Clean_Up_The_PipelineAsync.cs | 2 +- .../When_building_a_dispatcher.cs | 2 +- ...uilding_a_dispatcher_with_named_gateway.cs | 2 +- 74 files changed, 1728 insertions(+), 305 deletions(-) create mode 100644 src/Paramore.Brighter/Transforms/Storage/IAmAStorageProvider.cs create mode 100644 src/Paramore.Brighter/Transforms/Storage/InMemoryStorageProvider.cs create mode 100644 src/Paramore.Brighter/Transforms/Storage/NullLuggageStoreAsync.cs create mode 100644 src/Paramore.Brighter/Transforms/Transformers/ClaimCheckTransformerAsync.cs create mode 100644 src/Paramore.Brighter/Transforms/Transformers/CompressPayloadTransformerAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Claims/Test Doubles/MyLargeCommandMessageMapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_is_under_the_threshold_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_unwraps_a_large_payload_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_wraps_a_large_payload_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Claims/When_luggage_should_be_kept_in_the_store_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/CommandProcessors/TestDoubles/MyCommandMessageMapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_compresses_a_large_payload_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_not_compressed_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_under_the_threshold_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/Compression/When_decompressing_a_large_payload_in_a_message_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/MyEventMessageMapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_No_Transform_Async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_Factory_Returns_Null.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_Factory_Returns_Null_Async.cs diff --git a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionBrighterBuilder.cs b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionBrighterBuilder.cs index 6e0c371cc6..be09795d25 100644 --- a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionBrighterBuilder.cs +++ b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionBrighterBuilder.cs @@ -140,21 +140,13 @@ public IBrighterBuilder MapperRegistryFromAssemblies(params Assembly[] assemblie if (assemblies.Length == 0) throw new ArgumentException("Value cannot be an empty collection.", nameof(assemblies)); - var mappers = - from ti in assemblies.SelectMany(a => a.DefinedTypes).Distinct() - where ti.IsClass && !ti.IsAbstract && !ti.IsInterface - from i in ti.ImplementedInterfaces - where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IAmAMessageMapper<>) - select new { RequestType = i.GenericTypeArguments.First(), HandlerType = ti.AsType() }; - - foreach (var mapper in mappers) - { - _mapperRegistry.Add(mapper.RequestType, mapper.HandlerType); - } + RegisterMappersFromAssemblies(assemblies); + RegisterAsyncMappersFromAssemblies(assemblies); return this; } + /// /// Register handlers with the built in subscriber registry /// @@ -222,5 +214,36 @@ from i in ti.ImplementedInterfaces _serviceCollectionSubscriberRegistry.Add(subscriber.RequestType, subscriber.HandlerType); } } + + private void RegisterMappersFromAssemblies(Assembly[] assemblies) + { + var mappers = + from ti in assemblies.SelectMany(a => a.DefinedTypes).Distinct() + where ti.IsClass && !ti.IsAbstract && !ti.IsInterface + from i in ti.ImplementedInterfaces + where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IAmAMessageMapper<>) + select new { RequestType = i.GenericTypeArguments.First(), HandlerType = ti.AsType() }; + + foreach (var mapper in mappers) + { + _mapperRegistry.Add(mapper.RequestType, mapper.HandlerType); + } + } + + private void RegisterAsyncMappersFromAssemblies(Assembly[] assemblies) + { + var mappers = + from ti in assemblies.SelectMany(a => a.DefinedTypes).Distinct() + where ti.IsClass && !ti.IsAbstract && !ti.IsInterface + from i in ti.ImplementedInterfaces + where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IAmAMessageMapperAsync<>) + select new { RequestType = i.GenericTypeArguments.First(), HandlerType = ti.AsType() }; + + foreach (var mapper in mappers) + { + _mapperRegistry.AddAsync(mapper.RequestType, mapper.HandlerType); + } + } + } } diff --git a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index 89d5aa35c3..ea7b1c5a54 100644 --- a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -359,10 +359,15 @@ public static MessageMapperRegistry MessageMapperRegistry(IServiceProvider provi null ); - foreach (var messageMapper in serviceCollectionMessageMapperRegistry) + foreach (var messageMapper in serviceCollectionMessageMapperRegistry.Mappers) { messageMapperRegistry.Register(messageMapper.Key, messageMapper.Value); } + + foreach (var messageMapper in serviceCollectionMessageMapperRegistry.AsyncMappers) + { + messageMapperRegistry.RegisterAsync(messageMapper.Key, messageMapper.Value); + } return messageMapperRegistry; } @@ -423,5 +428,17 @@ public static ServiceProviderTransformerFactory TransformFactory(IServiceProvide { return new ServiceProviderTransformerFactory(provider); } + + /// + /// Creates transforms. Normally you don't need to call this, it is called by the builder for Brighter or + /// the Service Activator + /// Visibility is required for use from both + /// + /// The IoC container to build the transform factory over + /// + public static ServiceProviderTransformerFactoryAsync TransformFactoryAsync(IServiceProvider provider) + { + return new ServiceProviderTransformerFactoryAsync(provider); + } } } diff --git a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionMessageMapperRegistry.cs b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionMessageMapperRegistry.cs index de2592de63..1a76add609 100644 --- a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionMessageMapperRegistry.cs +++ b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionMessageMapperRegistry.cs @@ -35,18 +35,20 @@ namespace Paramore.Brighter.Extensions.DependencyInjection /// When parsing for message mappers in assemblies, stores any found message mappers. A later step will add these to the message mapper registry /// Not used directly /// - public class ServiceCollectionMessageMapperRegistry: IEnumerable> + public class ServiceCollectionMessageMapperRegistry { private readonly IServiceCollection _serviceCollection; - private readonly Dictionary _mapperCollection = new Dictionary(); private readonly ServiceLifetime _lifetime; + public Dictionary Mappers { get; } = new Dictionary(); + public Dictionary AsyncMappers { get; } = new Dictionary(); + public ServiceCollectionMessageMapperRegistry(IServiceCollection serviceCollection, ServiceLifetime lifetime = ServiceLifetime.Singleton) { _serviceCollection = serviceCollection; _lifetime = lifetime; } - + /// /// Register a mapper with the collection (generic version) /// @@ -57,6 +59,16 @@ public void Register() where TRequest : class, IReques Add(typeof(TRequest), typeof(TMessageMapper)); } + /// + /// Register a mapper with the collection (generic version) + /// + /// The type of the request to map + /// The type of the mapper + public void RegisterAsync() where TRequest : class, IRequest where TMessageMapper : class, IAmAMessageMapperAsync + { + AddAsync(typeof(TRequest), typeof(TMessageMapper)); + } + /// /// Add a mapper to the collection /// @@ -65,25 +77,18 @@ public void Register() where TRequest : class, IReques public void Add(Type message, Type mapper) { _serviceCollection.TryAdd(new ServiceDescriptor(mapper, mapper, _lifetime)); - _mapperCollection.Add(message, mapper); + Mappers.Add(message, mapper); } - - /// - /// Get the genericly typed iterator over this collection - /// - /// An iterator - public IEnumerator> GetEnumerator() - { - return _mapperCollection.GetEnumerator(); - } - + /// - /// Get the untyped iterator over the collection + /// Add a mapper to the collection /// - /// An iterator - IEnumerator IEnumerable.GetEnumerator() + /// The type of message to map + /// The type of the mapper + public void AddAsync(Type message, Type mapper) { - return GetEnumerator(); + _serviceCollection.TryAdd(new ServiceDescriptor(mapper, mapper, _lifetime)); + AsyncMappers.Add(message, mapper); } } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreator.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreator.cs index a95798ec21..c4f3c40aa6 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreator.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreator.cs @@ -112,7 +112,7 @@ public Message CreateMessage(Amazon.SQS.Model.Message sqsMessage) private static MessageBody ReadMessageBody(Amazon.SQS.Model.Message sqsMessage, string contentType) { - if(contentType == CompressPayloadTransformer.GZIP || contentType == CompressPayloadTransformer.DEFLATE || contentType == CompressPayloadTransformer.BROTLI) + if(contentType == CompressPayloadTransformerAsync.GZIP || contentType == CompressPayloadTransformerAsync.DEFLATE || contentType == CompressPayloadTransformerAsync.BROTLI) return new MessageBody(sqsMessage.Body, contentType, CharacterEncoding.Base64); return new MessageBody(sqsMessage.Body, contentType, CharacterEncoding.UTF8); diff --git a/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index 14dea5fd13..6a48ab1618 100644 --- a/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Transactions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; @@ -70,9 +69,10 @@ private static Dispatcher BuildDispatcher(IServiceProvider serviceProvider) var messageMapperRegistry = ServiceCollectionExtensions.MessageMapperRegistry(serviceProvider); var messageTransformFactory = ServiceCollectionExtensions.TransformFactory(serviceProvider); + var messageTransformFactoryAsync = ServiceCollectionExtensions.TransformFactoryAsync(serviceProvider); return dispatcherBuilder - .MessageMappers(messageMapperRegistry, messageTransformFactory) + .MessageMappers(messageMapperRegistry, messageTransformFactory, messageTransformFactoryAsync) .DefaultChannelFactory(options.ChannelFactory) .Subscriptions(options.Subscriptions).Build(); } diff --git a/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs b/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs index 9b0d572c66..c13be87d0e 100644 --- a/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs +++ b/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs @@ -182,7 +182,7 @@ public Dispatcher Build(string hostName) return DispatchBuilder.With() .CommandProcessorFactory(() => new CommandProcessorProvider(commandProcessor)) - .MessageMappers(incomingMessageMapperRegistry, null) + .MessageMappers(incomingMessageMapperRegistry, null, null) .DefaultChannelFactory(_channelFactory) .Subscriptions(subscriptions) .Build(); diff --git a/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs b/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs index b4c66026b0..d933204e8f 100644 --- a/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs +++ b/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs @@ -68,11 +68,13 @@ public INeedAMessageMapper CommandProcessorFactory(Func /// The message mapper registry. - /// + /// A factory to produce transformers for a message mapper + /// A factory to produce async transformers for a message mapper /// INeedAChannelFactory. public INeedAChannelFactory MessageMappers( IAmAMessageMapperRegistry theMessageMapperRegistry, - IAmAMessageTransformerFactory messageTransformerFactory) + IAmAMessageTransformerFactory messageTransformerFactory, + IAmAMessageTransformerFactoryAsync messageTransformFactoryAsync) { _messageMapperRegistry = theMessageMapperRegistry; _messageTransformerFactory = messageTransformerFactory; @@ -136,6 +138,8 @@ public Dispatcher Build() { return new Dispatcher(_commandProcessorFactory, _messageMapperRegistry, _subscriptions, _messageTransformerFactory); } + + } #region Progressive Interfaces @@ -163,10 +167,11 @@ public interface INeedAMessageMapper /// /// The message mapper registry. /// The factory for creating transforms + /// The factory for creating async transforms /// INeedAChannelFactory. - INeedAChannelFactory MessageMappers( - IAmAMessageMapperRegistry messageMapperRegistry, - IAmAMessageTransformerFactory messageTransformerFactory); + INeedAChannelFactory MessageMappers(IAmAMessageMapperRegistry messageMapperRegistry, + IAmAMessageTransformerFactory messageTransformerFactory, + IAmAMessageTransformerFactoryAsync messageTransformFactoryAsync); } /// /// Interface INeedAChannelFactory diff --git a/src/Paramore.Brighter/CommandProcessor.cs b/src/Paramore.Brighter/CommandProcessor.cs index 705e1707a4..41c7a95f8e 100644 --- a/src/Paramore.Brighter/CommandProcessor.cs +++ b/src/Paramore.Brighter/CommandProcessor.cs @@ -148,7 +148,7 @@ public CommandProcessor( /// The request context factory. /// The policy registry. /// The external service bus that we want to send messages over - /// The mapper registry. + /// The mapper registry; it should also implement IAmAMessageMapperRegistryAsync /// The async mapper registry /// The feature switch config provider. /// Do we want to insert an inbox handler into pipelines without the attribute. Null (default = no), yes = how to configure @@ -163,7 +163,6 @@ public CommandProcessor( IPolicyRegistry policyRegistry, IAmAnExternalBusService bus, IAmAMessageMapperRegistry mapperRegistry = null, - IAmAMessageMapperRegistryAsync mapperRegistryAsync = null, IAmAFeatureSwitchRegistry featureSwitchRegistry = null, InboxConfiguration inboxConfiguration = null, IAmAMessageTransformerFactory messageTransformerFactory = null, @@ -176,10 +175,12 @@ public CommandProcessor( _responseChannelFactory = responseChannelFactory; _replySubscriptions = replySubscriptions; - if (mapperRegistry == null && mapperRegistryAsync == null) - throw new ConfigurationException("A Command Processor with an external bus must have a message mapper registry"); - if (mapperRegistry != null) _transformPipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); - if (mapperRegistryAsync != null) _transformPipelineBuilderAsync = new TransformPipelineBuilderAsync(mapperRegistryAsync, messageTransformerFactoryAsync); + if (mapperRegistry == null) + throw new ConfigurationException("A Command Processor with an external bus must have a message mapper registry that implements IAmAMessageMapperRegistry"); + if (!(mapperRegistry is IAmAMessageMapperRegistryAsync mapperRegistryAsync)) + throw new ConfigurationException("A Command Processor with an external bus must have a message mapper registry that implements IAmAMessageMapperRegistryAsync"); + _transformPipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + _transformPipelineBuilderAsync = new TransformPipelineBuilderAsync(mapperRegistryAsync, messageTransformerFactoryAsync); InitExtServiceBus(bus); } @@ -192,7 +193,6 @@ public CommandProcessor( /// The policy registry. /// The mapper registry. /// The external service bus that we want to send messages over - /// The mapper registry. /// The feature switch config provider. /// Do we want to insert an inbox handler into pipelines without the attribute. Null (default = no), yes = how to configure /// The factory used to create a transformer pipeline for a message mapper @@ -203,7 +203,6 @@ public CommandProcessor( IPolicyRegistry policyRegistry, IAmAnExternalBusService bus, IAmAMessageMapperRegistry mapperRegistry = null, - IAmAMessageMapperRegistryAsync mapperRegistryAsync = null, IAmAFeatureSwitchRegistry featureSwitchRegistry = null, InboxConfiguration inboxConfiguration = null, IAmAMessageTransformerFactory messageTransformerFactory = null, @@ -216,11 +215,13 @@ public CommandProcessor( _inboxConfiguration = inboxConfiguration; _replySubscriptions = replySubscriptions; - if (mapperRegistry == null && mapperRegistryAsync == null) - throw new ConfigurationException("A Command Processor with an external bus must have a message mapper registry"); - if (mapperRegistry != null) _transformPipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); - if (mapperRegistryAsync != null) _transformPipelineBuilderAsync = new TransformPipelineBuilderAsync(mapperRegistryAsync, messageTransformerFactoryAsync); - + if (mapperRegistry == null) + throw new ConfigurationException("A Command Processor with an external bus must have a message mapper registry that implements IAmAMessageMapperRegistry"); + if (!(mapperRegistry is IAmAMessageMapperRegistryAsync mapperRegistryAsync)) + throw new ConfigurationException("A Command Processor with an external bus must have a message mapper registry that implements IAmAMessageMapperRegistryAsync"); + _transformPipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + _transformPipelineBuilderAsync = new TransformPipelineBuilderAsync(mapperRegistryAsync, messageTransformerFactoryAsync); + InitExtServiceBus(bus); } @@ -526,22 +527,7 @@ IAmABoxTransactionProvider transactionProvider if (!bus.HasOutbox()) throw new InvalidOperationException("No outbox defined."); - Message message; - if (_transformPipelineBuilder.HasPipeline()) - { - message = _transformPipelineBuilder.BuildWrapPipeline().Wrap(request); - - } - else if (_transformPipelineBuilderAsync.HasPipeline()) - { - message = _transformPipelineBuilderAsync.BuildWrapPipeline().WrapAsync(request) - .GetAwaiter() - .GetResult(); - } - else - { - throw new InvalidOperationException("No message mapper defined for request"); - } + var message = MapMessage(request, new CancellationToken()).GetAwaiter().GetResult(); AddTelemetryToMessage(message); @@ -594,7 +580,7 @@ IAmABoxTransactionProvider transactionProvider foreach (var batch in SplitRequestBatchIntoTypes(requests)) { - var messages = MapMessages(batch.Key, batch); + var messages = MapMessages(batch.Key, batch, new CancellationToken()).GetAwaiter().GetResult(); s_logger.LogInformation("Save requests: {RequestType} {AmountOfMessages}", batch.Key, messages.Count()); @@ -661,24 +647,7 @@ public async Task DepositPostAsync( if (!bus.HasAsyncOutbox()) throw new InvalidOperationException("No async outbox defined."); - Message message; - if (_transformPipelineBuilderAsync.HasPipeline()) - { - message = await _transformPipelineBuilderAsync - .BuildWrapPipeline() - .WrapAsync(request, cancellationToken); - } - else if (_transformPipelineBuilder.HasPipeline()) - { - message = _transformPipelineBuilder - .BuildWrapPipeline() - .Wrap(request); - - } - else - { - throw new InvalidOperationException("No message mapper defined for request"); - } + Message message = await MapMessage(request, cancellationToken); AddTelemetryToMessage(message); @@ -687,8 +656,10 @@ await bus.AddToOutboxAsync(request, message, return message.Id; } - - /// + + + + /// /// Adds a message into the outbox, and returns the id of the saved message. /// Intended for use with the Outbox pattern: http://gistlabs.com/2014/05/the-outbox/ normally you include the /// call to DepositPostBox within the scope of the transaction to write corresponding entity state to your @@ -743,7 +714,7 @@ public async Task DepositPostAsync( foreach (var batch in SplitRequestBatchIntoTypes(requests)) { - var messages = await MapMessagesAsync(batch.Key, batch.ToArray(), cancellationToken); + var messages = await MapMessages(batch.Key, batch.ToArray(), cancellationToken); s_logger.LogInformation("Save requests: {RequestType} {AmountOfMessages}", batch.Key, messages.Count()); @@ -1003,23 +974,57 @@ private bool HandlerFactoryIsNotEitherIAmAHandlerFactorySyncOrAsync(IAmAHandlerF return true; } } - - private List MapMessages(Type requestType, IEnumerable requests) + + private async Task MapMessage(TRequest request, CancellationToken cancellationToken) + where TRequest : class, IRequest { - return (List)GetType() - .GetMethod(nameof(BulkMapMessages), BindingFlags.Instance | BindingFlags.NonPublic) - .MakeGenericMethod(requestType) - .Invoke(this, new[] { requests }); + Message message; + if (_transformPipelineBuilderAsync.HasPipeline()) + { + message = await _transformPipelineBuilderAsync + .BuildWrapPipeline() + .WrapAsync(request, cancellationToken); + } + else if (_transformPipelineBuilder.HasPipeline()) + { + message = _transformPipelineBuilder + .BuildWrapPipeline() + .Wrap(request); + + } + else + { + throw new ArgumentOutOfRangeException("No message mapper defined for request"); + } + + return message; } - private Task> MapMessagesAsync(Type requestType, IEnumerable requests, + private Task> MapMessages(Type requestType, IEnumerable requests, CancellationToken cancellationToken) { var parameters = new object[] { requests, cancellationToken }; - return (Task>)GetType() - .GetMethod(nameof(BulkMapMessagesAsync), BindingFlags.Instance | BindingFlags.NonPublic) + + var hasAsyncPipeline = (bool) typeof(TransformPipelineBuilderAsync) + .GetMethod(nameof(TransformPipelineBuilderAsync.HasPipeline), + BindingFlags.Instance | BindingFlags.Public) .MakeGenericMethod(requestType) - .Invoke(this, parameters); + .Invoke(this._transformPipelineBuilderAsync, null); + + if (hasAsyncPipeline) + { + return (Task>) GetType() + .GetMethod(nameof(BulkMapMessagesAsync), BindingFlags.Instance | BindingFlags.NonPublic) + .MakeGenericMethod(requestType) + .Invoke(this, parameters); + } + + var tcs = new TaskCompletionSource>(); + tcs.SetResult((List)GetType() + .GetMethod(nameof(BulkMapMessages), BindingFlags.Instance | BindingFlags.NonPublic) + .MakeGenericMethod(requestType) + .Invoke(this, new[] { requests })); + return tcs.Task; } private IEnumerable> SplitRequestBatchIntoTypes(IEnumerable requests) diff --git a/src/Paramore.Brighter/IAmAMessageMapper.cs b/src/Paramore.Brighter/IAmAMessageMapper.cs index b8d51fb717..e2565e6cea 100644 --- a/src/Paramore.Brighter/IAmAMessageMapper.cs +++ b/src/Paramore.Brighter/IAmAMessageMapper.cs @@ -52,4 +52,4 @@ public interface IAmAMessageMapper : IAmAMessageMapper where TRequest /// TRequest. TRequest MapToRequest(Message message); } -} \ No newline at end of file +} diff --git a/src/Paramore.Brighter/MessageMapperRegistry.cs b/src/Paramore.Brighter/MessageMapperRegistry.cs index cb4c12952f..5e74300db9 100644 --- a/src/Paramore.Brighter/MessageMapperRegistry.cs +++ b/src/Paramore.Brighter/MessageMapperRegistry.cs @@ -25,6 +25,7 @@ THE SOFTWARE. */ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; namespace Paramore.Brighter { @@ -35,7 +36,7 @@ namespace Paramore.Brighter /// registered via /// This is a default implementation of which is suitable for most usages, the interface is provided mainly for testing /// - public class MessageMapperRegistry : IAmAMessageMapperRegistry, IAmAMessageMapperRegistryAsync, IEnumerable> + public class MessageMapperRegistry : IAmAMessageMapperRegistry, IAmAMessageMapperRegistryAsync { private readonly IAmAMessageMapperFactory _messageMapperFactory; private readonly IAmAMessageMapperFactoryAsync _messageMapperFactoryAsync; @@ -83,7 +84,7 @@ public IAmAMessageMapperAsync GetAsync() where TRequest : cl { if (_asyncMessageMappers.ContainsKey(typeof(TRequest))) { - var messageMapperType = _messageMappers[typeof(TRequest)]; + var messageMapperType = _asyncMessageMappers[typeof(TRequest)]; return (IAmAMessageMapperAsync)_messageMapperFactoryAsync.Create(messageMapperType); } else @@ -148,24 +149,5 @@ public void RegisterAsync(Type request, Type mapper) _asyncMessageMappers.Add(request, mapper); } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// A that can be used to iterate through the collection. - public IEnumerator> GetEnumerator() - { - return _messageMappers.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// An object that can be used to iterate through the collection. - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } } diff --git a/src/Paramore.Brighter/Transforms/Attributes/ClaimCheck.cs b/src/Paramore.Brighter/Transforms/Attributes/ClaimCheck.cs index 7843f411d0..0fb5408030 100644 --- a/src/Paramore.Brighter/Transforms/Attributes/ClaimCheck.cs +++ b/src/Paramore.Brighter/Transforms/Attributes/ClaimCheck.cs @@ -56,7 +56,7 @@ public override object[] InitializerParams() /// The type of the Claims Check Transformer public override Type GetHandlerType() { - return typeof(ClaimCheckTransformer); + return typeof(ClaimCheckTransformerAsync); } } } diff --git a/src/Paramore.Brighter/Transforms/Attributes/Compress.cs b/src/Paramore.Brighter/Transforms/Attributes/Compress.cs index 6ba9d5bbeb..95c7e38df5 100644 --- a/src/Paramore.Brighter/Transforms/Attributes/Compress.cs +++ b/src/Paramore.Brighter/Transforms/Attributes/Compress.cs @@ -70,7 +70,7 @@ public override object[] InitializerParams() /// The type for the compression middleware public override Type GetHandlerType() { - return typeof(CompressPayloadTransformer); + return typeof(CompressPayloadTransformerAsync); } } } diff --git a/src/Paramore.Brighter/Transforms/Attributes/Decompress.cs b/src/Paramore.Brighter/Transforms/Attributes/Decompress.cs index b6361fef9a..12e742ccf6 100644 --- a/src/Paramore.Brighter/Transforms/Attributes/Decompress.cs +++ b/src/Paramore.Brighter/Transforms/Attributes/Decompress.cs @@ -65,7 +65,7 @@ public override object[] InitializerParams() /// The type for the compression middleware public override Type GetHandlerType() { - return typeof(CompressPayloadTransformer); + return typeof(CompressPayloadTransformerAsync); } } } diff --git a/src/Paramore.Brighter/Transforms/Attributes/RetrieveClaim.cs b/src/Paramore.Brighter/Transforms/Attributes/RetrieveClaim.cs index b2889e97ab..31b2c1ca30 100644 --- a/src/Paramore.Brighter/Transforms/Attributes/RetrieveClaim.cs +++ b/src/Paramore.Brighter/Transforms/Attributes/RetrieveClaim.cs @@ -55,7 +55,7 @@ public override object[] InitializerParams() /// The type of the Claims Check Transformer public override Type GetHandlerType() { - return typeof(ClaimCheckTransformer); + return typeof(ClaimCheckTransformerAsync); } } } diff --git a/src/Paramore.Brighter/Transforms/Storage/IAmAStorageProvider.cs b/src/Paramore.Brighter/Transforms/Storage/IAmAStorageProvider.cs new file mode 100644 index 0000000000..6f27f0583c --- /dev/null +++ b/src/Paramore.Brighter/Transforms/Storage/IAmAStorageProvider.cs @@ -0,0 +1,66 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Transforms.Storage +{ + /// + /// Provides storage for a message body that is too large to transmit over message-oriented middleware + /// The stored value is the 'luggage' and the id of the item is the 'claim check' + /// + public interface IAmAStorageProvider + { + /// + /// Delete the luggage identified by the claim check + /// Used to clean up after luggage is retrieved + /// + /// The claim check for the luggage + void Delete(string claimCheck); + + + /// + /// Downloads the luggage associated with the claim check + /// + /// The claim check for the luggage + /// The luggage as a stream + Stream Retrieve(string claimCheck); + + /// + /// Do we have luggage for this claim check - in case of error or deletion + /// + /// + bool HasClaim(string claimCheck); + + /// + /// Puts luggage into the store and provides a claim check for that luggage + /// + /// A stream representing the luggage to check + /// A claim check for the luggage stored + string Store(Stream stream); + } +} diff --git a/src/Paramore.Brighter/Transforms/Storage/IAmAStorageProviderAsync.cs b/src/Paramore.Brighter/Transforms/Storage/IAmAStorageProviderAsync.cs index 9c9be7c9d8..fcf82a3a6e 100644 --- a/src/Paramore.Brighter/Transforms/Storage/IAmAStorageProviderAsync.cs +++ b/src/Paramore.Brighter/Transforms/Storage/IAmAStorageProviderAsync.cs @@ -41,7 +41,7 @@ public interface IAmAStorageProviderAsync /// /// The claim check for the luggage /// The cancellation token - Task DeleteAsync(string claimCheck, CancellationToken cancellationToken); + Task DeleteAsync(string claimCheck, CancellationToken cancellationToken = default(CancellationToken)); /// /// Downloads the luggage associated with the claim check @@ -49,14 +49,14 @@ public interface IAmAStorageProviderAsync /// The claim check for the luggage /// The cancellation token /// The luggage as a stream - Task RetrieveAsync(string claimCheck, CancellationToken cancellationToken); + Task RetrieveAsync(string claimCheck, CancellationToken cancellationToken = default(CancellationToken)); /// /// Do we have luggage for this claim check - in case of error or deletion /// /// /// The cancellation token - Task HasClaimAsync(string claimCheck, CancellationToken cancellationToken); + Task HasClaimAsync(string claimCheck, CancellationToken cancellationToken = default(CancellationToken)); /// /// Puts luggage into the store and provides a claim check for that luggage @@ -64,6 +64,6 @@ public interface IAmAStorageProviderAsync /// A stream representing the luggage to check /// The cancellation token /// A claim check for the luggage stored - Task StoreAsync(Stream stream, CancellationToken cancellationToken); + Task StoreAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/src/Paramore.Brighter/Transforms/Storage/InMemoryStorageProvider.cs b/src/Paramore.Brighter/Transforms/Storage/InMemoryStorageProvider.cs new file mode 100644 index 0000000000..d7bf90a25c --- /dev/null +++ b/src/Paramore.Brighter/Transforms/Storage/InMemoryStorageProvider.cs @@ -0,0 +1,94 @@ +#region Licence + +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Transforms.Storage +{ + /// + /// Provides in-memory storage of a message body, intended for use with testing only + /// + public class InMemoryStorageProvider : IAmAStorageProvider + { + private readonly Dictionary _contents = new Dictionary(); + + /// + /// Delete the luggage identified by the claim check + /// Used to clean up after luggage is retrieved + /// + /// The claim check for the luggage + public void Delete(string claimCheck) + { + _contents.Remove(claimCheck); + } + + /// + /// Downloads the luggage associated with the claim check + /// + /// The claim check for the luggage + /// The luggage as a stream + public Stream Retrieve(string claimCheck) + { + if (_contents.TryGetValue(claimCheck, out string value)) + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + writer.Write(value); + writer.Flush(); + stream.Position = 0; + return stream; + } + + throw new ArgumentOutOfRangeException("claimCheck", claimCheck, "Could not find the claim check in the store"); + } + + /// + /// Do we have luggage for this claim check - in case of error or deletion + /// + /// + /// Add cancellation token + public bool HasClaim(string claimCheck) + { + return _contents.ContainsKey(claimCheck); + } + + /// + /// Puts luggage into the store and provides a claim check for that luggage + /// + /// A stream representing the luggage to check + /// A claim check for the luggage stored + public string Store(Stream stream) + { + var id = Guid.NewGuid().ToString(); + var reader = new StreamReader(stream); + _contents.Add(id, reader.ReadToEnd()); + return id; + } + } +} diff --git a/src/Paramore.Brighter/Transforms/Storage/NullLuggageStore.cs b/src/Paramore.Brighter/Transforms/Storage/NullLuggageStore.cs index ece8f1bd72..5854bb7016 100644 --- a/src/Paramore.Brighter/Transforms/Storage/NullLuggageStore.cs +++ b/src/Paramore.Brighter/Transforms/Storage/NullLuggageStore.cs @@ -1,27 +1,25 @@ using System.IO; -using System.Threading; -using System.Threading.Tasks; namespace Paramore.Brighter.Transforms.Storage { - public class NullLuggageStore : IAmAStorageProviderAsync + public class NullLuggageStore : IAmAStorageProvider { - public Task DeleteAsync(string claimCheck, CancellationToken cancellationToken) + public void Delete(string claimCheck) { throw new System.NotImplementedException("This is a null store, you must register a real store after Brighter"); } - public Task RetrieveAsync(string claimCheck, CancellationToken cancellationToken) + public Stream Retrieve(string claimCheck) { throw new System.NotImplementedException("This is a null store, you must register a real store after Brighter"); } - public Task HasClaimAsync(string claimCheck, CancellationToken cancellationToken) + public bool HasClaim(string claimCheck) { throw new System.NotImplementedException("This is a null store, you must register a real store after Brighter"); } - public Task StoreAsync(Stream stream, CancellationToken cancellationToken) + public string Store(Stream stream) { throw new System.NotImplementedException("This is a null store, you must register a real store after Brighter"); } diff --git a/src/Paramore.Brighter/Transforms/Storage/NullLuggageStoreAsync.cs b/src/Paramore.Brighter/Transforms/Storage/NullLuggageStoreAsync.cs new file mode 100644 index 0000000000..d110df93ff --- /dev/null +++ b/src/Paramore.Brighter/Transforms/Storage/NullLuggageStoreAsync.cs @@ -0,0 +1,29 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Transforms.Storage +{ + public class NullLuggageStoreAsync : IAmAStorageProviderAsync + { + public Task DeleteAsync(string claimCheck, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new System.NotImplementedException("This is a null store, you must register a real store after Brighter"); + } + + public Task RetrieveAsync(string claimCheck, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new System.NotImplementedException("This is a null store, you must register a real store after Brighter"); + } + + public Task HasClaimAsync(string claimCheck, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new System.NotImplementedException("This is a null store, you must register a real store after Brighter"); + } + + public Task StoreAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new System.NotImplementedException("This is a null store, you must register a real store after Brighter"); + } + } +} diff --git a/src/Paramore.Brighter/Transforms/Transformers/ClaimCheckTransformer.cs b/src/Paramore.Brighter/Transforms/Transformers/ClaimCheckTransformer.cs index f4ba27e9a2..9356c19a41 100644 --- a/src/Paramore.Brighter/Transforms/Transformers/ClaimCheckTransformer.cs +++ b/src/Paramore.Brighter/Transforms/Transformers/ClaimCheckTransformer.cs @@ -25,16 +25,14 @@ THE SOFTWARE. */ using System; using System.IO; -using System.Threading; -using System.Threading.Tasks; using Paramore.Brighter.Transforms.Storage; namespace Paramore.Brighter.Transforms.Transformers { - public class ClaimCheckTransformer : IAmAMessageTransformAsync + public class ClaimCheckTransformer : IAmAMessageTransform { public const string CLAIM_CHECK = "claim_check_header"; - private readonly IAmAStorageProviderAsync _store; + private readonly IAmAStorageProvider _store; private int _thresholdInBytes; private bool _retainLuggage; @@ -45,7 +43,7 @@ public class ClaimCheckTransformer : IAmAMessageTransformAsync /// /// The storage to use for the payload /// The size at which checking luggage is triggered. If size is 0 or less, the check will always be triggered - public ClaimCheckTransformer(IAmAStorageProviderAsync store) + public ClaimCheckTransformer(IAmAStorageProvider store) { _store = store; } @@ -95,20 +93,19 @@ public void InitializeUnwrapFromAttributeParams(params object[] initializerList) /// If we place it in storage, set a header property to contain the 'claim' that can be used to retrieve the 'luggage' /// /// The message whose contents we want to - /// Add cancellation token /// The message, with 'luggage' swapped out if over the threshold - public async Task WrapAsync(Message message, CancellationToken cancellationToken= default) + public Message Wrap(Message message) { if (System.Text.Encoding.Unicode.GetByteCount(message.Body.Value) < _thresholdInBytes) return message; var body = message.Body.Value; var stream = new MemoryStream(); var writer = new StreamWriter(stream); - await writer.WriteAsync(body); - await writer.FlushAsync(); + writer.Write(body); + writer.Flush(); stream.Position = 0; - var id = await _store.StoreAsync(stream, cancellationToken); + var id = _store.Store(stream); message.Header.Bag[CLAIM_CHECK] = id; message.Body = new MessageBody($"Claim Check {id}"); @@ -120,19 +117,18 @@ public async Task WrapAsync(Message message, CancellationToken cancella /// If a message has a 'claim' in its header, then retrieve the associated 'luggage' from the store and replace the body /// /// The message, with luggage retrieved if required - /// /// - public async Task UnwrapAsync(Message message, CancellationToken cancellationToken = default) + public Message Unwrap(Message message) { if (message.Header.Bag.TryGetValue(CLAIM_CHECK, out object objId)) { var id = (string)objId; - var luggage = await new StreamReader(await _store.RetrieveAsync(id, cancellationToken)).ReadToEndAsync(); + var luggage = new StreamReader(_store.Retrieve(id)).ReadToEnd(); var newBody = new MessageBody(luggage); message.Body = newBody; if (!_retainLuggage) { - await _store.DeleteAsync(id, cancellationToken); + _store.Delete(id); message.Header.Bag.Remove(CLAIM_CHECK); } } diff --git a/src/Paramore.Brighter/Transforms/Transformers/ClaimCheckTransformerAsync.cs b/src/Paramore.Brighter/Transforms/Transformers/ClaimCheckTransformerAsync.cs new file mode 100644 index 0000000000..8cb10c1f27 --- /dev/null +++ b/src/Paramore.Brighter/Transforms/Transformers/ClaimCheckTransformerAsync.cs @@ -0,0 +1,143 @@ +#region Licence + +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Paramore.Brighter.Transforms.Storage; + +namespace Paramore.Brighter.Transforms.Transformers +{ + public class ClaimCheckTransformerAsync : IAmAMessageTransformAsync + { + public const string CLAIM_CHECK = "claim_check_header"; + private readonly IAmAStorageProviderAsync _store; + private int _thresholdInBytes; + private bool _retainLuggage; + + /// + /// A claim check moves the payload of a message, which when wrapping checks for a payloads that exceeds a certain threshold size, and inserts those + /// above that value into storage, removing them from the payload, then inserts a claim check into the message header's context bag. On unwrapping + /// it retrieves the payload, via the claim check and replaces the payload. + /// + /// The storage to use for the payload + /// The size at which checking luggage is triggered. If size is 0 or less, the check will always be triggered + public ClaimCheckTransformerAsync(IAmAStorageProviderAsync store) + { + _store = store; + } + + /// + /// NOTE: Default constructor is not intended for usage, but is required for the service activator to not throw an exception + /// due to missing dependencies when auto-registration registers this transformer but you do not use the claim check + /// functionality + /// If you forget to register your storage provider, you will get a not implemented exception when you try to use the + /// claim check functionality + /// + public ClaimCheckTransformerAsync() + { + _store = new NullLuggageStoreAsync(); + } + + public void Dispose() + { + } + + /// + /// We assume that the initializer list contains + /// [0] - an integer representing the size of the threshold to convert to a string in Kb i.e. 5 would be 5Kb + /// + /// The initialization for a claim check + public void InitializeWrapFromAttributeParams(params object[] initializerList) + { + if (initializerList.Length != 1) throw new ArgumentException("Missing parameter for threshold size", "initializerList"); + + _thresholdInBytes = (int)initializerList[0] * 1024; + } + + /// + /// We assume the initializer list contains + /// [0] -a bool, true if we should keep the message contents in storage, false if we should delete it + /// + /// + public void InitializeUnwrapFromAttributeParams(params object[] initializerList) + { + if (initializerList.Length != 1) throw new ArgumentException("Missing parameter for luggage retention", "initializerList"); + + _retainLuggage = (bool)initializerList[0]; + } + + /// + /// If a message exceeds the threshold passed through the initializer list, then put it into storage + /// If we place it in storage, set a header property to contain the 'claim' that can be used to retrieve the 'luggage' + /// + /// The message whose contents we want to + /// Add cancellation token + /// The message, with 'luggage' swapped out if over the threshold + public async Task WrapAsync(Message message, CancellationToken cancellationToken= default) + { + if (System.Text.Encoding.Unicode.GetByteCount(message.Body.Value) < _thresholdInBytes) return message; + + var body = message.Body.Value; + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + await writer.WriteAsync(body); + await writer.FlushAsync(); + stream.Position = 0; + + var id = await _store.StoreAsync(stream, cancellationToken); + + message.Header.Bag[CLAIM_CHECK] = id; + message.Body = new MessageBody($"Claim Check {id}"); + + return message; + } + + /// + /// If a message has a 'claim' in its header, then retrieve the associated 'luggage' from the store and replace the body + /// + /// The message, with luggage retrieved if required + /// + /// + public async Task UnwrapAsync(Message message, CancellationToken cancellationToken = default) + { + if (message.Header.Bag.TryGetValue(CLAIM_CHECK, out object objId)) + { + var id = (string)objId; + var luggage = await new StreamReader(await _store.RetrieveAsync(id, cancellationToken)).ReadToEndAsync(); + var newBody = new MessageBody(luggage); + message.Body = newBody; + if (!_retainLuggage) + { + await _store.DeleteAsync(id, cancellationToken); + message.Header.Bag.Remove(CLAIM_CHECK); + } + } + + return message; + } + } +} diff --git a/src/Paramore.Brighter/Transforms/Transformers/CompressPayloadTransformer.cs b/src/Paramore.Brighter/Transforms/Transformers/CompressPayloadTransformer.cs index 5e9549ef2d..fe09a5303c 100644 --- a/src/Paramore.Brighter/Transforms/Transformers/CompressPayloadTransformer.cs +++ b/src/Paramore.Brighter/Transforms/Transformers/CompressPayloadTransformer.cs @@ -7,7 +7,7 @@ namespace Paramore.Brighter.Transforms.Transformers { - public class CompressPayloadTransformer : IAmAMessageTransformAsync + public class CompressPayloadTransformer : IAmAMessageTransform { private CompressionMethod _compressionMethod = CompressionMethod.GZip; private CompressionLevel _compressionLevel = CompressionLevel.Optimal; @@ -40,7 +40,7 @@ public void InitializeUnwrapFromAttributeParams(params object[] initializerList) _compressionMethod = (CompressionMethod)initializerList[0]; } - public async Task WrapAsync(Message message, CancellationToken cancellationToken = default) + public Message Wrap(Message message) { var bytes = message.Body.Bytes; @@ -51,7 +51,7 @@ public async Task WrapAsync(Message message, CancellationToken cancella using var output = new MemoryStream(); (Stream compressionStream, string mimeType) = CreateCompressionStream(output); - await input.CopyToAsync(compressionStream); + input.CopyTo(compressionStream); compressionStream.Close(); message.Header.ContentType = mimeType; @@ -62,7 +62,7 @@ public async Task WrapAsync(Message message, CancellationToken cancella } - public async Task UnwrapAsync(Message message, CancellationToken cancellationToken = default) + public Message Unwrap(Message message) { if (!IsCompressed(message)) return message; @@ -71,7 +71,7 @@ public async Task UnwrapAsync(Message message, CancellationToken cancel using var output = new MemoryStream(); Stream deCompressionStream = CreateDecompressionStream(input); - await deCompressionStream.CopyToAsync(output); + deCompressionStream.CopyTo(output); deCompressionStream.Close(); string contentType = (string)message.Header.Bag[ORIGINAL_CONTENTTYPE_HEADER]; diff --git a/src/Paramore.Brighter/Transforms/Transformers/CompressPayloadTransformerAsync.cs b/src/Paramore.Brighter/Transforms/Transformers/CompressPayloadTransformerAsync.cs new file mode 100644 index 0000000000..a0cbff59b0 --- /dev/null +++ b/src/Paramore.Brighter/Transforms/Transformers/CompressPayloadTransformerAsync.cs @@ -0,0 +1,146 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Net.Mime; +using System.Threading; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Transforms.Transformers +{ + public class CompressPayloadTransformerAsync : IAmAMessageTransformAsync + { + private CompressionMethod _compressionMethod = CompressionMethod.GZip; + private CompressionLevel _compressionLevel = CompressionLevel.Optimal; + private int _thresholdInBytes; + private const ushort GZIP_LEAD_BYTES = 0x8b1f; + private const byte ZLIB_LEAD_BYTE = 0x78; + + /// Compression method GZip + public const string GZIP = "application/gzip"; + /// Compression method Deflate + public const string DEFLATE = "application/deflate"; + /// Compression method Brotli + public const string BROTLI = "application/br"; + + /// Original content type header name + public const string ORIGINAL_CONTENTTYPE_HEADER = "originalContentType"; + + public void Dispose() { } + + public void InitializeWrapFromAttributeParams(params object[] initializerList) + { + _compressionMethod = (CompressionMethod)initializerList[0]; + _compressionLevel = (CompressionLevel)initializerList[1]; + _thresholdInBytes = (int)initializerList[2] * 1024; + + } + + public void InitializeUnwrapFromAttributeParams(params object[] initializerList) + { + _compressionMethod = (CompressionMethod)initializerList[0]; + } + + public async Task WrapAsync(Message message, CancellationToken cancellationToken = default) + { + var bytes = message.Body.Bytes; + + //don't transform it too small + if (bytes.Length < _thresholdInBytes) return message; + + using var input = new MemoryStream(bytes); + using var output = new MemoryStream(); + + (Stream compressionStream, string mimeType) = CreateCompressionStream(output); + await input.CopyToAsync(compressionStream); + compressionStream.Close(); + + message.Header.ContentType = mimeType; + message.Header.Bag.Add(ORIGINAL_CONTENTTYPE_HEADER, message.Body.ContentType); + message.Body = new MessageBody(output.ToArray(), mimeType, CharacterEncoding.Raw); + + return message; + } + + + public async Task UnwrapAsync(Message message, CancellationToken cancellationToken = default) + { + if (!IsCompressed(message)) return message; + + var bytes = message.Body.Bytes; + using var input = new MemoryStream(bytes); + using var output = new MemoryStream(); + + Stream deCompressionStream = CreateDecompressionStream(input); + await deCompressionStream.CopyToAsync(output); + deCompressionStream.Close(); + + string contentType = (string)message.Header.Bag[ORIGINAL_CONTENTTYPE_HEADER]; + message.Body = new MessageBody(output.ToArray(), contentType, CharacterEncoding.UTF8); + message.Header.ContentType = contentType; + + return message; + } + + private (Stream , string) CreateCompressionStream(MemoryStream uncompressed) + { + switch (_compressionMethod) + { + case CompressionMethod.GZip: + return (new GZipStream(uncompressed, _compressionLevel), GZIP); +#if NETSTANDARD2_0 + case CompressionMethod.Zlib: + throw new ArgumentException("Zlib is not supported in nestandard20"); + case CompressionMethod.Brotli: + throw new ArgumentException("Brotli is not supported in nestandard20"); +#else + case CompressionMethod.Zlib: + return (new ZLibStream(uncompressed, _compressionLevel), DEFLATE); + case CompressionMethod.Brotli: + return (new BrotliStream(uncompressed, _compressionLevel), BROTLI); +#endif + default: + return (uncompressed, "application/json"); + } + } + + private Stream CreateDecompressionStream(MemoryStream compressed) + { + switch (_compressionMethod) + { + case CompressionMethod.GZip: + return new GZipStream(compressed, CompressionMode.Decompress); + +#if NETSTANDARD2_0 + case CompressionMethod.Zlib: + throw new ArgumentException("Zlib is not supported in nestandard20"); + case CompressionMethod.Brotli: + throw new ArgumentException("Brotli is not supported in nestandard20"); +#else + case CompressionMethod.Zlib: + return new ZLibStream(compressed, CompressionMode.Decompress); + case CompressionMethod.Brotli: + return new BrotliStream(compressed, CompressionMode.Decompress); +#endif + default: + return compressed; + } + } + + private bool IsCompressed(Message message) + { + switch (_compressionMethod) + { + case CompressionMethod.GZip: + return message.Header.ContentType == "application/gzip" && message.Body.Bytes.Length >= 2 && BitConverter.ToUInt16(message.Body.Bytes, 0) == GZIP_LEAD_BYTES; + case CompressionMethod.Zlib: + return message.Header.ContentType == "application/deflate" && message.Body.Bytes[0] == ZLIB_LEAD_BYTE; + case CompressionMethod.Brotli: + return message.Header.ContentType == "application/br"; + default: + return false; + + } + } + + } +} diff --git a/tests/Paramore.Brighter.AWS.Tests/Transformers/When_unwrapping_a_large_message.cs b/tests/Paramore.Brighter.AWS.Tests/Transformers/When_unwrapping_a_large_message.cs index 15716537cc..8af6121f3a 100644 --- a/tests/Paramore.Brighter.AWS.Tests/Transformers/When_unwrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.AWS.Tests/Transformers/When_unwrapping_a_large_message.cs @@ -69,7 +69,7 @@ public LargeMessagePaylodUnwrapTests() .GetAwaiter() .GetResult(); - var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformer(_luggageStore)); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformerAsync(_luggageStore)); _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); } @@ -101,7 +101,7 @@ public async Task When_unwrapping_a_large_message() new MessageBody(JsonSerializer.Serialize(myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))) ); - message.Header.Bag[ClaimCheckTransformer.CLAIM_CHECK] = id; + message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK] = id; //act var transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); diff --git a/tests/Paramore.Brighter.AWS.Tests/Transformers/When_wrapping_a_large_message.cs b/tests/Paramore.Brighter.AWS.Tests/Transformers/When_wrapping_a_large_message.cs index 363cd63ab2..79cd70f191 100644 --- a/tests/Paramore.Brighter.AWS.Tests/Transformers/When_wrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.AWS.Tests/Transformers/When_wrapping_a_large_message.cs @@ -69,7 +69,7 @@ public LargeMessagePayloadWrapTests() .GetAwaiter() .GetResult(); - var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformer(_luggageStore)); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformerAsync(_luggageStore)); _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); } @@ -82,8 +82,8 @@ public async Task When_wrapping_a_large_message() var message = await _transformPipeline.WrapAsync(_myCommand); //assert - message.Header.Bag.ContainsKey(ClaimCheckTransformer.CLAIM_CHECK).Should().BeTrue(); - _id = (string)message.Header.Bag[ClaimCheckTransformer.CLAIM_CHECK]; + message.Header.Bag.ContainsKey(ClaimCheckTransformerAsync.CLAIM_CHECK).Should().BeTrue(); + _id = (string)message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK]; message.Body.Value.Should().Be($"Claim Check {_id}"); (await _luggageStore.HasClaimAsync(_id)).Should().BeTrue(); diff --git a/tests/Paramore.Brighter.Azure.Tests/Transformers/When_unwrapping_a_large_message.cs b/tests/Paramore.Brighter.Azure.Tests/Transformers/When_unwrapping_a_large_message.cs index 98e0202c9d..6a47274117 100644 --- a/tests/Paramore.Brighter.Azure.Tests/Transformers/When_unwrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.Azure.Tests/Transformers/When_unwrapping_a_large_message.cs @@ -37,7 +37,7 @@ public LargeMessagePaylodUnwrapTests() null); mapperRegistry.Register(); - var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformer(_luggageStore)); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformerAsync(_luggageStore)); _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); } @@ -69,7 +69,7 @@ public async Task When_unwrapping_a_large_message() new MessageBody(JsonSerializer.Serialize(myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))) ); - message.Header.Bag[ClaimCheckTransformer.CLAIM_CHECK] = id; + message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK] = id; //act var transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); diff --git a/tests/Paramore.Brighter.Azure.Tests/Transformers/When_wrapping_a_large_message.cs b/tests/Paramore.Brighter.Azure.Tests/Transformers/When_wrapping_a_large_message.cs index 81054d06b8..0d465fc815 100644 --- a/tests/Paramore.Brighter.Azure.Tests/Transformers/When_wrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.Azure.Tests/Transformers/When_wrapping_a_large_message.cs @@ -42,7 +42,7 @@ public LargeMessagePayloadWrapTests() _luggageStore = new AzureBlobLuggageStore(_bucketUrl, new AzureCliCredential()); - var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformer(_luggageStore)); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformerAsync(_luggageStore)); _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); @@ -57,8 +57,8 @@ public async Task When_wrapping_a_large_message() var message = _transformPipeline.WrapAsync(_myCommand).Result; //assert - message.Header.Bag.ContainsKey(ClaimCheckTransformer.CLAIM_CHECK).Should().BeTrue(); - _id = (string)message.Header.Bag[ClaimCheckTransformer.CLAIM_CHECK]; + message.Header.Bag.ContainsKey(ClaimCheckTransformerAsync.CLAIM_CHECK).Should().BeTrue(); + _id = (string)message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK]; message.Body.Value.Should().Be($"Claim Check {_id}"); (await _luggageStore.HasClaimAsync(_id, CancellationToken.None)).Should().BeTrue(); diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/Test Doubles/MyLargeCommandMessageMapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/Claims/Test Doubles/MyLargeCommandMessageMapperAsync.cs new file mode 100644 index 0000000000..7987067188 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Claims/Test Doubles/MyLargeCommandMessageMapperAsync.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using Paramore.Brighter.Transforms.Attributes; + +namespace Paramore.Brighter.Core.Tests.Claims.Test_Doubles; + +public class MyLargeCommandMessageMapperAsync : IAmAMessageMapperAsync +{ + [ClaimCheck(0, thresholdInKb: 5)] + public Task MapToMessage(MyLargeCommand request) + { + var tcs = new TaskCompletionSource(); + + tcs.SetResult(new Message( + new MessageHeader(request.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.General))) + )); + + return tcs.Task; + } + + [RetrieveClaim(0, retain:false)] + public async Task MapToRequest(Message message) + { + using var stream = new MemoryStream(message.Body.Bytes); + stream.Position = 0; + return await JsonSerializer.DeserializeAsync(stream); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_is_under_the_threshold.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_is_under_the_threshold.cs index 3241b66905..593960eb59 100644 --- a/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_is_under_the_threshold.cs +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_is_under_the_threshold.cs @@ -12,32 +12,30 @@ public class ClaimCheckSmallPayloadTests { private readonly ClaimCheckTransformer _transformer; private readonly Message _message; - private readonly string _body; - private readonly InMemoryStorageProviderAsync _store; public ClaimCheckSmallPayloadTests() { //arrange - _store = new InMemoryStorageProviderAsync(); - _transformer = new ClaimCheckTransformer(store: _store); + InMemoryStorageProvider store = new(); + _transformer = new ClaimCheckTransformer(store: store); //set the threshold to 5K _transformer.InitializeWrapFromAttributeParams(5); //but create a string that is just under 5K long - assuming string is 26 + length *2 to allow for 64-bit platform - _body = DataGenerator.CreateString(2485); + string body = DataGenerator.CreateString(2485); _message = new Message( new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow), - new MessageBody(_body)); + new MessageBody(body)); } [Fact] - public async Task When_a_message_is_under_the_threshold() + public void When_a_message_is_under_the_threshold() { - var luggageCheckedMessage = await _transformer.WrapAsync(_message); + var luggageCheckedMessage = _transformer.Wrap(_message); //assert - bool hasLuggage = luggageCheckedMessage.Header.Bag.TryGetValue(ClaimCheckTransformer.CLAIM_CHECK, out object _); + bool hasLuggage = luggageCheckedMessage.Header.Bag.TryGetValue(ClaimCheckTransformerAsync.CLAIM_CHECK, out object _); hasLuggage.Should().BeFalse(); } diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_is_under_the_threshold_async.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_is_under_the_threshold_async.cs new file mode 100644 index 0000000000..fefc43808a --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_is_under_the_threshold_async.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Paramore.Brighter.Transforms.Storage; +using Paramore.Brighter.Transforms.Transformers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.Claims; + +public class AsyncClaimCheckSmallPayloadTests +{ + private readonly ClaimCheckTransformerAsync _transformerAsync; + private readonly Message _message; + private readonly InMemoryStorageProviderAsync _store; + + public AsyncClaimCheckSmallPayloadTests() + { + //arrange + _store = new InMemoryStorageProviderAsync(); + _transformerAsync = new ClaimCheckTransformerAsync(store: _store); + + //set the threshold to 5K + _transformerAsync.InitializeWrapFromAttributeParams(5); + + //but create a string that is just under 5K long - assuming string is 26 + length *2 to allow for 64-bit platform + string body = DataGenerator.CreateString(2485); + _message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow), + new MessageBody(body)); + } + + [Fact] + public async Task When_a_message_is_under_the_threshold() + { + var luggageCheckedMessage = await _transformerAsync.WrapAsync(_message); + + //assert + bool hasLuggage = luggageCheckedMessage.Header.Bag.TryGetValue(ClaimCheckTransformerAsync.CLAIM_CHECK, out object _); + + hasLuggage.Should().BeFalse(); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_unwraps_a_large_payload.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_unwraps_a_large_payload.cs index 2c6c210188..b91625b3a5 100644 --- a/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_unwraps_a_large_payload.cs +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_unwraps_a_large_payload.cs @@ -11,43 +11,43 @@ namespace Paramore.Brighter.Core.Tests.Claims; public class RetrieveClaimLargePayloadTests { - private readonly InMemoryStorageProviderAsync _store; - private readonly ClaimCheckTransformer _transformer; + private readonly InMemoryStorageProvider _store; + private readonly ClaimCheckTransformer _transformerAsync; private readonly string _contents; public RetrieveClaimLargePayloadTests() { - _store = new InMemoryStorageProviderAsync(); - _transformer = new ClaimCheckTransformer(store: _store); + _store = new InMemoryStorageProvider(); + _transformerAsync = new ClaimCheckTransformer(store: _store); //delete the luggage from the store after claiming it - _transformer.InitializeUnwrapFromAttributeParams(false); + _transformerAsync.InitializeUnwrapFromAttributeParams(false); _contents = DataGenerator.CreateString(6000); } [Fact] - public async Task When_a_message_unwraps_a_large_payload() + public void When_a_message_unwraps_a_large_payload() { //arrange var stream = new MemoryStream(); var writer = new StreamWriter(stream); - await writer.WriteAsync(_contents); - await writer.FlushAsync(); + writer.Write(_contents); + writer.Flush(); stream.Position = 0; - var id = await _store.StoreAsync(stream); + var id = _store.Store(stream); var message = new Message( new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow), new MessageBody("Claim Check {id}")); - message.Header.Bag[ClaimCheckTransformer.CLAIM_CHECK] = id; + message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK] = id; //act - var unwrappedMessage = await _transformer.UnwrapAsync(message); + var unwrappedMessage = _transformerAsync.Unwrap(message); //assert unwrappedMessage.Body.Value.Should().Be(_contents); //clean up - message.Header.Bag.TryGetValue(ClaimCheckTransformer.CLAIM_CHECK, out object _).Should().BeFalse(); - (await _store.HasClaimAsync(id)).Should().BeFalse(); + message.Header.Bag.TryGetValue(ClaimCheckTransformerAsync.CLAIM_CHECK, out object _).Should().BeFalse(); + _store.HasClaim(id).Should().BeFalse(); } } diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_unwraps_a_large_payload_async.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_unwraps_a_large_payload_async.cs new file mode 100644 index 0000000000..4a7f91c5aa --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_unwraps_a_large_payload_async.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Paramore.Brighter.Transforms.Storage; +using Paramore.Brighter.Transforms.Transformers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.Claims; + +public class AsyncRetrieveClaimLargePayloadTests +{ + private readonly InMemoryStorageProviderAsync _store; + private readonly ClaimCheckTransformerAsync _transformerAsync; + private readonly string _contents; + + public AsyncRetrieveClaimLargePayloadTests() + { + _store = new InMemoryStorageProviderAsync(); + _transformerAsync = new ClaimCheckTransformerAsync(store: _store); + //delete the luggage from the store after claiming it + _transformerAsync.InitializeUnwrapFromAttributeParams(false); + _contents = DataGenerator.CreateString(6000); + } + + [Fact] + public async Task When_a_message_unwraps_a_large_payload() + { + //arrange + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + await writer.WriteAsync(_contents); + await writer.FlushAsync(); + stream.Position = 0; + + var id = await _store.StoreAsync(stream); + + var message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow), + new MessageBody("Claim Check {id}")); + message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK] = id; + + //act + var unwrappedMessage = await _transformerAsync.UnwrapAsync(message); + + //assert + unwrappedMessage.Body.Value.Should().Be(_contents); + //clean up + message.Header.Bag.TryGetValue(ClaimCheckTransformerAsync.CLAIM_CHECK, out object _).Should().BeFalse(); + (await _store.HasClaimAsync(id)).Should().BeFalse(); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_wraps_a_large_payload.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_wraps_a_large_payload.cs index 1c3c0ac179..d97a560f88 100644 --- a/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_wraps_a_large_payload.cs +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_wraps_a_large_payload.cs @@ -14,12 +14,12 @@ public class ClaimCheckLargePayloadTests private readonly ClaimCheckTransformer _transformer; private readonly Message _message; private readonly string _body; - private readonly InMemoryStorageProviderAsync _store; + private readonly InMemoryStorageProvider _store; public ClaimCheckLargePayloadTests() { //arrange - _store = new InMemoryStorageProviderAsync(); + _store = new InMemoryStorageProvider(); _transformer = new ClaimCheckTransformer(store: _store); _transformer.InitializeWrapFromAttributeParams(5); @@ -30,10 +30,10 @@ public ClaimCheckLargePayloadTests() } [Fact] - public async Task When_a_message_wraps_a_large_payload() + public void When_a_message_wraps_a_large_payload() { //act - var luggageCheckedMessage = await _transformer.WrapAsync(_message); + var luggageCheckedMessage = _transformer.Wrap(_message); //assert bool hasLuggage = luggageCheckedMessage.Header.Bag.TryGetValue(ClaimCheckTransformer.CLAIM_CHECK, out object storedData); @@ -42,7 +42,7 @@ public async Task When_a_message_wraps_a_large_payload() var claimCheck = (string)storedData; - var luggage = await new StreamReader(await _store.RetrieveAsync(claimCheck)).ReadToEndAsync(); + var luggage = new StreamReader(_store.Retrieve(claimCheck)).ReadToEnd(); luggage.Should().Be(_body); } diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_wraps_a_large_payload_async.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_wraps_a_large_payload_async.cs new file mode 100644 index 0000000000..eab93c24cf --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_a_message_wraps_a_large_payload_async.cs @@ -0,0 +1,49 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Paramore.Brighter.Transforms.Storage; +using Paramore.Brighter.Transforms.Transformers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.Claims; + +public class AsyncClaimCheckLargePayloadTests +{ + private readonly ClaimCheckTransformerAsync _transformerAsync; + private readonly Message _message; + private readonly string _body; + private readonly InMemoryStorageProviderAsync _store; + + public AsyncClaimCheckLargePayloadTests() + { + //arrange + _store = new InMemoryStorageProviderAsync(); + _transformerAsync = new ClaimCheckTransformerAsync(store: _store); + _transformerAsync.InitializeWrapFromAttributeParams(5); + + _body = DataGenerator.CreateString(6000); + _message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow), + new MessageBody(_body)); + } + + [Fact] + public async Task When_a_message_wraps_a_large_payload() + { + //act + var luggageCheckedMessage = await _transformerAsync.WrapAsync(_message); + + //assert + bool hasLuggage = luggageCheckedMessage.Header.Bag.TryGetValue(ClaimCheckTransformerAsync.CLAIM_CHECK, out object storedData); + + hasLuggage.Should().BeTrue(); + + var claimCheck = (string)storedData; + + var luggage = await new StreamReader(await _store.RetrieveAsync(claimCheck)).ReadToEndAsync(); + + luggage.Should().Be(_body); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_luggage_should_be_kept_in_the_store.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_luggage_should_be_kept_in_the_store.cs index 1138c45d1b..0690a6a17f 100644 --- a/tests/Paramore.Brighter.Core.Tests/Claims/When_luggage_should_be_kept_in_the_store.cs +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_luggage_should_be_kept_in_the_store.cs @@ -11,13 +11,13 @@ namespace Paramore.Brighter.Core.Tests.Claims; public class RetrieveClaimLeaveLuggage { - private readonly InMemoryStorageProviderAsync _store; + private readonly InMemoryStorageProvider _store; private readonly ClaimCheckTransformer _transformer; private readonly string _contents; public RetrieveClaimLeaveLuggage() { - _store = new InMemoryStorageProviderAsync(); + _store = new InMemoryStorageProvider(); _transformer = new ClaimCheckTransformer(store: _store); _transformer.InitializeUnwrapFromAttributeParams(true); @@ -25,28 +25,28 @@ public RetrieveClaimLeaveLuggage() } [Fact] - public async Task When_luggage_should_be_kept_in_the_store() + public void When_luggage_should_be_kept_in_the_store() { //arrange var stream = new MemoryStream(); var writer = new StreamWriter(stream); - await writer.WriteAsync(_contents); - await writer.FlushAsync(); + writer.WriteAsync(_contents); + writer.FlushAsync(); stream.Position = 0; - var id = await _store.StoreAsync(stream); + var id = _store.Store(stream); var message = new Message( new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow), new MessageBody("Claim Check {id}")); - message.Header.Bag[ClaimCheckTransformer.CLAIM_CHECK] = id; + message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK] = id; //act - var unwrappedMessage = await _transformer.UnwrapAsync(message); + var unwrappedMessage = _transformer.Unwrap(message); //assert - message.Header.Bag.TryGetValue(ClaimCheckTransformer.CLAIM_CHECK, out object _).Should().BeTrue(); - (await _store.HasClaimAsync(id)).Should().BeTrue(); + message.Header.Bag.TryGetValue(ClaimCheckTransformerAsync.CLAIM_CHECK, out object _).Should().BeTrue(); + _store.HasClaim(id).Should().BeTrue(); } } diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_luggage_should_be_kept_in_the_store_async.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_luggage_should_be_kept_in_the_store_async.cs new file mode 100644 index 0000000000..e429ca243a --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_luggage_should_be_kept_in_the_store_async.cs @@ -0,0 +1,52 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Paramore.Brighter.Transforms.Storage; +using Paramore.Brighter.Transforms.Transformers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.Claims; + +public class AsyncRetrieveClaimLeaveLuggage +{ + private readonly InMemoryStorageProviderAsync _store; + private readonly ClaimCheckTransformerAsync _transformerAsync; + private readonly string _contents; + + public AsyncRetrieveClaimLeaveLuggage() + { + _store = new InMemoryStorageProviderAsync(); + _transformerAsync = new ClaimCheckTransformerAsync(store: _store); + _transformerAsync.InitializeUnwrapFromAttributeParams(true); + + _contents = DataGenerator.CreateString(6000); + } + + [Fact] + public async Task When_luggage_should_be_kept_in_the_store() + { + //arrange + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + await writer.WriteAsync(_contents); + await writer.FlushAsync(); + stream.Position = 0; + + var id = await _store.StoreAsync(stream); + + var message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow), + new MessageBody("Claim Check {id}")); + message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK] = id; + + //act + var unwrappedMessage = await _transformerAsync.UnwrapAsync(message); + + //assert + message.Header.Bag.TryGetValue(ClaimCheckTransformerAsync.CLAIM_CHECK, out object _).Should().BeTrue(); + (await _store.HasClaimAsync(id)).Should().BeTrue(); + + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message.cs index ada15e8dd4..11e59da844 100644 --- a/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message.cs @@ -13,8 +13,8 @@ namespace Paramore.Brighter.Core.Tests.Claims; public class LargeMessagePaylodUnwrapTests { - private readonly TransformPipelineBuilderAsync _pipelineBuilder; - private readonly InMemoryStorageProviderAsync _inMemoryStorageProviderAsync; + private readonly TransformPipelineBuilder _pipelineBuilder; + private readonly InMemoryStorageProvider _inMemoryStorageProvider; public LargeMessagePaylodUnwrapTests() { @@ -26,14 +26,14 @@ public LargeMessagePaylodUnwrapTests() null); mapperRegistry.Register(); - _inMemoryStorageProviderAsync = new InMemoryStorageProviderAsync(); - var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformer(_inMemoryStorageProviderAsync)); + _inMemoryStorageProvider = new InMemoryStorageProvider(); + var messageTransformerFactory = new SimpleMessageTransformerFactory(_ => new ClaimCheckTransformer(_inMemoryStorageProvider)); - _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); } [Fact] - public async Task When_unwrapping_a_large_message() + public void When_unwrapping_a_large_message() { //arrange //store our luggage and get the claim check @@ -43,10 +43,10 @@ public async Task When_unwrapping_a_large_message() var stream = new MemoryStream(); var writer = new StreamWriter(stream); - await writer.WriteAsync(commandAsJson); - await writer.FlushAsync(); + writer.Write(commandAsJson); + writer.Flush(); stream.Position = 0; - var id = await _inMemoryStorageProviderAsync.StoreAsync(stream); + var id = _inMemoryStorageProvider.Store(stream); //pretend we ran through the claim check myCommand.Value = $"Claim Check {id}"; @@ -57,15 +57,15 @@ public async Task When_unwrapping_a_large_message() new MessageBody(JsonSerializer.Serialize(myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))) ); - message.Header.Bag[ClaimCheckTransformer.CLAIM_CHECK] = id; + message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK] = id; //act var transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); - var transformedMessage = await transformPipeline.UnwrapAsync(message); + var transformedMessage = transformPipeline.Unwrap(message); //assert //contents should be from storage transformedMessage.Value.Should().Be(contents); - (await _inMemoryStorageProviderAsync.HasClaimAsync(id)).Should().BeFalse(); + _inMemoryStorageProvider.HasClaim(id).Should().BeFalse(); } } diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message_async.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message_async.cs new file mode 100644 index 0000000000..35348badfa --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_unwrapping_a_large_message_async.cs @@ -0,0 +1,72 @@ +using System; +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.Claims.Test_Doubles; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Paramore.Brighter.Transforms.Storage; +using Paramore.Brighter.Transforms.Transformers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.Claims; + +public class AsyncLargeMessagePaylodUnwrapTests +{ + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private readonly InMemoryStorageProviderAsync _inMemoryStorageProviderAsync; + + public AsyncLargeMessagePaylodUnwrapTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyLargeCommandMessageMapperAsync()) + ); + mapperRegistry.RegisterAsync(); + + _inMemoryStorageProviderAsync = new InMemoryStorageProviderAsync(); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync(_ => new ClaimCheckTransformerAsync(_inMemoryStorageProviderAsync)); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + } + + [Fact] + public async Task When_unwrapping_a_large_message() + { + //arrange + //store our luggage and get the claim check + var contents = DataGenerator.CreateString(6000); + var myCommand = new MyLargeCommand(1) { Value = contents }; + var commandAsJson = JsonSerializer.Serialize(myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General)); + + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + await writer.WriteAsync(commandAsJson); + await writer.FlushAsync(); + stream.Position = 0; + var id = await _inMemoryStorageProviderAsync.StoreAsync(stream); + + //pretend we ran through the claim check + myCommand.Value = $"Claim Check {id}"; + + //set the headers, so that we have a claim check listed + var message = new Message( + new MessageHeader(myCommand.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))) + ); + + message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK] = id; + + //act + var transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); + var transformedMessage = await transformPipeline.UnwrapAsync(message); + + //assert + //contents should be from storage + transformedMessage.Value.Should().Be(contents); + (await _inMemoryStorageProviderAsync.HasClaimAsync(id)).Should().BeFalse(); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message.cs index 641179ef2b..1449cbe96f 100644 --- a/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message.cs +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message.cs @@ -10,10 +10,10 @@ namespace Paramore.Brighter.Core.Tests.Claims; public class LargeMessagePayloadWrapTests { - private WrapPipelineAsync _transformPipeline; - private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private WrapPipeline _transformPipeline; + private readonly TransformPipelineBuilder _pipelineBuilder; private readonly MyLargeCommand _myCommand; - private InMemoryStorageProviderAsync _inMemoryStorageProviderAsync; + private InMemoryStorageProvider _inMemoryStorageProvider; public LargeMessagePayloadWrapTests() { @@ -27,25 +27,25 @@ public LargeMessagePayloadWrapTests() _myCommand = new MyLargeCommand(6000); - _inMemoryStorageProviderAsync = new InMemoryStorageProviderAsync(); - var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync( - _ => new ClaimCheckTransformer(_inMemoryStorageProviderAsync)); + _inMemoryStorageProvider = new InMemoryStorageProvider(); + var messageTransformerFactory = new SimpleMessageTransformerFactory( + _ => new ClaimCheckTransformer(_inMemoryStorageProvider)); - _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); } [Fact] - public async Task When_wrapping_a_large_message() + public void When_wrapping_a_large_message() { //act _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); - var message = await _transformPipeline.WrapAsync(_myCommand); + var message = _transformPipeline.Wrap(_myCommand); //assert - message.Header.Bag.ContainsKey(ClaimCheckTransformer.CLAIM_CHECK).Should().BeTrue(); - var id = (string) message.Header.Bag[ClaimCheckTransformer.CLAIM_CHECK]; + message.Header.Bag.ContainsKey(ClaimCheckTransformerAsync.CLAIM_CHECK).Should().BeTrue(); + var id = (string) message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK]; message.Body.Value.Should().Be($"Claim Check {id}"); - (await _inMemoryStorageProviderAsync.HasClaimAsync(id)).Should().BeTrue(); + _inMemoryStorageProvider.HasClaim(id).Should().BeTrue(); } } diff --git a/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message_async.cs b/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message_async.cs new file mode 100644 index 0000000000..fbeeb3e20c --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Claims/When_wrapping_a_large_message_async.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.Claims.Test_Doubles; +using Paramore.Brighter.Transforms.Storage; +using Paramore.Brighter.Transforms.Transformers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.Claims; + +public class AsyncLargeMessagePayloadWrapTests +{ + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + private readonly MyLargeCommand _myCommand; + private InMemoryStorageProviderAsync _inMemoryStorageProviderAsync; + + public AsyncLargeMessagePayloadWrapTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyLargeCommandMessageMapperAsync())); + mapperRegistry.RegisterAsync(); + + _myCommand = new MyLargeCommand(6000); + + _inMemoryStorageProviderAsync = new InMemoryStorageProviderAsync(); + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync( + _ => new ClaimCheckTransformerAsync(_inMemoryStorageProviderAsync)); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + } + + [Fact] + public async Task When_wrapping_a_large_message() + { + //act + _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); + var message = await _transformPipeline.WrapAsync(_myCommand); + + //assert + message.Header.Bag.ContainsKey(ClaimCheckTransformerAsync.CLAIM_CHECK).Should().BeTrue(); + var id = (string) message.Header.Bag[ClaimCheckTransformerAsync.CLAIM_CHECK]; + message.Body.Value.Should().Be($"Claim Check {id}"); + (await _inMemoryStorageProviderAsync.HasClaimAsync(id)).Should().BeTrue(); + + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/TestDoubles/MyCommandMessageMapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/TestDoubles/MyCommandMessageMapperAsync.cs new file mode 100644 index 0000000000..9801587e26 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/TestDoubles/MyCommandMessageMapperAsync.cs @@ -0,0 +1,51 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Core.Tests.CommandProcessors.TestDoubles +{ + internal class MyCommandMessageMapperAsync : IAmAMessageMapperAsync + { + public Task MapToMessage(MyCommand request) + { + var tcs = new TaskCompletionSource(); + var header = new MessageHeader(request.Id, "MyCommand", MessageType.MT_COMMAND); + var body = new MessageBody(JsonSerializer.Serialize(request, JsonSerialisationOptions.Options)); + var message = new Message(header, body); + tcs.SetResult(message); + return tcs.Task; + } + + public async Task MapToRequest(Message message) + { + using var stream = new MemoryStream(message.Body.Bytes); + stream.Position = 0; + var command = await JsonSerializer.DeserializeAsync(stream, JsonSerialisationOptions.Options); + return command; + } + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs index 90291afd4e..313a64504e 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Depositing_A_Message_In_The_Message_StoreAsync_Bulk.cs @@ -53,17 +53,19 @@ public CommandProcessorBulkDepositPostTestsAsync() new MessageBody(JsonSerializer.Serialize(_myEvent, JsonSerialisationOptions.Options)) ); - var messageMapperRegistry = new MessageMapperRegistry(new SimpleMessageMapperFactory((type) => + var messageMapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync((type) => { - if (type.Equals(typeof(MyCommandMessageMapper))) - return new MyCommandMessageMapper(); + if (type == typeof(MyCommandMessageMapperAsync)) + return new MyCommandMessageMapperAsync(); else { - return new MyEventMessageMapper(); + return new MyEventMessageMapperAsync(); } - }), null); - messageMapperRegistry.Register(); - messageMapperRegistry.Register(); + })); + messageMapperRegistry.RegisterAsync(); + messageMapperRegistry.RegisterAsync(); var retryPolicy = Policy .Handle() diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs index 3942a97ab4..1ee22bdff4 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry.cs @@ -40,7 +40,6 @@ public class CommandProcessorNoMessageMapperTests : IDisposable { private readonly CommandProcessor _commandProcessor; private readonly MyCommand _myCommand = new MyCommand(); - private Message _message; private readonly FakeOutbox _fakeOutbox; private readonly FakeMessageProducerWithPublishConfirmation _fakeMessageProducerWithPublishConfirmation; private Exception _exception; @@ -53,10 +52,6 @@ public CommandProcessorNoMessageMapperTests() _fakeMessageProducerWithPublishConfirmation = new FakeMessageProducerWithPublishConfirmation(); const string topic = "MyCommand"; - _message = new Message( - new MessageHeader(_myCommand.Id, topic, MessageType.MT_COMMAND), - new MessageBody(JsonSerializer.Serialize(_myCommand, JsonSerialisationOptions.Options)) - ); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory((_) => new MyCommandMessageMapper()), @@ -91,6 +86,7 @@ public CommandProcessorNoMessageMapperTests() messageMapperRegistry); } + [Fact] public void When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry() { _exception = Catch.Exception(() => _commandProcessor.Post(_myCommand)); diff --git a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs index 307d87c059..64e385a6b8 100644 --- a/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/CommandProcessors/When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry_Async.cs @@ -88,7 +88,7 @@ public async Task When_Posting_A_Message_And_There_Is_No_Message_Mapper_Registry _exception = await Catch.ExceptionAsync(async () => await _commandProcessor.PostAsync(_myCommand)); //_should_throw_an_exception - _exception.Should().BeOfType(); + _exception.Should().BeOfType(); } public void Dispose() diff --git a/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_compresses_a_large_payload.cs b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_compresses_a_large_payload.cs index c5f72c7cae..635f26836f 100644 --- a/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_compresses_a_large_payload.cs +++ b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_compresses_a_large_payload.cs @@ -27,10 +27,10 @@ public CompressLargePayloadTests() } [Fact] - public async Task When_a_message_gzip_compresses_a_large_payload() + public void When_a_message_gzip_compresses_a_large_payload() { _transformer.InitializeWrapFromAttributeParams(CompressionMethod.GZip, CompressionLevel.Optimal, 5); - var compressedMessage = await _transformer.WrapAsync(_message); + var compressedMessage = _transformer.Wrap(_message); //look for gzip in the bytes compressedMessage.Body.Bytes.Should().NotBeNull(); @@ -38,18 +38,18 @@ public async Task When_a_message_gzip_compresses_a_large_payload() BitConverter.ToUInt16(compressedMessage.Body.Bytes, 0).Should().Be(GZIP_LEAD_BYTES); //mime types - compressedMessage.Header.ContentType.Should().Be(CompressPayloadTransformer.GZIP); - compressedMessage.Header.Bag[CompressPayloadTransformer.ORIGINAL_CONTENTTYPE_HEADER].Should().Be(MessageBody.APPLICATION_JSON); - compressedMessage.Body.ContentType.Should().Be(CompressPayloadTransformer.GZIP); + compressedMessage.Header.ContentType.Should().Be(CompressPayloadTransformerAsync.GZIP); + compressedMessage.Header.Bag[CompressPayloadTransformerAsync.ORIGINAL_CONTENTTYPE_HEADER].Should().Be(MessageBody.APPLICATION_JSON); + compressedMessage.Body.ContentType.Should().Be(CompressPayloadTransformerAsync.GZIP); } [Fact] - public async Task When_a_message_zlib_compresses_a_large_payload() + public void When_a_message_zlib_compresses_a_large_payload() { _transformer.InitializeWrapFromAttributeParams(CompressionMethod.Zlib, CompressionLevel.Optimal, 5); - var compressedMessage = await _transformer.WrapAsync(_message); + var compressedMessage = _transformer.Wrap(_message); //look for gzip in the bytes compressedMessage.Body.Bytes.Should().NotBeNull(); @@ -58,16 +58,16 @@ public async Task When_a_message_zlib_compresses_a_large_payload() compressedMessage.Body.Bytes[0].Should().Be(ZLIB_LEAD_BYTE); //mime types - compressedMessage.Header.ContentType.Should().Be(CompressPayloadTransformer.DEFLATE); - compressedMessage.Header.Bag[CompressPayloadTransformer.ORIGINAL_CONTENTTYPE_HEADER].Should().Be(MessageBody.APPLICATION_JSON); - compressedMessage.Body.ContentType.Should().Be(CompressPayloadTransformer.DEFLATE); + compressedMessage.Header.ContentType.Should().Be(CompressPayloadTransformerAsync.DEFLATE); + compressedMessage.Header.Bag[CompressPayloadTransformerAsync.ORIGINAL_CONTENTTYPE_HEADER].Should().Be(MessageBody.APPLICATION_JSON); + compressedMessage.Body.ContentType.Should().Be(CompressPayloadTransformerAsync.DEFLATE); } [Fact] - public async Task When_a_message_brotli_compresses_a_large_payload() + public void When_a_message_brotli_compresses_a_large_payload() { _transformer.InitializeWrapFromAttributeParams(CompressionMethod.Brotli, CompressionLevel.Optimal, 5); - var compressedMessage = await _transformer.WrapAsync(_message); + var compressedMessage = _transformer.Wrap(_message); //look for gzip in the bytes compressedMessage.Body.Bytes.Should().NotBeNull(); @@ -75,9 +75,9 @@ public async Task When_a_message_brotli_compresses_a_large_payload() compressedMessage.Body.ContentType.Should().Be("application/br"); //mime types - compressedMessage.Header.ContentType.Should().Be(CompressPayloadTransformer.BROTLI); - compressedMessage.Header.Bag[CompressPayloadTransformer.ORIGINAL_CONTENTTYPE_HEADER].Should().Be(MessageBody.APPLICATION_JSON); - compressedMessage.Body.ContentType.Should().Be(CompressPayloadTransformer.BROTLI); + compressedMessage.Header.ContentType.Should().Be(CompressPayloadTransformerAsync.BROTLI); + compressedMessage.Header.Bag[CompressPayloadTransformerAsync.ORIGINAL_CONTENTTYPE_HEADER].Should().Be(MessageBody.APPLICATION_JSON); + compressedMessage.Body.ContentType.Should().Be(CompressPayloadTransformerAsync.BROTLI); } } diff --git a/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_compresses_a_large_payload_async.cs b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_compresses_a_large_payload_async.cs new file mode 100644 index 0000000000..23e233fbda --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_compresses_a_large_payload_async.cs @@ -0,0 +1,83 @@ +using System; +using System.IO.Compression; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Paramore.Brighter.Transforms.Transformers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.Compression; + +public class AsyncCompressLargePayloadTests +{ + private readonly CompressPayloadTransformerAsync _transformer; + private readonly string _body; + private readonly Message _message; + private const ushort GZIP_LEAD_BYTES = 0x8b1f; + private const byte ZLIB_LEAD_BYTE = 0x78; + + public AsyncCompressLargePayloadTests() + { + _transformer = new CompressPayloadTransformerAsync(); + + _body = DataGenerator.CreateString(6000); + _message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow), + new MessageBody(_body, MessageBody.APPLICATION_JSON, CharacterEncoding.UTF8)); + } + + [Fact] + public async Task When_a_message_gzip_compresses_a_large_payload() + { + _transformer.InitializeWrapFromAttributeParams(CompressionMethod.GZip, CompressionLevel.Optimal, 5); + var compressedMessage = await _transformer.WrapAsync(_message); + + //look for gzip in the bytes + compressedMessage.Body.Bytes.Should().NotBeNull(); + compressedMessage.Body.Bytes.Length.Should().BeGreaterThanOrEqualTo(2); + BitConverter.ToUInt16(compressedMessage.Body.Bytes, 0).Should().Be(GZIP_LEAD_BYTES); + + //mime types + compressedMessage.Header.ContentType.Should().Be(CompressPayloadTransformerAsync.GZIP); + compressedMessage.Header.Bag[CompressPayloadTransformerAsync.ORIGINAL_CONTENTTYPE_HEADER].Should().Be(MessageBody.APPLICATION_JSON); + compressedMessage.Body.ContentType.Should().Be(CompressPayloadTransformerAsync.GZIP); + + + } + + [Fact] + public async Task When_a_message_zlib_compresses_a_large_payload() + { + _transformer.InitializeWrapFromAttributeParams(CompressionMethod.Zlib, CompressionLevel.Optimal, 5); + var compressedMessage = await _transformer.WrapAsync(_message); + + //look for gzip in the bytes + compressedMessage.Body.Bytes.Should().NotBeNull(); + compressedMessage.Body.Bytes.Length.Should().BeGreaterThanOrEqualTo(2); + compressedMessage.Body.ContentType.Should().Be("application/deflate"); + compressedMessage.Body.Bytes[0].Should().Be(ZLIB_LEAD_BYTE); + + //mime types + compressedMessage.Header.ContentType.Should().Be(CompressPayloadTransformerAsync.DEFLATE); + compressedMessage.Header.Bag[CompressPayloadTransformerAsync.ORIGINAL_CONTENTTYPE_HEADER].Should().Be(MessageBody.APPLICATION_JSON); + compressedMessage.Body.ContentType.Should().Be(CompressPayloadTransformerAsync.DEFLATE); + } + + [Fact] + public async Task When_a_message_brotli_compresses_a_large_payload() + { + _transformer.InitializeWrapFromAttributeParams(CompressionMethod.Brotli, CompressionLevel.Optimal, 5); + var compressedMessage = await _transformer.WrapAsync(_message); + + //look for gzip in the bytes + compressedMessage.Body.Bytes.Should().NotBeNull(); + compressedMessage.Body.Bytes.Length.Should().BeGreaterThanOrEqualTo(2); + compressedMessage.Body.ContentType.Should().Be("application/br"); + + //mime types + compressedMessage.Header.ContentType.Should().Be(CompressPayloadTransformerAsync.BROTLI); + compressedMessage.Header.Bag[CompressPayloadTransformerAsync.ORIGINAL_CONTENTTYPE_HEADER].Should().Be(MessageBody.APPLICATION_JSON); + compressedMessage.Body.ContentType.Should().Be(CompressPayloadTransformerAsync.BROTLI); + + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_not_compressed.cs b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_not_compressed.cs index 4c0f5722ef..35d1a91c80 100644 --- a/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_not_compressed.cs +++ b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_not_compressed.cs @@ -10,7 +10,7 @@ public class UncompressedPayloadTests { [Fact] - public async Task When_a_message_is_not_gzip_compressed() + public void When_a_message_is_not_gzip_compressed() { //arrange @@ -26,14 +26,14 @@ public async Task When_a_message_is_not_gzip_compressed() new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: mimeType),body); //act - var msg = await transformer.UnwrapAsync(message); + var msg = transformer.Unwrap(message); //assert msg.Body.Value.Should().Be(smallContent); } [Fact] - public async Task When_a_message_is_not_zlib_compressed() + public void When_a_message_is_not_zlib_compressed() { //arrange @@ -49,14 +49,14 @@ public async Task When_a_message_is_not_zlib_compressed() new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: mimeType),body); //act - var msg = await transformer.UnwrapAsync(message); + var msg = transformer.Unwrap(message); //assert msg.Body.Value.Should().Be(smallContent); } [Fact] - public async Task When_a_message_is_not_brotli_compressed() + public void When_a_message_is_not_brotli_compressed() { //arrange @@ -72,7 +72,7 @@ public async Task When_a_message_is_not_brotli_compressed() new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: mimeType),body); //act - var msg = await transformer.UnwrapAsync(message); + var msg = transformer.Unwrap(message); //assert msg.Body.Value.Should().Be(smallContent); diff --git a/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_not_compressed_async.cs b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_not_compressed_async.cs new file mode 100644 index 0000000000..8912e5ceb7 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_not_compressed_async.cs @@ -0,0 +1,80 @@ +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Transforms.Transformers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.Compression; + +public class AsyncUncompressedPayloadTests +{ + + [Fact] + public async Task When_a_message_is_not_gzip_compressed() + { + + //arrange + var transformer = new CompressPayloadTransformerAsync(); + transformer.InitializeUnwrapFromAttributeParams(CompressionMethod.GZip); + + var smallContent = "small message"; + string mimeType = MessageBody.APPLICATION_JSON; + + var body = new MessageBody(smallContent, mimeType); + + var message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: mimeType),body); + + //act + var msg = await transformer.UnwrapAsync(message); + + //assert + msg.Body.Value.Should().Be(smallContent); + } + + [Fact] + public async Task When_a_message_is_not_zlib_compressed() + { + + //arrange + var transformer = new CompressPayloadTransformerAsync(); + transformer.InitializeUnwrapFromAttributeParams(CompressionMethod.Zlib); + + var smallContent = "small message"; + string mimeType = MessageBody.APPLICATION_JSON; + + var body = new MessageBody(smallContent, mimeType); + + var message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: mimeType),body); + + //act + var msg = await transformer.UnwrapAsync(message); + + //assert + msg.Body.Value.Should().Be(smallContent); + } + + [Fact] + public async Task When_a_message_is_not_brotli_compressed() + { + + //arrange + var transformer = new CompressPayloadTransformerAsync(); + transformer.InitializeUnwrapFromAttributeParams(CompressionMethod.Brotli); + + var smallContent = "small message"; + string mimeType = MessageBody.APPLICATION_JSON; + + var body = new MessageBody(smallContent, mimeType); + + var message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: mimeType),body); + + //act + var msg = await transformer.UnwrapAsync(message); + + //assert + msg.Body.Value.Should().Be(smallContent); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_under_the_threshold.cs b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_under_the_threshold.cs index 8824c1d6f3..ba9b76beba 100644 --- a/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_under_the_threshold.cs +++ b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_under_the_threshold.cs @@ -29,9 +29,9 @@ public SmallPayloadNotCompressedTests() [Fact] - public async Task When_a_message_is_under_the_threshold() + public void When_a_message_is_under_the_threshold() { - var uncompressedMessage = await _transformer.WrapAsync(_message); + var uncompressedMessage = _transformer.Wrap(_message); //look for gzip in the bytes uncompressedMessage.Body.ContentType.Should().Be(MessageBody.APPLICATION_JSON); diff --git a/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_under_the_threshold_async.cs b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_under_the_threshold_async.cs new file mode 100644 index 0000000000..c1816c9318 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Compression/When_a_message_is_under_the_threshold_async.cs @@ -0,0 +1,39 @@ +using System; +using System.IO.Compression; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Transforms.Transformers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.Compression; + +public class AsyncSmallPayloadNotCompressedTests +{ + private readonly CompressPayloadTransformerAsync _transformer; + private readonly string _body; + private readonly Message _message; + private const ushort GZIP_LEAD_BYTES = 0x8b1f; + + + public AsyncSmallPayloadNotCompressedTests() + { + _transformer = new CompressPayloadTransformerAsync(); + _transformer.InitializeWrapFromAttributeParams(CompressionMethod.GZip, CompressionLevel.Optimal, 5); + + _body = "small message"; + _message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: MessageBody.APPLICATION_JSON), + new MessageBody(_body, MessageBody.APPLICATION_JSON, CharacterEncoding.UTF8)); + } + + + [Fact] + public async Task When_a_message_is_under_the_threshold() + { + var uncompressedMessage = await _transformer.WrapAsync(_message); + + //look for gzip in the bytes + uncompressedMessage.Body.ContentType.Should().Be(MessageBody.APPLICATION_JSON); + uncompressedMessage.Body.Value.Should().Be(_message.Body.Value); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/Compression/When_decompressing_a_large_payload_in_a_message.cs b/tests/Paramore.Brighter.Core.Tests/Compression/When_decompressing_a_large_payload_in_a_message.cs index bdd415e2fd..cd71ca75e9 100644 --- a/tests/Paramore.Brighter.Core.Tests/Compression/When_decompressing_a_large_payload_in_a_message.cs +++ b/tests/Paramore.Brighter.Core.Tests/Compression/When_decompressing_a_large_payload_in_a_message.cs @@ -13,7 +13,7 @@ namespace Paramore.Brighter.Core.Tests.Compression; public class UncompressLargePayloadTests { [Fact] - public async Task When_decompressing_a_large_gzip_payload_in_a_message() + public void When_decompressing_a_large_gzip_payload_in_a_message() { //arrange var transformer = new CompressPayloadTransformer(); @@ -26,9 +26,9 @@ public async Task When_decompressing_a_large_gzip_payload_in_a_message() Stream compressionStream = new GZipStream(output, CompressionLevel.Optimal); - string mimeType = CompressPayloadTransformer.GZIP; - await input.CopyToAsync(compressionStream); - await compressionStream.FlushAsync(); + string mimeType = CompressPayloadTransformerAsync.GZIP; + input.CopyToAsync(compressionStream); + compressionStream.FlushAsync(); var body = new MessageBody(output.ToArray(), mimeType); @@ -38,7 +38,7 @@ public async Task When_decompressing_a_large_gzip_payload_in_a_message() message.Header.Bag[CompressPayloadTransformer.ORIGINAL_CONTENTTYPE_HEADER] = MessageBody.APPLICATION_JSON; //act - var msg = await transformer.UnwrapAsync(message); + var msg = transformer.Unwrap(message); //assert msg.Body.Value.Should().Be(largeContent); @@ -48,7 +48,7 @@ public async Task When_decompressing_a_large_gzip_payload_in_a_message() } [Fact] - public async Task When_decompressing_a_large_zlib_payload_in_a_message() + public void When_decompressing_a_large_zlib_payload_in_a_message() { //arrange var transformer = new CompressPayloadTransformer(); @@ -61,9 +61,9 @@ public async Task When_decompressing_a_large_zlib_payload_in_a_message() Stream compressionStream = new ZLibStream(output, CompressionLevel.Optimal); - string mimeType = CompressPayloadTransformer.DEFLATE; - await input.CopyToAsync(compressionStream); - await compressionStream.FlushAsync(); + string mimeType = CompressPayloadTransformerAsync.DEFLATE; + input.CopyToAsync(compressionStream); + compressionStream.FlushAsync(); var body = new MessageBody(output.ToArray(), mimeType); @@ -73,7 +73,7 @@ public async Task When_decompressing_a_large_zlib_payload_in_a_message() message.Header.Bag[CompressPayloadTransformer.ORIGINAL_CONTENTTYPE_HEADER] = MessageBody.APPLICATION_JSON; //act - var msg = await transformer.UnwrapAsync(message); + var msg = transformer.Unwrap(message); //assert msg.Body.Value.Should().Be(largeContent); @@ -83,7 +83,7 @@ public async Task When_decompressing_a_large_zlib_payload_in_a_message() } [Fact] - public async Task When_decompressing_a_large_brotli_payload_in_a_message() + public void When_decompressing_a_large_brotli_payload_in_a_message() { //arrange var transformer = new CompressPayloadTransformer(); @@ -97,18 +97,18 @@ public async Task When_decompressing_a_large_brotli_payload_in_a_message() Stream compressionStream = new BrotliStream(output, CompressionLevel.Optimal); string mimeType = CompressPayloadTransformer.BROTLI; - await input.CopyToAsync(compressionStream); - await compressionStream.FlushAsync(); + input.CopyToAsync(compressionStream); + compressionStream.FlushAsync(); var body = new MessageBody(output.ToArray(), mimeType); var message = new Message( new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: mimeType),body); - message.Header.Bag[CompressPayloadTransformer.ORIGINAL_CONTENTTYPE_HEADER] = MessageBody.APPLICATION_JSON; + message.Header.Bag[CompressPayloadTransformerAsync.ORIGINAL_CONTENTTYPE_HEADER] = MessageBody.APPLICATION_JSON; //act - var msg = await transformer.UnwrapAsync(message); + var msg = transformer.Unwrap(message); //assert msg.Body.Value.Should().Be(largeContent); diff --git a/tests/Paramore.Brighter.Core.Tests/Compression/When_decompressing_a_large_payload_in_a_message_async.cs b/tests/Paramore.Brighter.Core.Tests/Compression/When_decompressing_a_large_payload_in_a_message_async.cs new file mode 100644 index 0000000000..36fba7fc64 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/Compression/When_decompressing_a_large_payload_in_a_message_async.cs @@ -0,0 +1,119 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Paramore.Brighter.Transforms.Transformers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.Compression; + +public class AsyncUncompressLargePayloadTests +{ + [Fact] + public async Task When_decompressing_a_large_gzip_payload_in_a_message() + { + //arrange + var transformer = new CompressPayloadTransformerAsync(); + transformer.InitializeUnwrapFromAttributeParams(CompressionMethod.GZip); + + var largeContent = DataGenerator.CreateString(6000); + + using var input = new MemoryStream(Encoding.ASCII.GetBytes(largeContent)); + using var output = new MemoryStream(); + + Stream compressionStream = new GZipStream(output, CompressionLevel.Optimal); + + string mimeType = CompressPayloadTransformerAsync.GZIP; + await input.CopyToAsync(compressionStream); + await compressionStream.FlushAsync(); + + var body = new MessageBody(output.ToArray(), mimeType); + + var message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: mimeType),body); + + message.Header.Bag[CompressPayloadTransformerAsync.ORIGINAL_CONTENTTYPE_HEADER] = MessageBody.APPLICATION_JSON; + + //act + var msg = await transformer.UnwrapAsync(message); + + //assert + msg.Body.Value.Should().Be(largeContent); + msg.Body.ContentType.Should().Be(MessageBody.APPLICATION_JSON); + msg.Header.ContentType.Should().Be(MessageBody.APPLICATION_JSON); + + } + + [Fact] + public async Task When_decompressing_a_large_zlib_payload_in_a_message() + { + //arrange + var transformer = new CompressPayloadTransformerAsync(); + transformer.InitializeUnwrapFromAttributeParams(CompressionMethod.Zlib); + + var largeContent = DataGenerator.CreateString(6000); + + using var input = new MemoryStream(Encoding.ASCII.GetBytes(largeContent)); + using var output = new MemoryStream(); + + Stream compressionStream = new ZLibStream(output, CompressionLevel.Optimal); + + string mimeType = CompressPayloadTransformerAsync.DEFLATE; + await input.CopyToAsync(compressionStream); + await compressionStream.FlushAsync(); + + var body = new MessageBody(output.ToArray(), mimeType); + + var message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: mimeType),body); + + message.Header.Bag[CompressPayloadTransformerAsync.ORIGINAL_CONTENTTYPE_HEADER] = MessageBody.APPLICATION_JSON; + + //act + var msg = await transformer.UnwrapAsync(message); + + //assert + msg.Body.Value.Should().Be(largeContent); + msg.Body.ContentType.Should().Be(MessageBody.APPLICATION_JSON); + msg.Header.ContentType.Should().Be(MessageBody.APPLICATION_JSON); + + } + + [Fact] + public async Task When_decompressing_a_large_brotli_payload_in_a_message() + { + //arrange + var transformer = new CompressPayloadTransformerAsync(); + transformer.InitializeUnwrapFromAttributeParams(CompressionMethod.Brotli); + + var largeContent = DataGenerator.CreateString(6000); + + using var input = new MemoryStream(Encoding.ASCII.GetBytes(largeContent)); + using var output = new MemoryStream(); + + Stream compressionStream = new BrotliStream(output, CompressionLevel.Optimal); + + string mimeType = CompressPayloadTransformerAsync.BROTLI; + await input.CopyToAsync(compressionStream); + await compressionStream.FlushAsync(); + + var body = new MessageBody(output.ToArray(), mimeType); + + var message = new Message( + new MessageHeader(Guid.NewGuid(), "test_topic", MessageType.MT_EVENT, DateTime.UtcNow, contentType: mimeType),body); + + message.Header.Bag[CompressPayloadTransformerAsync.ORIGINAL_CONTENTTYPE_HEADER] = MessageBody.APPLICATION_JSON; + + //act + var msg = await transformer.UnwrapAsync(message); + + //assert + msg.Body.Value.Should().Be(largeContent); + msg.Body.ContentType.Should().Be(MessageBody.APPLICATION_JSON); + msg.Header.ContentType.Should().Be(MessageBody.APPLICATION_JSON); + + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/MyEventMessageMapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/MyEventMessageMapperAsync.cs new file mode 100644 index 0000000000..7de7902a6b --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/MyEventMessageMapperAsync.cs @@ -0,0 +1,51 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using Paramore.Brighter.Core.Tests.CommandProcessors.TestDoubles; + +namespace Paramore.Brighter.Core.Tests.MessageDispatch.TestDoubles +{ + internal class MyEventMessageMapperAsync : IAmAMessageMapperAsync + { + public Task MapToMessage(MyEvent request) + { + var tcs = new TaskCompletionSource(); + var header = new MessageHeader(request.Id, "MyEvent", MessageType.MT_EVENT); + var body = new MessageBody(JsonSerializer.Serialize(request, JsonSerialisationOptions.Options)); + var message = new Message(header, body); + tcs.SetResult(message); + return tcs.Task; + } + + public async Task MapToRequest(Message message) + { + using var stream = new MemoryStream(message.Body.Bytes); + stream.Position = 0; + return await JsonSerializer.DeserializeAsync(stream, JsonSerialisationOptions.Options); + } + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_Transform.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_Transform.cs index 7640237110..3f28df4fd3 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_Transform.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_Transform.cs @@ -33,7 +33,7 @@ public void When_A_Message_Mapper_Map_To_Message_Has_A_Transform() _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); //assert - TraceFilters().ToString().Should().Be("MySimpleTransformAsync|MyTransformableCommandMessageMapper"); + TraceFilters().ToString().Should().Be("MySimpleTransform|MyTransformableCommandMessageMapper"); } private TransformPipelineTracer TraceFilters() diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_TransformAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_TransformAsync.cs index cb36fbd3ca..bda4ffc322 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_TransformAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_A_TransformAsync.cs @@ -18,7 +18,7 @@ public AsyncMessageUnwrapPathPipelineTests() var mapperRegistry = new MessageMapperRegistry( null, new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); - mapperRegistry.Register(); + mapperRegistry.RegisterAsync(); var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); @@ -33,7 +33,7 @@ public void When_A_Message_Mapper_Map_To_Message_Has_A_Transform() _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); //assert - TraceFilters().ToString().Should().Be("MySimpleTransformAsync|MyTransformableCommandMessageMapper"); + TraceFilters().ToString().Should().Be("MySimpleTransformAsync|MyTransformableCommandMessageMapperAsync"); } private TransformPipelineTracer TraceFilters() diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_TransformAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_TransformAsync.cs index c721d69529..834ff462b1 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_TransformAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Message_Has_No_TransformAsync.cs @@ -18,7 +18,7 @@ public AsyncMessageUnwrapPathNoTransformPipelineTests() var mapperRegistry = new MessageMapperRegistry( null, new SimpleMessageMapperFactoryAsync(_ => new MyVanillaCommandMessageMapperAsync())); - mapperRegistry.Register(); + mapperRegistry.RegisterAsync(); var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => null)); @@ -33,7 +33,7 @@ public void When_A_Message_Mapper_Map_To_Message_Has_No_Transform() _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); //assert - TraceFilters().ToString().Should().Be("MyVanillaCommandMessageMapper"); + TraceFilters().ToString().Should().Be("MyVanillaCommandMessageMapperAsync"); } private TransformPipelineTracer TraceFilters() diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_Transform.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_Transform.cs index 16af4fd42c..25f25a3f6e 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_Transform.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_Transform.cs @@ -34,7 +34,7 @@ public void When_A_Message_Mapper_Map_To_Request_Has_A_Transform() _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); //assert - TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapper|MySimpleTransformAsync"); + TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapper|MySimpleTransform"); } private TransformPipelineTracer TraceFilters() diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_TransformAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_TransformAsync.cs index fa42a0c83d..28d78fd914 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_TransformAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_A_TransformAsync.cs @@ -16,10 +16,10 @@ public AsyncMessageWrapPathPipelineTests() TransformPipelineBuilder.ClearPipelineCache(); var mapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), - null + null, + new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync()) ); - mapperRegistry.Register(); + mapperRegistry.RegisterAsync(); var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); @@ -34,7 +34,7 @@ public void When_A_Message_Mapper_Map_To_Request_Has_A_Transform() _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); //assert - TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapper|MySimpleTransformAsync"); + TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapperAsync|MySimpleTransformAsync"); } private TransformPipelineTracer TraceFilters() diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_No_Transform_Async.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_No_Transform_Async.cs new file mode 100644 index 0000000000..2a327315de --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_A_Message_Mapper_Map_To_Request_Has_No_Transform_Async.cs @@ -0,0 +1,45 @@ +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] + public class AsyncMessageWrapPathPipelineNoTransformTests +{ + private WrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + + public AsyncMessageWrapPathPipelineNoTransformTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyVanillaCommandMessageMapperAsync())); + mapperRegistry.RegisterAsync(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => null)); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + + } + + [Fact] + public void When_A_Message_Mapper_Map_To_Request_Has_No_Transform() + { + //act + _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); + + //assert + TraceFilters().ToString().Should().Be("MyVanillaCommandMessageMapperAsync"); + } + + private TransformPipelineTracer TraceFilters() + { + var pipelineTracer = new TransformPipelineTracer(); + _transformPipeline.DescribePath(pipelineTracer); + return pipelineTracer; + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory_Async.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory_Async.cs index ed2dd2a920..ecec3c5461 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_A_Wrap_Without_A_Factory_Async.cs @@ -21,7 +21,7 @@ public AsyncTransformPipelineMissingFactoryWrapTests() var mapperRegistry = new MessageMapperRegistry( null, new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); - mapperRegistry.Register(); + mapperRegistry.RegisterAsync(); _myCommand = new MyTransformableCommand(); @@ -35,7 +35,7 @@ public async Task When_Creating_A_Wrap_Without_A_Factory() _transformPipeline = _pipelineBuilder.BuildWrapPipeline(); // If no factory we default to just them mapper - TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapper"); + TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapperAsync"); //wrap should just do message mapper var message = await _transformPipeline.WrapAsync(_myCommand); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory_Async.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory_Async.cs index e9389d07b1..32b67b6f99 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory_Async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Creating_An_Unwrap_Without_A_Factory_Async.cs @@ -23,7 +23,7 @@ public AsyncTransformPipelineMissingFactoryUnwrapTests() var mapperRegistry = new MessageMapperRegistry( null, new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); - mapperRegistry.Register(); + mapperRegistry.RegisterAsync(); _myCommand = new MyTransformableCommand(); @@ -42,7 +42,7 @@ public async Task When_Creating_An_Unwrap_Without_A_Factory() _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline(); // If no factory we default to just them mapper - TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapper"); + TraceFilters().ToString().Should().Be("MyTransformableCommandMessageMapperAsync"); //wrap should just do message mapper var request = await _transformPipeline.UnwrapAsync(_message); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper.cs index 13f7df4e0c..50a436b57c 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_Mapper.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json; using System.Threading.Tasks; +using FluentAssertions; using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; using Xunit; @@ -46,6 +47,6 @@ public void When_Unwrapping_A_Message_Mapper() var request = _transformPipeline.Unwrap(_message); //assert - request.Value = MySimpleTransformAsync.HEADER_KEY; + request.Value.Should().Be( MySimpleTransformAsync.TRANSFORM_VALUE); } } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_MapperAsync.cs index edd86be3b0..048f294b80 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_MapperAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Message_MapperAsync.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json; using System.Threading.Tasks; +using FluentAssertions; using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; using Xunit; @@ -46,6 +47,6 @@ public async Task When_Unwrapping_A_Message_Mapper() var request = await _transformPipeline.UnwrapAsync(_message); //assert - request.Value = MySimpleTransformAsync.HEADER_KEY; + request.Value.Should().Be( MySimpleTransformAsync.TRANSFORM_VALUE); } } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_MapperAsync.cs index c107ee49f9..21bf4b753f 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_MapperAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_A_Vanilla_Message_MapperAsync.cs @@ -22,7 +22,7 @@ public AsyncVanillaMessageUnwrapRequestTests() var mapperRegistry = new MessageMapperRegistry( null, new SimpleMessageMapperFactoryAsync(_ => new MyVanillaCommandMessageMapperAsync())); - mapperRegistry.Register(); + mapperRegistry.RegisterAsync(); _myCommand = new MyTransformableCommand(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_Factory_Returns_Null.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_Factory_Returns_Null.cs new file mode 100644 index 0000000000..4cb8c09066 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_Factory_Returns_Null.cs @@ -0,0 +1,49 @@ +using System; +using System.Text.Json; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class MessageUnwrapRequestFailingMapperFactoryTests +{ + private UnwrapPipeline _transformPipeline; + private readonly TransformPipelineBuilder _pipelineBuilder; + + public MessageUnwrapRequestFailingMapperFactoryTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + new SimpleMessageMapperFactory(_ => null), + null); + mapperRegistry.Register(); + + MyTransformableCommand myCommand = new(); + + var messageTransformerFactory = new SimpleMessageTransformerFactory((_ => new MySimpleTransform())); + + _pipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory); + + Message message = new( + new MessageHeader(myCommand.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))) + ); + + message.Header.Bag[MySimpleTransformAsync.HEADER_KEY] = MySimpleTransformAsync.TRANSFORM_VALUE; + } + + [Fact] + public void When_Wrapping_But_Factory_Fails() + { + //act + var exception = Catch.Exception(() => _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline()); + exception.Should().NotBeNull(); + exception.Should().BeOfType(); + exception.InnerException.Should().BeOfType(); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_Factory_Returns_Null_Async.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_Factory_Returns_Null_Async.cs new file mode 100644 index 0000000000..12bd3543d7 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_Factory_Returns_Null_Async.cs @@ -0,0 +1,50 @@ +using System; +using System.Text.Json; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageSerialisation.Test_Doubles; +using Paramore.Brighter.Core.Tests.TestHelpers; +using Xunit; + +namespace Paramore.Brighter.Core.Tests.MessageSerialisation; + +[Collection("CommandProcessor")] +public class AsyncMessageUnwrapRequestFailingMapperFactoryTests +{ + private UnwrapPipelineAsync _transformPipeline; + private readonly TransformPipelineBuilderAsync _pipelineBuilder; + + public AsyncMessageUnwrapRequestFailingMapperFactoryTests() + { + //arrange + TransformPipelineBuilder.ClearPipelineCache(); + + var mapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => null) + ); + mapperRegistry.RegisterAsync(); + + MyTransformableCommand myCommand = new(); + + var messageTransformerFactory = new SimpleMessageTransformerFactoryAsync((_ => new MySimpleTransformAsync())); + + _pipelineBuilder = new TransformPipelineBuilderAsync(mapperRegistry, messageTransformerFactory); + + Message message = new( + new MessageHeader(myCommand.Id, "transform.event", MessageType.MT_COMMAND, DateTime.UtcNow), + new MessageBody(JsonSerializer.Serialize(myCommand, new JsonSerializerOptions(JsonSerializerDefaults.General))) + ); + + message.Header.Bag[MySimpleTransformAsync.HEADER_KEY] = MySimpleTransformAsync.TRANSFORM_VALUE; + } + + [Fact] + public void When_Wrapping_But_Factory_Fails() + { + //act + var exception = Catch.Exception(() => _transformPipeline = _pipelineBuilder.BuildUnwrapPipeline()); + exception.Should().NotBeNull(); + exception.Should().BeOfType(); + exception.InnerException.Should().BeOfType(); + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_Mapper.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_Mapper.cs index 95ba857250..e3eb0d0f7d 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_Mapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_Mapper.cs @@ -19,9 +19,8 @@ public MessageUnwrapRequestMissingMapperTests() TransformPipelineBuilder.ClearPipelineCache(); var mapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => null), + new SimpleMessageMapperFactory(_ => new MyTransformableCommandMessageMapper()), null); - mapperRegistry.Register(); MyTransformableCommand myCommand = new(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_MapperAsync.cs index 6f3de4b73a..7ccc803e3e 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_MapperAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Unwrapping_But_No_Registered_MapperAsync.cs @@ -20,8 +20,7 @@ public AsyncMessageUnwrapRequestMissingMapperTests() var mapperRegistry = new MessageMapperRegistry( null, - new SimpleMessageMapperFactoryAsync(_ => null)); - mapperRegistry.RegisterAsync(); + new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); MyTransformableCommand myCommand = new(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_MapperAsync.cs index 69f12d477b..6e2dc7147e 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_MapperAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Message_MapperAsync.cs @@ -21,7 +21,7 @@ public AsyncMessageWrapRequestTests() var mapperRegistry = new MessageMapperRegistry( null, new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); - mapperRegistry.Register(); + mapperRegistry.RegisterAsync(); _myCommand = new MyTransformableCommand(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_MapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_MapperAsync.cs index e95922f538..24b98a7597 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_MapperAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_A_Vanilla_Message_MapperAsync.cs @@ -21,7 +21,7 @@ public AsyncVanillaMessageWrapRequestTests() var mapperRegistry = new MessageMapperRegistry( null, new SimpleMessageMapperFactoryAsync(_ => new MyVanillaCommandMessageMapperAsync())); - mapperRegistry.Register(); + mapperRegistry.RegisterAsync(); _myCommand = new MyTransformableCommand(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_Pipeline.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_Pipeline.cs index 1d9edaffef..aa1a5c80f4 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_Pipeline.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_Pipeline.cs @@ -39,7 +39,7 @@ public void When_Wrapping_Clean_Up_The_Pipeline() _transformPipeline.Dispose(); //assert - s_released.Should().Be("|MySimpleTransformAsync"); + s_released.Should().Be("|MySimpleTransform"); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_PipelineAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_PipelineAsync.cs index 2e75c645c3..9fe06e2276 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_PipelineAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageSerialisation/When_Wrapping_Clean_Up_The_PipelineAsync.cs @@ -23,7 +23,7 @@ public AsyncMessageWrapCleanupTests() var mapperRegistry = new MessageMapperRegistry( null, new SimpleMessageMapperFactoryAsync(_ => new MyTransformableCommandMessageMapperAsync())); - mapperRegistry.Register(); + mapperRegistry.RegisterAsync(); _myCommand = new MyTransformableCommand(); diff --git a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs index bf31538cf6..14685cff59 100644 --- a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs +++ b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs @@ -84,7 +84,7 @@ public DispatchBuilderTests() _builder = DispatchBuilder.With() .CommandProcessorFactory(() =>new CommandProcessorProvider(commandProcessor)) - .MessageMappers(messageMapperRegistry, null) + .MessageMappers(messageMapperRegistry, null, null) .DefaultChannelFactory(new ChannelFactory(rmqMessageConsumerFactory)) .Subscriptions(new [] { diff --git a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs index e4cce5a681..e5d7920910 100644 --- a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs +++ b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs @@ -80,7 +80,7 @@ public DispatchBuilderWithNamedGateway() _builder = DispatchBuilder.With() .CommandProcessorFactory(() => new CommandProcessorProvider(commandProcessor)) - .MessageMappers(messageMapperRegistry, null) + .MessageMappers(messageMapperRegistry, null, null) .DefaultChannelFactory(new ChannelFactory(rmqMessageConsumerFactory)) .Subscriptions(new [] { From d2642e21d775bc0b4c226fb6d5203908492b7881 Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Fri, 29 Dec 2023 19:09:54 +0000 Subject: [PATCH 04/15] Update the ADRs for this PR --- ...0002-use-a-single-threaded-message-pump.md | 23 ++--- docs/adr/0005-support-async-pipelines.md | 83 +++++++++++++++++++ 2 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 docs/adr/0005-support-async-pipelines.md diff --git a/docs/adr/0002-use-a-single-threaded-message-pump.md b/docs/adr/0002-use-a-single-threaded-message-pump.md index 94fc5edb5a..f14ddf08dd 100644 --- a/docs/adr/0002-use-a-single-threaded-message-pump.md +++ b/docs/adr/0002-use-a-single-threaded-message-pump.md @@ -10,15 +10,17 @@ Accepted Any service activator pattern will have a message pump, which reads from a queue. -There are different strategies we could use, a common one for example is to use a BlockingCollection to hold messages read from the queue, and then use threads from the threadpool to process those messages. +There are different strategies we could use, a common one for example is to use a BlockingCollection to hold messages read from the queue, and then use threads from the threadpool to process those messages. However, a multi-threaded pump has the issue that it will de-order an +otherwise ordered queue, as the threads will pull items from the blocking collection in parallel, not sequentially. In addition, where we have multiple threads it becomes difficult to create resources used by the pump without protecting them from race conditions. -However, a multi-threaded pump has the issue that it will de-order an otherwise ordered queue, as the threads will pull items from the blocking collection in parallel, not sequentially. +The other option would be to use the thread pool to service requests, creating a thread for each incoming message. This would not scale, as we would quickly run out of threads in the pool. To avoid this issue, solutions that rely on the thread pool typically have to govern +the number of thread pool threads that can be used for concurrent requests. The problem becomes that at scale the semaphore that governs the number of threads becomes a bottleneck. -In addition, where we have multiple threads it becomes difficult to create resources used by the pump without protecting them from race conditions. +The alternative to these multi-threaded approaches is to use a single-threaded message pump that reads from the queue, processes the message, and only when it has processed that message, processes the next item. This prevents de-ordering of the queue, because items are read in sequence. -The alternative is to use a single-threaded message pump that reads from the queue, processes the message, and only when it has processed that message, processes the next item. This prevents de-ordering of the queue, because items are read in sequence. - -If a higher throughput is desired with a single threaded pump, then you can create multiple pumps. In essence, this is the competing consumers pattern, each performer is its own message pump. +This approach is the [Reactor](https://en.wikipedia.org/wiki/Reactor_pattern) pattern. The Reactor pattern uses a single thread to read from the queue, and then dispatches the message to a handler. If a higher throughput is desired with +a single threaded pump, then you can create multiple pumps. In essence, this is the competing consumers pattern, each performer is its own message pump. To make the Reactor pattern more performant, we can choose not to block on I/O by using asynchronous handlers. +This is the [Proactor](https://en.wikipedia.org/wiki/Proactor_pattern) pattern. Brighter provides a SynchronizationContext so that asynchronous handlers can be used, and the message pump will not block on I/O, whilst still prserving ordering. The message pump performs the usual sequence of actions: @@ -26,17 +28,18 @@ The message pump performs the usual sequence of actions: - Translate Message. Translate Message from Wire Format to Type - Dispatch Message. Dispatch Message based on Type - Prior art for this is the Windows Event Loop which uses this approach, and is used by COM for integration via the Single-Threaded Apartment model. + Prior art for this is the Windows Event Loop which uses this approach, and is used by COM for integration via the Single-Threaded Apartment model. ## Decision -Use a single-threaded message pump to preserve ordering and ensure sequential access to shared resources. Allow multiple pump instances for throughput. +Use a single-threaded message pump to preserve ordering and ensure sequential access to shared resources. Allow multiple pump instances for throughput. Allow asynchronous handlers to prevent blocking on I/O. ## Consequences -This makes an async model harder, as it relies on a sequential processing strategy, which is the opposite of an async strategy which would allow a handler to yield when it was doing I/O, allowing another message to be consumed. Because this implicitly de-orders messages, this is not compatible with this approach. +This makes an async model harder, as it relies on a sequential processing strategy which implies that we must use the message pump thread for callbacks. This is the opposite of an async strategy that uses the thread pool for callbacks, which would prevent queueing of callbacks at the cost +of potentially running those callbacks out of order. -it may imply that we should consider having a 'pluggable' pump that can use different strategies, asynchronous where you do not require ordering, and synchronous where you do. +It may imply that we should consider having a 'pluggable' pump that can use different strategies, asynchronous where you do not require ordering, and synchronous where you do. diff --git a/docs/adr/0005-support-async-pipelines.md b/docs/adr/0005-support-async-pipelines.md new file mode 100644 index 0000000000..0ce50a8cd0 --- /dev/null +++ b/docs/adr/0005-support-async-pipelines.md @@ -0,0 +1,83 @@ +# 1. Record architecture decisions + +Date: 2019-08-01 + +## Status + +Accepted + +## Context + +Give we have decided to use a reactor pattern (see [Single Threaded Message Pump](0002-use-a-single-threaded-message-pump.md), +we need to decide how to support asynchronous pipelines. There are three requirements: + +* We need to be able to support asynchronous handlers +* We need to be able to support asynchronous message mappers +* We need to provide our own synchronization context so that the thread on which callbacks are invoked is the message pump thread. + +Why do handlers need to be asynchronous? Because they may need to perform I/O, such as talking to a database or a web service, +and we do not want to block the message pump thread. + +Why do message mappers need to be asynchronous? Because they may need to perform I/O, such as talking to a schema registry or +using a Claim Check transform and we do not want to block the message pump thread. + +Why do we need to provide our own synchronization context? Because we want to ensure that the thread on which callbacks are invoked +is the message pump thread. This is important because we want to ensure that the message pump thread is not blocked by I/O. + +## Decision + +### Synchronization Context +Implement a custom synchronization context that will invoke callbacks on the message pump thread. The synchronization context +is based on the [Stephen Toub Single Threaded Synchronization Context](https://devblogs.microsoft.com/pfxteam/await-synchronizationcontext-and-console-apps/), +and will run callbacks in order on the message pump thread. + +### Asynchronous Handlers +Asynchronous handlers implement IHandleRequestsAsync and return a Task. The message pump will await the task. Any callback after +the code returns from the handler will be invoked on the thread that invoked the handler. + +#### Homogenous Handler Pipelines + +A handler pipeline needs to be all asynchronous or all synchronous. If a handler pipeline contains both synchronous and asynchronous +then we would be forced to either block asynchronous handlers on a synchronous pipeline, or invoke synchronous handlers on a thread +other than the message pump thread. + +### Asynchronous Message Mappers +Asynchronous message mappers implement IAmAMessageMapperAsync and return a Task. The message pump will await the task. Any callback after +the code returns from the handler will be invoked on the thread that invoked the handler. + +A message mapper pipeline terminates with an IAmAMessageMapper or an IAmAMessageMapperAsync. A pipeline may have transforms that +implement IAmAMessageTransform or IAmAMessageTransformAsync. (We do not use the message mapper interface for the pipeline, +by contrast to handlers, because whereas each handler step attempts to handle the message a transform is either wrapping a message built +by a message mapper or unwrapping a message to be passed to a message mapper.) + +#### Homogenous Message Mapper Pipelines + +A message mapper pipeline needs to be all asynchronous or all synchronous. If a message mapper pipeline contains both synchronous and asynchronous +then we would be forced to either block asynchronous message mappers on a synchronous pipeline, or invoke synchronous message mappers on a thread +other than the message pump thread. + +### Posting a Message + +When posting a message we assume that the async command processor Post methods will be for asynchronous message mappers and we search +for registered asynchonous message mappers before searching for synchronous message mappers. If the message mapper is synchronous then +we wrap it in a TaskCompletionSource and return the Task. + +When posting a message we assume that the sync command processor Post methods will be for synchronous message mappers and we search +for registered synchronous message mappers before searching for asynchronous message mappers. If the message mapper is asynchronous then +we block on the Task. + +### Receiving a Message + +When receiving a message we assume that you will use an async message pump for an asynchronous message mapper and we search for registered +asynchronous message mappers before searching for synchronous message mappers. If the message mapper is synchronous then we wrap it in a TaskCompletionSource +and return the Task. We then use the synchronization context to ensure that the callback is invoked on the message pump thread. + +When receiving a message we assume that you will use a sync message pump for a synchronous message mapper and we search for registered synchronous +message mappers before searching for asynchronous message mappers. If the message mapper is asynchronous then we block on the Task. + +## Consequences + +* We can support asynchronous handlers +* We can support asynchronous message mappers +* We provide our own synchronization context so that the thread on which callbacks are invoked is the message pump thread. + From c0b759afac0d4142268c7af438d48388fb1875ed Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Fri, 5 Jan 2024 19:52:51 +0000 Subject: [PATCH 05/15] Flow up the support for async message mappers --- .../ServiceCollectionExtensions.cs | 2 +- .../ServiceProviderMapperFactoryAsync.cs | 56 +++++++++++++ .../ServiceCollectionExtensions.cs | 2 +- .../ConsumerFactory.cs | 19 ++++- .../ControlBusReceiverBuilder.cs | 2 +- .../DispatchBuilder.cs | 24 ++++-- .../Dispatcher.cs | 83 +++++++++++++++---- .../MessagePump.cs | 34 +------- .../MessagePumpAsync.cs | 74 ++++++++++++----- .../MessagePumpBlocking.cs | 30 ++++++- ...message_is_requeued_until_rejectedAsync.cs | 12 ++- ...d_exception_Then_message_is_acked_async.cs | 12 ++- ..._to_connect_a_channel_and_handler_async.cs | 11 +-- ...as_a_new_connection_added_while_running.cs | 2 +- ..._asked_to_connect_a_channel_and_handler.cs | 2 +- ...essage_dispatcher_restarts_a_connection.cs | 2 +- ...tion_after_all_connections_have_stopped.cs | 2 +- ...a_message_dispatcher_shuts_a_connection.cs | 2 +- ...er_starts_different_types_of_performers.cs | 2 +- ...e_dispatcher_starts_multiple_performers.cs | 2 +- ...patched_it_should_reach_a_handler_async.cs | 10 ++- ...message_is_requeued_until_rejectedAsync.cs | 9 +- ...d_exception_Then_message_is_acked_async.cs | 9 +- .../When_building_a_dispatcher.cs | 2 +- ...uilding_a_dispatcher_with_named_gateway.cs | 2 +- 25 files changed, 296 insertions(+), 111 deletions(-) create mode 100644 src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderMapperFactoryAsync.cs diff --git a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index ea7b1c5a54..3150249d68 100644 --- a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -356,7 +356,7 @@ public static MessageMapperRegistry MessageMapperRegistry(IServiceProvider provi var messageMapperRegistry = new MessageMapperRegistry( new ServiceProviderMapperFactory(provider), - null + new ServiceProviderMapperFactoryAsync(provider) ); foreach (var messageMapper in serviceCollectionMessageMapperRegistry.Mappers) diff --git a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderMapperFactoryAsync.cs b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderMapperFactoryAsync.cs new file mode 100644 index 0000000000..3a1e1c00be --- /dev/null +++ b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceProviderMapperFactoryAsync.cs @@ -0,0 +1,56 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2022 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; + +namespace Paramore.Brighter.Extensions.DependencyInjection +{ + /// + /// Creates a message mapper from the underlying .NET IoC container + /// + public class ServiceProviderMapperFactoryAsync : IAmAMessageMapperFactoryAsync + { + private readonly IServiceProvider _serviceProvider; + + /// + /// Constructs a mapper factory that uses the .NET Service Provider for implementation details + /// + /// + public ServiceProviderMapperFactoryAsync(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + /// + /// Create an instance of the message mapper type from the .NET IoC container + /// Note that there is no release as we assume that Mappers are never IDisposable + /// + /// The type of mapper to instantiate + /// + public IAmAMessageMapperAsync Create(Type messageMapperType) + { + return (IAmAMessageMapperAsync) _serviceProvider.GetService(messageMapperType); + } + } +} diff --git a/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index 6a48ab1618..8f2f4f9e81 100644 --- a/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -72,7 +72,7 @@ private static Dispatcher BuildDispatcher(IServiceProvider serviceProvider) var messageTransformFactoryAsync = ServiceCollectionExtensions.TransformFactoryAsync(serviceProvider); return dispatcherBuilder - .MessageMappers(messageMapperRegistry, messageTransformFactory, messageTransformFactoryAsync) + .MessageMappers(messageMapperRegistry, messageMapperRegistry, messageTransformFactory, messageTransformFactoryAsync) .DefaultChannelFactory(options.ChannelFactory) .Subscriptions(options.Subscriptions).Build(); } diff --git a/src/Paramore.Brighter.ServiceActivator/ConsumerFactory.cs b/src/Paramore.Brighter.ServiceActivator/ConsumerFactory.cs index 5c10f85a5d..7d070fc58c 100644 --- a/src/Paramore.Brighter.ServiceActivator/ConsumerFactory.cs +++ b/src/Paramore.Brighter.ServiceActivator/ConsumerFactory.cs @@ -33,11 +33,13 @@ internal class ConsumerFactory : IConsumerFactory where TRequest : cla private readonly Subscription _subscription; private readonly IAmAMessageTransformerFactory _messageTransformerFactory; private readonly ConsumerName _consumerName; + private readonly IAmAMessageMapperRegistryAsync _messageMapperRegistryAsync; + private readonly IAmAMessageTransformerFactoryAsync _messageTransformerFactoryAsync; public ConsumerFactory( IAmACommandProcessorProvider commandProcessorProvider, + Subscription subscription, IAmAMessageMapperRegistry messageMapperRegistry, - Subscription subscription, IAmAMessageTransformerFactory messageTransformerFactory = null) { _commandProcessorProvider = commandProcessorProvider; @@ -46,6 +48,19 @@ public ConsumerFactory( _messageTransformerFactory = messageTransformerFactory; _consumerName = new ConsumerName($"{_subscription.Name}-{Guid.NewGuid()}"); } + + public ConsumerFactory( + IAmACommandProcessorProvider commandProcessorProvider, + Subscription subscription, + IAmAMessageMapperRegistryAsync messageMapperRegistryAsync, + IAmAMessageTransformerFactoryAsync messageTransformerFactoryAsync) + { + _commandProcessorProvider = commandProcessorProvider; + _messageMapperRegistryAsync = messageMapperRegistryAsync; + _subscription = subscription; + _messageTransformerFactoryAsync = messageTransformerFactoryAsync; + _consumerName = new ConsumerName($"{_subscription.Name}-{Guid.NewGuid()}"); + } public Consumer Create() { @@ -73,7 +88,7 @@ private Consumer CreateBlocking() private Consumer CreateAsync() { var channel = _subscription.ChannelFactory.CreateChannel(_subscription); - var messagePump = new MessagePumpAsync(_commandProcessorProvider, _messageMapperRegistry, _messageTransformerFactory) + var messagePump = new MessagePumpAsync(_commandProcessorProvider, _messageMapperRegistryAsync, _messageTransformerFactoryAsync) { Channel = channel, TimeoutInMilliseconds = _subscription.TimeoutInMilliseconds, diff --git a/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs b/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs index c13be87d0e..c61ab408c7 100644 --- a/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs +++ b/src/Paramore.Brighter.ServiceActivator/ControlBusReceiverBuilder.cs @@ -182,7 +182,7 @@ public Dispatcher Build(string hostName) return DispatchBuilder.With() .CommandProcessorFactory(() => new CommandProcessorProvider(commandProcessor)) - .MessageMappers(incomingMessageMapperRegistry, null, null) + .MessageMappers(incomingMessageMapperRegistry, null, null, null) .DefaultChannelFactory(_channelFactory) .Subscriptions(subscriptions) .Build(); diff --git a/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs b/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs index d933204e8f..2f30555f33 100644 --- a/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs +++ b/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs @@ -38,9 +38,11 @@ public class DispatchBuilder : INeedACommandProcessorFactory, INeedAChannelFacto { private Func _commandProcessorFactory; private IAmAMessageMapperRegistry _messageMapperRegistry; + private IAmAMessageMapperRegistryAsync _messageMapperRegistryAsync; private IAmAChannelFactory _defaultChannelFactory; private IEnumerable _subscriptions; private IAmAMessageTransformerFactory _messageTransformerFactory; + private IAmAMessageTransformerFactoryAsync _messageTransformerFactoryAsync; private DispatchBuilder() { } @@ -67,17 +69,26 @@ public INeedAMessageMapper CommandProcessorFactory(Func /// The message mappers used to map between commands, events, and on-the-wire handlers. /// - /// The message mapper registry. + /// The message mapper registry. + /// The async message mapper /// A factory to produce transformers for a message mapper /// A factory to produce async transformers for a message mapper /// INeedAChannelFactory. + /// throws You must provide at least one type of message mapper registry public INeedAChannelFactory MessageMappers( - IAmAMessageMapperRegistry theMessageMapperRegistry, + IAmAMessageMapperRegistry messageMapperRegistry, + IAmAMessageMapperRegistryAsync messageMapperRegistryAsync, IAmAMessageTransformerFactory messageTransformerFactory, IAmAMessageTransformerFactoryAsync messageTransformFactoryAsync) { - _messageMapperRegistry = theMessageMapperRegistry; + _messageMapperRegistry = messageMapperRegistry; + _messageMapperRegistryAsync = messageMapperRegistryAsync; _messageTransformerFactory = messageTransformerFactory; + _messageTransformerFactoryAsync = messageTransformFactoryAsync; + + if (messageMapperRegistry is null && messageMapperRegistryAsync is null) + throw new ConfigurationException("You must provide a message mapper registry or an async message mapper registry"); + return this; } @@ -136,7 +147,7 @@ public IAmADispatchBuilder Connections(IEnumerable connections) /// Dispatcher. public Dispatcher Build() { - return new Dispatcher(_commandProcessorFactory, _messageMapperRegistry, _subscriptions, _messageTransformerFactory); + return new Dispatcher(_commandProcessorFactory, _subscriptions, _messageMapperRegistry, _messageMapperRegistryAsync, _messageTransformerFactory, _messageTransformerFactoryAsync); } @@ -166,10 +177,13 @@ public interface INeedAMessageMapper /// The message mappers used to map between commands, events, and on-the-wire handlers. /// /// The message mapper registry. + /// The async message mapper registry /// The factory for creating transforms /// The factory for creating async transforms /// INeedAChannelFactory. - INeedAChannelFactory MessageMappers(IAmAMessageMapperRegistry messageMapperRegistry, + INeedAChannelFactory MessageMappers( + IAmAMessageMapperRegistry messageMapperRegistry, + IAmAMessageMapperRegistryAsync messageMapperRegistryAsync, IAmAMessageTransformerFactory messageTransformerFactory, IAmAMessageTransformerFactoryAsync messageTransformFactoryAsync); } diff --git a/src/Paramore.Brighter.ServiceActivator/Dispatcher.cs b/src/Paramore.Brighter.ServiceActivator/Dispatcher.cs index db378678ad..9f778430a9 100644 --- a/src/Paramore.Brighter.ServiceActivator/Dispatcher.cs +++ b/src/Paramore.Brighter.ServiceActivator/Dispatcher.cs @@ -26,11 +26,13 @@ THE SOFTWARE. */ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Paramore.Brighter.Extensions; using Paramore.Brighter.Logging; using Paramore.Brighter.ServiceActivator.Status; +using BindingFlags = System.Reflection.BindingFlags; namespace Paramore.Brighter.ServiceActivator { @@ -47,6 +49,8 @@ public class Dispatcher : IDispatcher private Task _controlTask; private readonly IAmAMessageMapperRegistry _messageMapperRegistry; private readonly IAmAMessageTransformerFactory _messageTransformerFactory; + private readonly IAmAMessageMapperRegistryAsync _messageMapperRegistryAsync; + private readonly IAmAMessageTransformerFactoryAsync _messageTransformerFactoryAsync; private readonly ConcurrentDictionary _tasks; private readonly ConcurrentDictionary _consumers; @@ -91,20 +95,29 @@ public class Dispatcher : IDispatcher /// Initializes a new instance of the class. /// /// The command processor Factory. - /// The message mapper registry. /// The subscriptions. + /// The message mapper registry. + /// Async message mapper registry. /// Creates instances of Transforms - public Dispatcher( - Func commandProcessorFactory, - IAmAMessageMapperRegistry messageMapperRegistry, + /// Creates instances of Transforms async + /// throws You must provide at least one type of message mapper registry + public Dispatcher(Func commandProcessorFactory, IEnumerable subscriptions, - IAmAMessageTransformerFactory messageTransformerFactory = null) + IAmAMessageMapperRegistry messageMapperRegistry = null, + IAmAMessageMapperRegistryAsync messageMapperRegistryAsync = null, + IAmAMessageTransformerFactory messageTransformerFactory = null, + IAmAMessageTransformerFactoryAsync messageTransformerFactoryAsync= null) { CommandProcessorFactory = commandProcessorFactory; Connections = subscriptions; _messageMapperRegistry = messageMapperRegistry; + _messageMapperRegistryAsync = messageMapperRegistryAsync; _messageTransformerFactory = messageTransformerFactory; + _messageTransformerFactoryAsync = messageTransformerFactoryAsync; + + if (messageMapperRegistry is null && messageMapperRegistryAsync is null) + throw new ConfigurationException("You must provide a message mapper registry or an async message mapper registry"); State = DispatcherState.DS_NOTREADY; @@ -114,12 +127,24 @@ public Dispatcher( State = DispatcherState.DS_AWAITING; } - public Dispatcher( - IAmACommandProcessor commandProcessor, - IAmAMessageMapperRegistry messageMapperRegistry, - IEnumerable subscription, - IAmAMessageTransformerFactory messageTransformerFactory = null) - : this(() => new CommandProcessorProvider(commandProcessor), messageMapperRegistry, subscription, messageTransformerFactory) + /// + /// Initializes a new instance of the class. + /// + /// The command processor we should use with the dispatcher (prefer to use Command Processor Provider for IoC Scope control + /// The subscriptions. + /// The message mapper registry. + /// Async message mapper registry. + /// Creates instances of Transforms + /// Creates instances of Transforms async + /// throws You must provide at least one type of message mapper registry + public Dispatcher(IAmACommandProcessor commandProcessor, + IEnumerable subscriptions, + IAmAMessageMapperRegistry messageMapperRegistry = null, + IAmAMessageMapperRegistryAsync messageMapperRegistryAsync = null, + IAmAMessageTransformerFactory messageTransformerFactory = null, + IAmAMessageTransformerFactoryAsync messageTransformerFactoryAsync= null) + : this(() => + new CommandProcessorProvider(commandProcessor), subscriptions, messageMapperRegistry, messageMapperRegistryAsync, messageTransformerFactory, messageTransformerFactoryAsync) { } @@ -347,9 +372,39 @@ private Consumer CreateConsumer(Subscription subscription, int consumerNumber) { s_logger.LogInformation("Dispatcher: Creating consumer number {ConsumerNumber} for subscription: {ChannelName}", consumerNumber, subscription.Name); var consumerFactoryType = typeof(ConsumerFactory<>).MakeGenericType(subscription.DataType); - var consumerFactory = (IConsumerFactory)Activator.CreateInstance(consumerFactoryType, CommandProcessorFactory.Invoke(), _messageMapperRegistry, subscription, _messageTransformerFactory); - - return consumerFactory.Create(); + if (!subscription.RunAsync) + { + var types = new Type[] + { + typeof(IAmACommandProcessorProvider), typeof(Subscription), typeof(IAmAMessageMapperRegistry),typeof(IAmAMessageTransformerFactory) + }; + + var consumerFactoryCtor = consumerFactoryType.GetConstructor( + BindingFlags.Instance | BindingFlags.Public, null, + CallingConventions.HasThis, types, null + ); + + var consumerFactory = (IConsumerFactory)consumerFactoryCtor?.Invoke(new object[] { CommandProcessorFactory.Invoke(), subscription, _messageMapperRegistry, _messageTransformerFactory }); + + return consumerFactory.Create(); + } + else + { + + var types = new Type[] + { + typeof(IAmACommandProcessorProvider),typeof(Subscription), typeof(IAmAMessageMapperRegistryAsync), typeof(IAmAMessageTransformerFactoryAsync) + }; + + var consumerFactoryCtor = consumerFactoryType.GetConstructor( + BindingFlags.Instance | BindingFlags.Public, null, + CallingConventions.HasThis, types, null + ); + + var consumerFactory = (IConsumerFactory)consumerFactoryCtor?.Invoke(new object[] { CommandProcessorFactory.Invoke(), subscription, _messageMapperRegistryAsync, _messageTransformerFactoryAsync }); + + return consumerFactory.Create(); + } } } } diff --git a/src/Paramore.Brighter.ServiceActivator/MessagePump.cs b/src/Paramore.Brighter.ServiceActivator/MessagePump.cs index 900d47e657..15f5e4ad9d 100644 --- a/src/Paramore.Brighter.ServiceActivator/MessagePump.cs +++ b/src/Paramore.Brighter.ServiceActivator/MessagePump.cs @@ -57,23 +57,15 @@ public abstract class MessagePump : IAmAMessagePump where TRequest : c protected readonly IAmACommandProcessorProvider CommandProcessorProvider; private int _unacceptableMessageCount = 0; - private readonly UnwrapPipeline _unwrapPipeline; /// /// Constructs a message pump /// /// Provides a way to grab a command processor correctly scoped - /// The registry of mappers - /// The factory that lets us create instances of transforms - public MessagePump( - IAmACommandProcessorProvider commandProcessorProvider, - IAmAMessageMapperRegistry messageMapperRegistry, - IAmAMessageTransformerFactory messageTransformerFactory = null) + protected MessagePump(IAmACommandProcessorProvider commandProcessorProvider) { CommandProcessorProvider = commandProcessorProvider; - var transformPipelineBuilder = new TransformPipelineBuilder(messageMapperRegistry, messageTransformerFactory); - _unwrapPipeline = transformPipelineBuilder.BuildUnwrapPipeline(); - } + } public int TimeoutInMilliseconds { get; set; } @@ -331,27 +323,7 @@ private bool RequeueMessage(Message message) return Channel.Requeue(message, RequeueDelayInMilliseconds); } - private TRequest TranslateMessage(Message message) - { - s_logger.LogDebug("MessagePump: Translate message {Id} on thread # {ManagementThreadId}", message.Id, Thread.CurrentThread.ManagedThreadId); - - TRequest request; - - try - { - request = _unwrapPipeline.Unwrap(message); - } - catch (ConfigurationException) - { - throw; - } - catch (Exception exception) - { - throw new MessageMappingException($"Failed to map message {message.Id} using pipeline for type {typeof(TRequest).FullName} ", exception); - } - - return request; - } + protected abstract TRequest TranslateMessage(Message message); private bool UnacceptableMessageLimitReached() { diff --git a/src/Paramore.Brighter.ServiceActivator/MessagePumpAsync.cs b/src/Paramore.Brighter.ServiceActivator/MessagePumpAsync.cs index 23bfff818b..397635a121 100644 --- a/src/Paramore.Brighter.ServiceActivator/MessagePumpAsync.cs +++ b/src/Paramore.Brighter.ServiceActivator/MessagePumpAsync.cs @@ -24,6 +24,7 @@ THE SOFTWARE. */ using System; using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace Paramore.Brighter.ServiceActivator @@ -39,6 +40,8 @@ namespace Paramore.Brighter.ServiceActivator /// The Request on the Data Type Channel public class MessagePumpAsync : MessagePump where TRequest : class, IRequest { + private readonly UnwrapPipelineAsync _unwrapPipeline; + /// /// Constructs a message pump /// @@ -47,25 +50,14 @@ public class MessagePumpAsync : MessagePump where TRequest : /// The factory that lets us create instances of transforms public MessagePumpAsync( IAmACommandProcessorProvider commandProcessorProvider, - IAmAMessageMapperRegistry messageMapperRegistry, - IAmAMessageTransformerFactory messageTransformerFactory = null) - : base(commandProcessorProvider, messageMapperRegistry, messageTransformerFactory) + IAmAMessageMapperRegistryAsync messageMapperRegistry, + IAmAMessageTransformerFactoryAsync messageTransformerFactory) + : base(commandProcessorProvider) { + var transformPipelineBuilder = new TransformPipelineBuilderAsync(messageMapperRegistry, messageTransformerFactory); + _unwrapPipeline = transformPipelineBuilder.BuildUnwrapPipeline(); } - /// - /// Constructs a message pump - /// - /// A command processor - /// The registry of mappers - /// The factory that lets us create instances of transforms - public MessagePumpAsync( - IAmACommandProcessor commandProcessor, - IAmAMessageMapperRegistry messageMapperRegistry, - IAmAMessageTransformerFactory messageTransformerFactory = null) - : this(new CommandProcessorProvider(commandProcessor), messageMapperRegistry, messageTransformerFactory) - {} - protected override void DispatchRequest(MessageHeader messageHeader, TRequest request) { s_logger.LogDebug("MessagePump: Dispatching message {Id} from {ChannelName} on thread # {ManagementThreadId}", request.Id, Thread.CurrentThread.ManagedThreadId, Channel.Name); @@ -78,19 +70,25 @@ protected override void DispatchRequest(MessageHeader messageHeader, TRequest re { case MessageType.MT_COMMAND: { - Run(SendAsync, request); + RunDispatch(SendAsync, request); break; } case MessageType.MT_DOCUMENT: case MessageType.MT_EVENT: { - Run(PublishAsync, request); + RunDispatch(PublishAsync, request); break; } } } - - private static void Run(Action act, TRequest request) + + protected override TRequest TranslateMessage(Message message) + { + s_logger.LogDebug("MessagePump: Translate message {Id} on thread # {ManagementThreadId}", message.Id, Thread.CurrentThread.ManagedThreadId); + return RunTranslate(TranslateAsync, message); + } + + private static void RunDispatch(Action act, TRequest request) { if (act == null) throw new ArgumentNullException("act"); @@ -116,6 +114,36 @@ private static void Run(Action act, TRequest request) } } + private static TRequest RunTranslate(Func> act, Message message) + { + if (act == null) throw new ArgumentNullException("act"); + + var prevCtx = SynchronizationContext.Current; + try + { + // Establish the new context + var context = new BrighterSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(context); + + context.OperationStarted(); + + var future = act(message); + + future.ContinueWith(delegate { context.OperationCompleted(); }, TaskScheduler.Default); + + context.OperationCompleted(); + + // Pump continuations and propagate any exceptions + context.RunOnCurrentThread(); + + return future.GetAwaiter().GetResult(); + } + finally + { + SynchronizationContext.SetSynchronizationContext(prevCtx); + } + } + private async void PublishAsync(TRequest request) { await CommandProcessorProvider.Get().PublishAsync(request, continueOnCapturedContext: true); @@ -125,6 +153,12 @@ private async void SendAsync(TRequest request) { await CommandProcessorProvider.Get().SendAsync(request, continueOnCapturedContext: true); } + + private async Task TranslateAsync(Message message) + { + var request = await _unwrapPipeline.UnwrapAsync(message); + return request; + } } } diff --git a/src/Paramore.Brighter.ServiceActivator/MessagePumpBlocking.cs b/src/Paramore.Brighter.ServiceActivator/MessagePumpBlocking.cs index 8f46e227d3..92b9c570d7 100644 --- a/src/Paramore.Brighter.ServiceActivator/MessagePumpBlocking.cs +++ b/src/Paramore.Brighter.ServiceActivator/MessagePumpBlocking.cs @@ -22,6 +22,7 @@ THE SOFTWARE. */ #endregion +using System; using System.Threading; using Microsoft.Extensions.Logging; @@ -36,6 +37,7 @@ namespace Paramore.Brighter.ServiceActivator /// public class MessagePumpBlocking : MessagePump where TRequest : class, IRequest { + private readonly UnwrapPipeline _unwrapPipeline; /// /// Constructs a message pump /// @@ -45,9 +47,11 @@ public class MessagePumpBlocking : MessagePump where TReques public MessagePumpBlocking( IAmACommandProcessorProvider commandProcessorProvider, IAmAMessageMapperRegistry messageMapperRegistry, - IAmAMessageTransformerFactory messageTransformerFactory = null) - : base(commandProcessorProvider, messageMapperRegistry, messageTransformerFactory) + IAmAMessageTransformerFactory messageTransformerFactory) + : base(commandProcessorProvider) { + var transformPipelineBuilder = new TransformPipelineBuilder(messageMapperRegistry, messageTransformerFactory); + _unwrapPipeline = transformPipelineBuilder.BuildUnwrapPipeline(); } /// @@ -86,5 +90,27 @@ protected override void DispatchRequest(MessageHeader messageHeader, TRequest re } } } + + protected override TRequest TranslateMessage(Message message) + { + s_logger.LogDebug("MessagePump: Translate message {Id} on thread # {ManagementThreadId}", message.Id, Thread.CurrentThread.ManagedThreadId); + + TRequest request; + + try + { + request = _unwrapPipeline.Unwrap(message); + } + catch (ConfigurationException) + { + throw; + } + catch (Exception exception) + { + throw new MessageMappingException($"Failed to map message {message.Id} using pipeline for type {typeof(TRequest).FullName} ", exception); + } + + return request; + } } } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs index d664c0bd49..860758ba70 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs @@ -43,13 +43,17 @@ public class MessagePumpCommandProcessingDeferMessageActionTestsAsync public MessagePumpCommandProcessingDeferMessageActionTestsAsync() { _commandProcessor = new SpyRequeueCommandProcessor(); + var commandProcessorProvider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), - null); - messageMapperRegistry.Register(); + null, + new SimpleMessageMapperFactoryAsync(_ => new MyCommandMessageMapperAsync())); + messageMapperRegistry.RegisterAsync(); - _messagePump = new MessagePumpAsync(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; + _messagePump = new MessagePumpAsync(commandProcessorProvider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount + }; var msg = new TransformPipelineBuilder(messageMapperRegistry, null) .BuildWrapPipeline() diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs index 4c97c0dabe..54d89e82be 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs @@ -46,13 +46,17 @@ public MessagePumpCommandProcessingExceptionTestsAsync() { _commandProcessor = new SpyExceptionCommandProcessor(); + var commandProcessorProvider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), - null); - messageMapperRegistry.Register(); + null, + new SimpleMessageMapperFactoryAsync(_ => new MyCommandMessageMapperAsync())); + messageMapperRegistry.RegisterAsync(); - _messagePump = new MessagePumpAsync(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; + _messagePump = new MessagePumpAsync(commandProcessorProvider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount + }; var msg = new TransformPipelineBuilder(messageMapperRegistry, null) .BuildWrapPipeline() diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_Is_asked_to_connect_a_channel_and_handler_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_Is_asked_to_connect_a_channel_and_handler_async.cs index 84d182b14a..4bd16118bf 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_Is_asked_to_connect_a_channel_and_handler_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_Is_asked_to_connect_a_channel_and_handler_async.cs @@ -24,9 +24,10 @@ public MessageDispatcherRoutingAsyncTests() _commandProcessor = new SpyCommandProcessor(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory((_) => new MyEventMessageMapper()), - null); - messageMapperRegistry.Register(); + null, + new SimpleMessageMapperFactoryAsync((_) => new MyEventMessageMapperAsync()) + ); + messageMapperRegistry.RegisterAsync(); var connection = new Subscription( new SubscriptionName("test"), @@ -36,10 +37,10 @@ public MessageDispatcherRoutingAsyncTests() channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey"), runAsync: true); - _dispatcher = new Dispatcher(_commandProcessor, messageMapperRegistry, new List { connection }); + _dispatcher = new Dispatcher(_commandProcessor, new List { connection }, null, messageMapperRegistry); var @event = new MyEvent(); - var message = new MyEventMessageMapper().MapToMessage(@event); + var message = new MyEventMessageMapperAsync().MapToMessage(@event).Result; _channel.Enqueue(message); _dispatcher.State.Should().Be(DispatcherState.DS_AWAITING); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_has_a_new_connection_added_while_running.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_has_a_new_connection_added_while_running.cs index 122b04fb24..0eb539b0ee 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_has_a_new_connection_added_while_running.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_has_a_new_connection_added_while_running.cs @@ -55,7 +55,7 @@ public DispatcherAddNewConnectionTests() _subscription = new Subscription(new SubscriptionName("test"), noOfPerformers: 1, timeoutInMilliseconds: 1000, channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); _newSubscription = new Subscription(new SubscriptionName("newTest"), noOfPerformers: 1, timeoutInMilliseconds: 1000, channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); - _dispatcher = new Dispatcher(_commandProcessor, messageMapperRegistry, new List { _subscription }); + _dispatcher = new Dispatcher(_commandProcessor, new List { _subscription }, messageMapperRegistry); var @event = new MyEvent(); var message = new MyEventMessageMapper().MapToMessage(@event); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_is_asked_to_connect_a_channel_and_handler.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_is_asked_to_connect_a_channel_and_handler.cs index 8f70bc97c4..8bc3049664 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_is_asked_to_connect_a_channel_and_handler.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_is_asked_to_connect_a_channel_and_handler.cs @@ -58,7 +58,7 @@ public MessageDispatcherRoutingTests() channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); - _dispatcher = new Dispatcher(_commandProcessor, messageMapperRegistry, new List { connection }); + _dispatcher = new Dispatcher(_commandProcessor, new List { connection }, messageMapperRegistry); var @event = new MyEvent(); var message = new MyEventMessageMapper().MapToMessage(@event); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection.cs index a415264d54..0b1e885137 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection.cs @@ -53,7 +53,7 @@ public MessageDispatcherResetConnection() messageMapperRegistry.Register(); _subscription = new Subscription(new SubscriptionName("test"), noOfPerformers: 1, timeoutInMilliseconds: 1000, channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); - _dispatcher = new Dispatcher(_commandProcessor, messageMapperRegistry, new List { _subscription }); + _dispatcher = new Dispatcher(_commandProcessor, new List { _subscription }, messageMapperRegistry); var @event = new MyEvent(); var message = new MyEventMessageMapper().MapToMessage(@event); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection_after_all_connections_have_stopped.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection_after_all_connections_have_stopped.cs index e40490bafd..215e15423f 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection_after_all_connections_have_stopped.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_restarts_a_connection_after_all_connections_have_stopped.cs @@ -55,7 +55,7 @@ public DispatcherRestartConnectionTests() _subscription = new Subscription(new SubscriptionName("test"), noOfPerformers: 1, timeoutInMilliseconds: 100, channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); _newSubscription = new Subscription(new SubscriptionName("newTest"), noOfPerformers: 1, timeoutInMilliseconds: 100, channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); - _dispatcher = new Dispatcher(_commandProcessor, messageMapperRegistry, new List { _subscription, _newSubscription }); + _dispatcher = new Dispatcher(_commandProcessor, new List { _subscription, _newSubscription }, messageMapperRegistry); var @event = new MyEvent(); var message = new MyEventMessageMapper().MapToMessage(@event); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_shuts_a_connection.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_shuts_a_connection.cs index 0652478595..9d05d376a2 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_shuts_a_connection.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_shuts_a_connection.cs @@ -51,7 +51,7 @@ public MessageDispatcherShutConnectionTests() messageMapperRegistry.Register(); _subscription = new Subscription(new SubscriptionName("test"), noOfPerformers: 3, timeoutInMilliseconds: 1000, channelFactory: new InMemoryChannelFactory(channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); - _dispatcher = new Dispatcher(commandProcessor, messageMapperRegistry, new List { _subscription }); + _dispatcher = new Dispatcher(commandProcessor, new List { _subscription }, messageMapperRegistry); var @event = new MyEvent(); var message = new MyEventMessageMapper().MapToMessage(@event); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_different_types_of_performers.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_different_types_of_performers.cs index 603da47151..21758e7860 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_different_types_of_performers.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_different_types_of_performers.cs @@ -64,7 +64,7 @@ public MessageDispatcherMultipleConnectionTests() var myEventConnection = new Subscription(new SubscriptionName("test"), noOfPerformers: 1, timeoutInMilliseconds: 1000, channelFactory: new InMemoryChannelFactory(_eventChannel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); var myCommandConnection = new Subscription(new SubscriptionName("anothertest"), noOfPerformers: 1, timeoutInMilliseconds: 1000, channelFactory: new InMemoryChannelFactory(_commandChannel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); - _dispatcher = new Dispatcher(commandProcessor, messageMapperRegistry, new List { myEventConnection, myCommandConnection }); + _dispatcher = new Dispatcher(commandProcessor, new List { myEventConnection, myCommandConnection }, messageMapperRegistry); var @event = new MyEvent(); var eventMessage = new MyEventMessageMapper().MapToMessage(@event); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_multiple_performers.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_multiple_performers.cs index 6291d84d4c..d5fd2d38af 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_multiple_performers.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_dispatcher_starts_multiple_performers.cs @@ -52,7 +52,7 @@ public MessageDispatcherMultiplePerformerTests() messageMapperRegistry.Register(); var connection = new Subscription(new SubscriptionName("test"), noOfPerformers: 3, timeoutInMilliseconds: 100, channelFactory: new InMemoryChannelFactory(_channel), channelName: new ChannelName("fakeChannel"), routingKey: new RoutingKey("fakekey")); - _dispatcher = new Dispatcher(_commandProcessor, messageMapperRegistry, new List { connection }); + _dispatcher = new Dispatcher(_commandProcessor, new List { connection }, messageMapperRegistry); var @event = new MyEvent(); var message = new MyEventMessageMapper().MapToMessage(@event); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler_async.cs index 2983a0f318..beb9a2090b 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler_async.cs @@ -26,16 +26,18 @@ public MessagePumpDispatchAsyncTests() handlerFactory, new InMemoryRequestContextFactory(), new PolicyRegistry()); + + var commandProcessorProvider = new CommandProcessorProvider(commandProcessor); PipelineBuilder.ClearPipelineCache(); var channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), - null); - messageMapperRegistry.Register(); + null, + new SimpleMessageMapperFactoryAsync(_ => new MyEventMessageMapperAsync())); + messageMapperRegistry.RegisterAsync(); - _messagePump = new MessagePumpAsync(commandProcessor, messageMapperRegistry) + _messagePump = new MessagePumpAsync(commandProcessorProvider, messageMapperRegistry, null) { Channel = channel, TimeoutInMilliseconds = 5000 }; var message = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_EVENT), new MessageBody(JsonSerializer.Serialize(_myEvent))); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs index 6905b8ceb0..4d19cab050 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs @@ -43,13 +43,14 @@ public class MessagePumpEventProcessingDeferMessageActionTestsAsync public MessagePumpEventProcessingDeferMessageActionTestsAsync() { _commandProcessor = new SpyRequeueCommandProcessor(); + var commandProcessorProvider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), - null); - messageMapperRegistry.Register(); + null, + new SimpleMessageMapperFactoryAsync(_ => new MyEventMessageMapperAsync())); + messageMapperRegistry.RegisterAsync(); - _messagePump = new MessagePumpAsync(_commandProcessor, messageMapperRegistry) + _messagePump = new MessagePumpAsync(commandProcessorProvider, messageMapperRegistry, null) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; var msg = new TransformPipelineBuilder(messageMapperRegistry, null) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs index d9f8e716c2..8645451b0e 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs @@ -45,13 +45,14 @@ public class MessagePumpEventProcessingExceptionTestsAsync public MessagePumpEventProcessingExceptionTestsAsync() { _commandProcessor = new SpyExceptionCommandProcessor(); + var commandProcessorProvider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( - new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), - null); - messageMapperRegistry.Register(); + null, + new SimpleMessageMapperFactoryAsync(_ => new MyEventMessageMapperAsync())); + messageMapperRegistry.RegisterAsync(); - _messagePump = new MessagePumpAsync(_commandProcessor, messageMapperRegistry) + _messagePump = new MessagePumpAsync(commandProcessorProvider, messageMapperRegistry, null) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; diff --git a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs index 14685cff59..fa9c47193b 100644 --- a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs +++ b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher.cs @@ -84,7 +84,7 @@ public DispatchBuilderTests() _builder = DispatchBuilder.With() .CommandProcessorFactory(() =>new CommandProcessorProvider(commandProcessor)) - .MessageMappers(messageMapperRegistry, null, null) + .MessageMappers(messageMapperRegistry, null, null, null) .DefaultChannelFactory(new ChannelFactory(rmqMessageConsumerFactory)) .Subscriptions(new [] { diff --git a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs index e5d7920910..2473c48092 100644 --- a/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs +++ b/tests/Paramore.Brighter.RMQ.Tests/MessageDispatch/When_building_a_dispatcher_with_named_gateway.cs @@ -80,7 +80,7 @@ public DispatchBuilderWithNamedGateway() _builder = DispatchBuilder.With() .CommandProcessorFactory(() => new CommandProcessorProvider(commandProcessor)) - .MessageMappers(messageMapperRegistry, null, null) + .MessageMappers(messageMapperRegistry, null, null, null) .DefaultChannelFactory(new ChannelFactory(rmqMessageConsumerFactory)) .Subscriptions(new [] { From 3380a9a679cb10fb5382fadfca8962dabc672743 Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Fri, 5 Jan 2024 20:01:01 +0000 Subject: [PATCH 06/15] Fix issues around failing tests that use wrong transform pipeline --- ..._message_Then_message_is_requeued_until_rejectedAsync.cs | 5 +++-- ...hrows_unhandled_exception_Then_message_is_acked_async.cs | 5 +++-- ..._message_Then_message_is_requeued_until_rejectedAsync.cs | 6 ++++-- ...hrows_unhandled_exception_Then_message_is_acked_async.cs | 6 ++++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs index 860758ba70..c708995298 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs @@ -55,9 +55,10 @@ public MessagePumpCommandProcessingDeferMessageActionTestsAsync() Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; - var msg = new TransformPipelineBuilder(messageMapperRegistry, null) + var msg = new TransformPipelineBuilderAsync(messageMapperRegistry, null) .BuildWrapPipeline() - .Wrap(new MyCommand()); + .WrapAsync(new MyCommand()) + .Result; _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs index 54d89e82be..3cb2f320a2 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs @@ -58,9 +58,10 @@ public MessagePumpCommandProcessingExceptionTestsAsync() Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; - var msg = new TransformPipelineBuilder(messageMapperRegistry, null) + var msg = new TransformPipelineBuilderAsync(messageMapperRegistry, null) .BuildWrapPipeline() - .Wrap(new MyCommand()); + .WrapAsync(new MyCommand()) + .Result; _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs index 4d19cab050..46a7a97728 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejectedAsync.cs @@ -53,8 +53,10 @@ public MessagePumpEventProcessingDeferMessageActionTestsAsync() _messagePump = new MessagePumpAsync(commandProcessorProvider, messageMapperRegistry, null) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; - var msg = new TransformPipelineBuilder(messageMapperRegistry, null) - .BuildWrapPipeline().Wrap(new MyEvent()); + var msg = new TransformPipelineBuilderAsync(messageMapperRegistry, null) + .BuildWrapPipeline() + .WrapAsync(new MyEvent()) + .Result; _channel.Enqueue(msg); } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs index 8645451b0e..9a310b6aca 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs @@ -57,8 +57,10 @@ public MessagePumpEventProcessingExceptionTestsAsync() Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; - var msg = new TransformPipelineBuilder(messageMapperRegistry, null) - .BuildWrapPipeline().Wrap(new MyEvent()); + var msg = new TransformPipelineBuilderAsync(messageMapperRegistry, null) + .BuildWrapPipeline() + .WrapAsync(new MyEvent()) + .Result; _channel.Enqueue(msg); } From cfec65b34ae7db19bd5323dc7e136be89e624341 Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Fri, 5 Jan 2024 20:03:18 +0000 Subject: [PATCH 07/15] Remove redundant operation completed --- src/Paramore.Brighter.ServiceActivator/MessagePumpAsync.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Paramore.Brighter.ServiceActivator/MessagePumpAsync.cs b/src/Paramore.Brighter.ServiceActivator/MessagePumpAsync.cs index 397635a121..623751e339 100644 --- a/src/Paramore.Brighter.ServiceActivator/MessagePumpAsync.cs +++ b/src/Paramore.Brighter.ServiceActivator/MessagePumpAsync.cs @@ -131,8 +131,6 @@ private static TRequest RunTranslate(Func> act, Message future.ContinueWith(delegate { context.OperationCompleted(); }, TaskScheduler.Default); - context.OperationCompleted(); - // Pump continuations and propagate any exceptions context.RunOnCurrentThread(); From 79e086d74fa88162079b0a6795c2255a69764220 Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Wed, 10 Jan 2024 13:42:01 +0000 Subject: [PATCH 08/15] Remove non command provider constructor; add additional async versions of messagepump tests --- .../MessagePumpBlocking.cs | 13 --- ...n_throwing_defer_action_respect_redrive.cs | 3 +- .../TestDoubles/FailingEventMessageMapper.cs | 30 +------ .../FailingEventMessageMapperAsync.cs | 24 ++++++ .../TestDoubles/MyFailingMapperEvent.cs | 28 +++++++ ...d_retry_until_connection_re_established.cs | 7 +- ...d_retry_until_connection_re_established.cs | 8 +- ...Then_message_is_requeued_until_rejected.cs | 7 +- ...handled_exception_Then_message_is_acked.cs | 7 +- ...message_fails_to_be_mapped_to_a_request.cs | 7 +- ...e_unacceptable_message_limit_is_reached.cs | 10 ++- ...ceptable_message_limit_is_reached_async.cs | 76 +++++++++++++++++ ...e_fails_to_be_mapped_to_a_request_async.cs | 50 +++++++++++ ...is_dispatched_it_should_reach_a_handler.cs | 8 +- ...threshold_for_commands_has_been_reached.cs | 3 +- ...t_threshold_for_events_has_been_reached.cs | 3 +- ..._requeue_of_command_exception_is_thrown.cs | 3 +- ..._a_requeue_of_event_exception_is_thrown.cs | 3 +- ...Then_message_is_requeued_until_rejected.cs | 6 +- ...handled_exception_Then_message_is_acked.cs | 6 +- ...hen_an_unacceptable_message_is_recieved.cs | 7 +- ...n_unacceptable_message_limit_is_reached.cs | 8 +- ...a_channel_pump_out_to_command_processor.cs | 3 +- ...nel_pump_out_to_command_processor_async.cs | 81 ++++++++++++++++++ ...pump_on_a_thread_should_be_able_to_stop.cs | 13 ++- ...n_a_thread_should_be_able_to_stop_async.cs | 82 +++++++++++++++++++ ...try_limits_force_a_message_onto_the_DLQ.cs | 3 +- 27 files changed, 423 insertions(+), 76 deletions(-) create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/FailingEventMessageMapperAsync.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/MyFailingMapperEvent.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop_async.cs diff --git a/src/Paramore.Brighter.ServiceActivator/MessagePumpBlocking.cs b/src/Paramore.Brighter.ServiceActivator/MessagePumpBlocking.cs index 92b9c570d7..295ae165ba 100644 --- a/src/Paramore.Brighter.ServiceActivator/MessagePumpBlocking.cs +++ b/src/Paramore.Brighter.ServiceActivator/MessagePumpBlocking.cs @@ -54,19 +54,6 @@ public MessagePumpBlocking( _unwrapPipeline = transformPipelineBuilder.BuildUnwrapPipeline(); } - /// - /// Constructs a message pump - /// - /// A command processor - /// The registry of mappers - /// The factory that lets us create instances of transforms - public MessagePumpBlocking( - IAmACommandProcessor commandProcessor, - IAmAMessageMapperRegistry messageMapperRegistry, - IAmAMessageTransformerFactory messageTransformerFactory = null) : - this(new CommandProcessorProvider(commandProcessor), messageMapperRegistry, messageTransformerFactory) - {} - protected override void DispatchRequest(MessageHeader messageHeader, TRequest request) { s_logger.LogDebug("MessagePump: Dispatching message {Id} from {ChannelName} on thread # {ManagementThreadId}", request.Id, Thread.CurrentThread.ManagedThreadId, Channel.Name); diff --git a/tests/Paramore.Brighter.AWS.Tests/MessagingGateway/When_throwing_defer_action_respect_redrive.cs b/tests/Paramore.Brighter.AWS.Tests/MessagingGateway/When_throwing_defer_action_respect_redrive.cs index 463ffacb09..7f96b13533 100644 --- a/tests/Paramore.Brighter.AWS.Tests/MessagingGateway/When_throwing_defer_action_respect_redrive.cs +++ b/tests/Paramore.Brighter.AWS.Tests/MessagingGateway/When_throwing_defer_action_respect_redrive.cs @@ -89,6 +89,7 @@ public SnsReDrivePolicySDlqTests() requestContextFactory: new InMemoryRequestContextFactory(), policyRegistry: new PolicyRegistry() ); + var provider = new CommandProcessorProvider(_commandProcessor); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyDeferredCommandMessageMapper(_topicName)), @@ -97,7 +98,7 @@ public SnsReDrivePolicySDlqTests() messageMapperRegistry.Register(); //pump messages from a channel to a handler - in essence we are building our own dispatcher in this test - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3 }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/FailingEventMessageMapper.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/FailingEventMessageMapper.cs index 2edf1406ef..955c1c74fa 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/FailingEventMessageMapper.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/FailingEventMessageMapper.cs @@ -1,5 +1,5 @@ using System; -using System.Diagnostics; +using Newtonsoft.Json; namespace Paramore.Brighter.Core.Tests.MessageDispatch.TestDoubles { @@ -7,37 +7,13 @@ internal class FailingEventMessageMapper : IAmAMessageMapper(message.Body.Value); } } - - internal class MyFailingMapperEvent : IRequest - { - /// - /// Gets or sets the identifier. - /// - /// The identifier. - public Guid Id { get; set; } - public string MissingStringField { get; set; } - public int MissingIntField { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public MyFailingMapperEvent() - { - Id = new Guid(); - } - - /// - /// Gets or sets the span that this operation live within - /// - public Activity Span { get; set; } - } } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/FailingEventMessageMapperAsync.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/FailingEventMessageMapperAsync.cs new file mode 100644 index 0000000000..0945020c05 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/FailingEventMessageMapperAsync.cs @@ -0,0 +1,24 @@ +using System; +using System.Diagnostics; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Core.Tests.MessageDispatch.TestDoubles +{ + internal class FailingEventMessageMapperAsync : IAmAMessageMapperAsync + { + public Task MapToMessage(MyFailingMapperEvent request) + { + var tcs = new TaskCompletionSource(); + tcs.SetException(new JsonException()); + return tcs.Task; + } + + public Task MapToRequest(Message message) + { + var tcs = new TaskCompletionSource(); + tcs.SetException(new JsonException()); + return tcs.Task; + } + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/MyFailingMapperEvent.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/MyFailingMapperEvent.cs new file mode 100644 index 0000000000..8d9823dfc0 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/TestDoubles/MyFailingMapperEvent.cs @@ -0,0 +1,28 @@ +using System; +using System.Diagnostics; + +namespace Paramore.Brighter.Core.Tests.MessageDispatch.TestDoubles; + +internal class MyFailingMapperEvent : IRequest +{ + /// + /// Gets or sets the identifier. + /// + /// The identifier. + public Guid Id { get; set; } + public string MissingStringField { get; set; } + public int MissingIntField { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public MyFailingMapperEvent() + { + Id = new Guid(); + } + + /// + /// Gets or sets the span that this operation live within + /// + public Activity Span { get; set; } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_command_should_retry_until_connection_re_established.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_command_should_retry_until_connection_re_established.cs index 6bb9c516f4..0ad0d4ebeb 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_command_should_retry_until_connection_re_established.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_command_should_retry_until_connection_re_established.cs @@ -41,13 +41,16 @@ public class MessagePumpRetryCommandOnConnectionFailureTests public MessagePumpRetryCommandOnConnectionFailureTests() { _commandProcessor = new SpyCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); var channel = new FailingChannel { NumberOfRetries = 1 }; var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) - { Channel = channel, TimeoutInMilliseconds = 500, RequeueCount = -1 }; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = channel, TimeoutInMilliseconds = 500, RequeueCount = -1 + }; var command = new MyCommand(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_event_should_retry_until_connection_re_established.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_event_should_retry_until_connection_re_established.cs index 95953d2e29..3cf867bcc9 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_event_should_retry_until_connection_re_established.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_channel_failure_exception_is_thrown_for_event_should_retry_until_connection_re_established.cs @@ -30,6 +30,7 @@ THE SOFTWARE. */ using Xunit; using Paramore.Brighter.ServiceActivator; using System.Text.Json; +using FakeItEasy; namespace Paramore.Brighter.Core.Tests.MessageDispatch { @@ -41,14 +42,17 @@ public class MessagePumpRetryEventConnectionFailureTests public MessagePumpRetryEventConnectionFailureTests() { _commandProcessor = new SpyCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); var channel = new FailingChannel { NumberOfRetries = 1 }; var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) - { Channel = channel, TimeoutInMilliseconds = 500, RequeueCount = -1 }; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = channel, TimeoutInMilliseconds = 500, RequeueCount = -1 + }; var @event = new MyEvent(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs index 008fb56398..255bc9e0e3 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs @@ -43,13 +43,16 @@ public class MessagePumpCommandProcessingDeferMessageActionTests public MessagePumpCommandProcessingDeferMessageActionTests() { _commandProcessor = new SpyRequeueCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) - { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount + }; var msg = new TransformPipelineBuilder(messageMapperRegistry, null) .BuildWrapPipeline() diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs index c27b0160b9..cbb9c1471f 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs @@ -45,13 +45,16 @@ public class MessagePumpCommandProcessingExceptionTests public MessagePumpCommandProcessingExceptionTests() { _commandProcessor = new SpyExceptionCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) - { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount + }; var msg = new TransformPipelineBuilder(messageMapperRegistry, null) .BuildWrapPipeline() diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request.cs index 110bb59301..61e782a1f2 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request.cs @@ -18,14 +18,17 @@ public class MessagePumpFailingMessageTranslationTests public MessagePumpFailingMessageTranslationTests() { _commandProcessor = new SpyRequeueCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new FailingEventMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) - { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3, UnacceptableMessageLimit = 3 }; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3, UnacceptableMessageLimit = 3 + }; var unmappableMessage = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_EVENT), new MessageBody("{ \"Id\" : \"48213ADB-A085-4AFF-A42C-CF8209350CF7\" }")); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached.cs index b7d645a020..1b7dd83afe 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached.cs @@ -36,19 +36,21 @@ public class MessagePumpUnacceptableMessageLimitTests { private readonly IAmAMessagePump _messagePump; private readonly FakeChannel _channel; - private readonly SpyRequeueCommandProcessor _commandProcessor; public MessagePumpUnacceptableMessageLimitTests() { - _commandProcessor = new SpyRequeueCommandProcessor(); + SpyRequeueCommandProcessor commandProcessor = new(); + var provider = new CommandProcessorProvider(commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new FailingEventMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) - { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3, UnacceptableMessageLimit = 3 }; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3, UnacceptableMessageLimit = 3 + }; var unmappableMessage = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_EVENT), new MessageBody("{ \"Id\" : \"48213ADB-A085-4AFF-A42C-CF8209350CF7\" }")); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached_async.cs new file mode 100644 index 0000000000..cb88fd6d7b --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_and_the_unacceptable_message_limit_is_reached_async.cs @@ -0,0 +1,76 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageDispatch.TestDoubles; +using Xunit; +using Paramore.Brighter.ServiceActivator; +using Paramore.Brighter.ServiceActivator.TestHelpers; + +namespace Paramore.Brighter.Core.Tests.MessageDispatch +{ + public class MessagePumpUnacceptableMessageLimitTestsAsync + { + private readonly IAmAMessagePump _messagePump; + private readonly FakeChannel _channel; + + public MessagePumpUnacceptableMessageLimitTestsAsync() + { + SpyRequeueCommandProcessor commandProcessor = new(); + var provider = new CommandProcessorProvider(commandProcessor); + _channel = new FakeChannel(); + var messageMapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new FailingEventMessageMapperAsync())); + messageMapperRegistry.RegisterAsync(); + + _messagePump = new MessagePumpAsync(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3, UnacceptableMessageLimit = 3 + }; + + var unmappableMessage = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_EVENT), new MessageBody("{ \"Id\" : \"48213ADB-A085-4AFF-A42C-CF8209350CF7\" }")); + + _channel.Enqueue(unmappableMessage); + _channel.Enqueue(unmappableMessage); + _channel.Enqueue(unmappableMessage); + } + + [Fact] + public async Task When_A_Message_Fails_To_Be_Mapped_To_A_Request_And_The_Unacceptable_Message_Limit_Is_Reached() + { + var task = Task.Factory.StartNew(() => _messagePump.Run(), TaskCreationOptions.LongRunning); + await Task.Delay(1000); + + await Task.WhenAll(new[] { task }); + + //should_have_acknowledge_the_3_messages + _channel.AcknowledgeCount.Should().Be(3); + //should_dispose_the_input_channel + _channel.DisposeHappened.Should().BeTrue(); + } + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_async.cs new file mode 100644 index 0000000000..4b790ae962 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_fails_to_be_mapped_to_a_request_async.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.MessageDispatch.TestDoubles; +using Xunit; +using Paramore.Brighter.ServiceActivator; +using Paramore.Brighter.ServiceActivator.TestHelpers; + +namespace Paramore.Brighter.Core.Tests.MessageDispatch +{ + + public class MessagePumpFailingMessageTranslationTestsAsync + { + private readonly IAmAMessagePump _messagePump; + private readonly FakeChannel _channel; + + public MessagePumpFailingMessageTranslationTestsAsync() + { + SpyRequeueCommandProcessor commandProcessor = new(); + var provider = new CommandProcessorProvider(commandProcessor); + _channel = new FakeChannel(); + var messageMapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new FailingEventMessageMapperAsync())); + messageMapperRegistry.RegisterAsync(); + + _messagePump = new MessagePumpAsync(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3, UnacceptableMessageLimit = 3 + }; + + var unmappableMessage = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_EVENT), new MessageBody("{ \"Id\" : \"48213ADB-A085-4AFF-A42C-CF8209350CF7\" }")); + + _channel.Enqueue(unmappableMessage); + } + + [Fact] + public async Task When_A_Message_Fails_To_Be_Mapped_To_A_Request () + { + var task = Task.Factory.StartNew(() => _messagePump.Run(), TaskCreationOptions.LongRunning); + + _channel.Stop(); + + await Task.WhenAll(task); + + //should_have_acknowledge_the_message + _channel.AcknowledgeHappened.Should().BeTrue(); + } + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler.cs index d1f9d5de8d..455fbd43d2 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_message_is_dispatched_it_should_reach_a_handler.cs @@ -53,6 +53,8 @@ public MessagePumpDispatchTests() handlerFactory, new InMemoryRequestContextFactory(), new PolicyRegistry()); + + var provider = new CommandProcessorProvider(commandProcessor); PipelineBuilder.ClearPipelineCache(); @@ -63,8 +65,10 @@ public MessagePumpDispatchTests() null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(commandProcessor, messageMapperRegistry) - { Channel = channel, TimeoutInMilliseconds = 5000 }; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = channel, TimeoutInMilliseconds = 5000 + }; var message = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_EVENT), new MessageBody(JsonSerializer.Serialize(_myEvent, JsonSerialisationOptions.Options))); channel.Enqueue(message); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_commands_has_been_reached.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_commands_has_been_reached.cs index d3dd5805a1..b242c2872c 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_commands_has_been_reached.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_commands_has_been_reached.cs @@ -44,12 +44,13 @@ public class MessagePumpCommandRequeueCountThresholdTests public MessagePumpCommandRequeueCountThresholdTests() { _commandProcessor = new SpyRequeueCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3 }; _command = new MyCommand(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_events_has_been_reached.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_events_has_been_reached.cs index 809a0c5813..2305c61905 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_events_has_been_reached.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_count_threshold_for_events_has_been_reached.cs @@ -44,13 +44,14 @@ public class MessagePumpEventRequeueCountThresholdTests public MessagePumpEventRequeueCountThresholdTests() { _commandProcessor = new SpyRequeueCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3 }; _event = new MyEvent(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_command_exception_is_thrown.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_command_exception_is_thrown.cs index 69abbdf27a..5762519764 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_command_exception_is_thrown.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_command_exception_is_thrown.cs @@ -43,12 +43,13 @@ public class MessagePumpCommandRequeueTests public MessagePumpCommandRequeueTests() { _commandProcessor = new SpyRequeueCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyCommandMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = -1 }; var message1 = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_COMMAND), new MessageBody(JsonSerializer.Serialize(_command, JsonSerialisationOptions.Options))); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_event_exception_is_thrown.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_event_exception_is_thrown.cs index 5c925d5dd4..0c60f9e6b3 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_event_exception_is_thrown.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_a_requeue_of_event_exception_is_thrown.cs @@ -43,13 +43,14 @@ public class MessagePumpEventRequeueTests public MessagePumpEventRequeueTests() { _commandProcessor = new SpyRequeueCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = -1 }; _event = new MyEvent(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs index e12feb2811..e5a2dea1b4 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_a_defer_message_Then_message_is_requeued_until_rejected.cs @@ -43,13 +43,17 @@ public class MessagePumpEventProcessingDeferMessageActionTests public MessagePumpEventProcessingDeferMessageActionTests() { _commandProcessor = new SpyRequeueCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount + }; var transformPipelineBuilder = new TransformPipelineBuilder(messageMapperRegistry, null); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs index 2ceffec575..79d1e4f7fc 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs @@ -45,13 +45,17 @@ public class MessagePumpEventProcessingExceptionTests public MessagePumpEventProcessingExceptionTests() { _commandProcessor = new SpyExceptionCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount }; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = _requeueCount + }; var transformPipelineBuilder = new TransformPipelineBuilder(messageMapperRegistry, null); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs index 3953993835..2884e67327 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs @@ -43,14 +43,17 @@ public class MessagePumpUnacceptableMessageTests public MessagePumpUnacceptableMessageTests() { _commandProcessor = new SpyRequeueCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) - { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3 }; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3 + }; var myMessage = JsonSerializer.Serialize(new MyEvent()); var unacceptableMessage = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_UNACCEPTABLE), new MessageBody(myMessage)); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs index c18c6a3576..4ad4be3430 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs @@ -42,14 +42,18 @@ public class MessagePumpUnacceptableMessageLimitBreachedTests public MessagePumpUnacceptableMessageLimitBreachedTests() { _commandProcessor = new SpyRequeueCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); + _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) - { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3, UnacceptableMessageLimit = 3}; + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3, UnacceptableMessageLimit = 3 + }; var unacceptableMessage1 = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_UNACCEPTABLE), new MessageBody("")); var unacceptableMessage2 = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_UNACCEPTABLE), new MessageBody("")); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor.cs index 329517214e..90874940bd 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor.cs @@ -43,13 +43,14 @@ public class MessagePumpToCommandProcessorTests public MessagePumpToCommandProcessorTests() { _commandProcessor = new SpyCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); _channel = new FakeChannel(); var messagerMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), null); messagerMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messagerMapperRegistry) + _messagePump = new MessagePumpBlocking(provider, messagerMapperRegistry, null) { Channel = _channel, TimeoutInMilliseconds = 5000 }; _event = new MyEvent(); diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor_async.cs new file mode 100644 index 0000000000..c5ddd3c302 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_reading_a_message_from_a_channel_pump_out_to_command_processor_async.cs @@ -0,0 +1,81 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.CommandProcessors.TestDoubles; +using Paramore.Brighter.Core.Tests.MessageDispatch.TestDoubles; +using Xunit; +using Paramore.Brighter.ServiceActivator; +using Paramore.Brighter.ServiceActivator.TestHelpers; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Paramore.Brighter.Core.Tests.MessageDispatch +{ + public class MessagePumpToCommandProcessorTestsAsync + { + private readonly IAmAMessagePump _messagePump; + private readonly FakeChannel _channel; + private readonly SpyCommandProcessor _commandProcessor; + private readonly MyEvent _event; + + public MessagePumpToCommandProcessorTestsAsync() + { + _commandProcessor = new SpyCommandProcessor(); + var provider = new CommandProcessorProvider(_commandProcessor); + _channel = new FakeChannel(); + var messagerMapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyEventMessageMapperAsync())); + messagerMapperRegistry.RegisterAsync(); + + _messagePump = new MessagePumpAsync(provider, messagerMapperRegistry, null) + { Channel = _channel, TimeoutInMilliseconds = 5000 }; + + _event = new MyEvent(); + + var message = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_EVENT), new MessageBody(JsonSerializer.Serialize(_event, JsonSerialisationOptions.Options))); + _channel.Enqueue(message); + var quitMessage = new Message(new MessageHeader(Guid.Empty, "", MessageType.MT_QUIT), new MessageBody("")); + _channel.Enqueue(quitMessage); + } + + [Fact] + public void When_Reading_A_Message_From_A_Channel_Pump_Out_To_Command_Processor() + { + //although run does not return a Task, it will process handler and mapper asynchronously, using our + //synchronization context. Messages should retain ordering of callbacks, so our test message should be processed + //before we quit + _messagePump.Run(); + + //_should_send_the_message_via_the_command_processor + _commandProcessor.Commands[0].Should().Be(CommandType.PublishAsync); + //_should_convert_the_message_into_an_event + _commandProcessor.Observe().Should().Be(_event); + //_should_dispose_the_input_channel + _channel.DisposeHappened.Should().BeTrue(); + } + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop.cs index 887579db37..9252b3dcb8 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop.cs @@ -36,21 +36,20 @@ namespace Paramore.Brighter.Core.Tests.MessageDispatch { public class PerformerCanStopTests { - private readonly Performer _performer; - private readonly SpyCommandProcessor _commandProcessor; private readonly FakeChannel _channel; private readonly Task _performerTask; public PerformerCanStopTests() { - _commandProcessor = new SpyCommandProcessor(); + SpyCommandProcessor commandProcessor = new(); + var provider = new CommandProcessorProvider(commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), null); messageMapperRegistry.Register(); - var messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry); + var messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null); messagePump.Channel = _channel; messagePump.TimeoutInMilliseconds = 5000; @@ -58,9 +57,9 @@ public PerformerCanStopTests() var message = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_EVENT), new MessageBody(JsonSerializer.Serialize(@event, JsonSerialisationOptions.Options))); _channel.Enqueue(message); - _performer = new Performer(_channel, messagePump); - _performerTask = _performer.Run(); - _performer.Stop(); + Performer performer = new(_channel, messagePump); + _performerTask = performer.Run(); + performer.Stop(); } #pragma warning disable xUnit1031 diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop_async.cs new file mode 100644 index 0000000000..adc2ba5631 --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_running_a_message_pump_on_a_thread_should_be_able_to_stop_async.cs @@ -0,0 +1,82 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.CommandProcessors.TestDoubles; +using Paramore.Brighter.Core.Tests.MessageDispatch.TestDoubles; +using Xunit; +using Paramore.Brighter.ServiceActivator; +using Paramore.Brighter.ServiceActivator.TestHelpers; +using System.Text.Json; + +namespace Paramore.Brighter.Core.Tests.MessageDispatch +{ + public class PerformerCanStopTestsAsync + { + private readonly FakeChannel _channel; + private readonly Task _performerTask; + + public PerformerCanStopTestsAsync() + { + SpyCommandProcessor commandProcessor = new(); + var provider = new CommandProcessorProvider(commandProcessor); + _channel = new FakeChannel(); + var messageMapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyEventMessageMapperAsync())); + messageMapperRegistry.RegisterAsync(); + + var messagePump = new MessagePumpAsync(provider, messageMapperRegistry, null); + messagePump.Channel = _channel; + messagePump.TimeoutInMilliseconds = 5000; + + var @event = new MyEvent(); + var message = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_EVENT), new MessageBody(JsonSerializer.Serialize(@event, JsonSerialisationOptions.Options))); + _channel.Enqueue(message); + + Performer performer = new(_channel, messagePump); + _performerTask = performer.Run(); + performer.Stop(); + } + +#pragma warning disable xUnit1031 + [Fact] + public void When_Running_A_Message_Pump_On_A_Thread_Should_Be_Able_To_Stop() + { + _performerTask.Wait(); + + //_should_terminate_successfully + _performerTask.IsCompleted.Should().BeTrue(); + //_should_not_have_errored + _performerTask.IsFaulted.Should().BeFalse(); + //_should_not_show_as_cancelled + _performerTask.IsCanceled.Should().BeFalse(); + //_should_have_consumed_the_messages_in_the_channel + _channel.Length.Should().Be(0); + } +#pragma warning restore xUnit1031 + } +} diff --git a/tests/Paramore.Brighter.RMQ.Tests/MessagingGateway/When_retry_limits_force_a_message_onto_the_DLQ.cs b/tests/Paramore.Brighter.RMQ.Tests/MessagingGateway/When_retry_limits_force_a_message_onto_the_DLQ.cs index 880cd9719d..1e7ee9928d 100644 --- a/tests/Paramore.Brighter.RMQ.Tests/MessagingGateway/When_retry_limits_force_a_message_onto_the_DLQ.cs +++ b/tests/Paramore.Brighter.RMQ.Tests/MessagingGateway/When_retry_limits_force_a_message_onto_the_DLQ.cs @@ -84,6 +84,7 @@ public RMQMessageConsumerRetryDLQTests() requestContextFactory: new InMemoryRequestContextFactory(), policyRegistry: new PolicyRegistry() ); + var provider = new CommandProcessorProvider(_commandProcessor); //pump messages from a channel to a handler - in essence we are building our own dispatcher in this test var messageMapperRegistry = new MessageMapperRegistry( @@ -91,7 +92,7 @@ public RMQMessageConsumerRetryDLQTests() null); messageMapperRegistry.Register(); - _messagePump = new MessagePumpBlocking(_commandProcessor, messageMapperRegistry) + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) { Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3 }; From 46cd384a64dc8bcc0caaa33940f7c79c8ee3c783 Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Wed, 10 Jan 2024 15:13:52 +0000 Subject: [PATCH 09/15] Increase async test coverage --- ...hen_an_unacceptable_message_is_recieved.cs | 5 +- ..._unacceptable_message_is_recieved_async.cs | 78 ++++++++++++++++++ ...n_unacceptable_message_limit_is_reached.cs | 5 +- ...ceptable_message_limit_is_reached_async.cs | 82 +++++++++++++++++++ 4 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved_async.cs create mode 100644 tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached_async.cs diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs index 2884e67327..bc6b0dfc0b 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved.cs @@ -38,12 +38,11 @@ public class MessagePumpUnacceptableMessageTests { private readonly IAmAMessagePump _messagePump; private readonly FakeChannel _channel; - private readonly SpyRequeueCommandProcessor _commandProcessor; public MessagePumpUnacceptableMessageTests() { - _commandProcessor = new SpyRequeueCommandProcessor(); - var provider = new CommandProcessorProvider(_commandProcessor); + SpyRequeueCommandProcessor commandProcessor = new(); + var provider = new CommandProcessorProvider(commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( new SimpleMessageMapperFactory(_ => new MyEventMessageMapper()), diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved_async.cs new file mode 100644 index 0000000000..9d64bf118a --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_is_recieved_async.cs @@ -0,0 +1,78 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using System.Text.Json; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.CommandProcessors.TestDoubles; +using Paramore.Brighter.Core.Tests.MessageDispatch.TestDoubles; +using Xunit; +using Paramore.Brighter.ServiceActivator; +using Paramore.Brighter.ServiceActivator.TestHelpers; + +namespace Paramore.Brighter.Core.Tests.MessageDispatch +{ + public class AsyncMessagePumpUnacceptableMessageTests + { + private readonly IAmAMessagePump _messagePump; + private readonly FakeChannel _channel; + + public AsyncMessagePumpUnacceptableMessageTests() + { + SpyRequeueCommandProcessor commandProcessor = new(); + var provider = new CommandProcessorProvider(commandProcessor); + _channel = new FakeChannel(); + var messageMapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyEventMessageMapperAsync())); + messageMapperRegistry.RegisterAsync(); + + _messagePump = new MessagePumpAsync(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3 + }; + + var myMessage = JsonSerializer.Serialize(new MyEvent()); + var unacceptableMessage = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_UNACCEPTABLE), new MessageBody(myMessage)); + + _channel.Enqueue(unacceptableMessage); + } + + [Fact] + public async Task When_An_Unacceptable_Message_Is_Recieved() + { + var task = Task.Factory.StartNew(() => _messagePump.Run(), TaskCreationOptions.LongRunning); + await Task.Delay(1000); + + var quitMessage = new Message(new MessageHeader(Guid.Empty, "", MessageType.MT_QUIT), new MessageBody("")); + _channel.Enqueue(quitMessage); + + await Task.WhenAll(new[] { task }); + + //should_acknowledge_the_message + _channel.AcknowledgeHappened.Should().BeTrue(); + } + } +} diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs index 4ad4be3430..b3740f6db3 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached.cs @@ -37,12 +37,11 @@ public class MessagePumpUnacceptableMessageLimitBreachedTests { private readonly IAmAMessagePump _messagePump; private readonly FakeChannel _channel; - private readonly SpyRequeueCommandProcessor _commandProcessor; public MessagePumpUnacceptableMessageLimitBreachedTests() { - _commandProcessor = new SpyRequeueCommandProcessor(); - var provider = new CommandProcessorProvider(_commandProcessor); + SpyRequeueCommandProcessor commandProcessor = new(); + var provider = new CommandProcessorProvider(commandProcessor); _channel = new FakeChannel(); var messageMapperRegistry = new MessageMapperRegistry( diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached_async.cs new file mode 100644 index 0000000000..351ceff10e --- /dev/null +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/When_an_unacceptable_message_limit_is_reached_async.cs @@ -0,0 +1,82 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2014 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Paramore.Brighter.Core.Tests.CommandProcessors.TestDoubles; +using Paramore.Brighter.Core.Tests.MessageDispatch.TestDoubles; +using Xunit; +using Paramore.Brighter.ServiceActivator; +using Paramore.Brighter.ServiceActivator.TestHelpers; + +namespace Paramore.Brighter.Core.Tests.MessageDispatch +{ + public class AsyncMessagePumpUnacceptableMessageLimitBreachedTests + { + private readonly IAmAMessagePump _messagePump; + private readonly FakeChannel _channel; + + public AsyncMessagePumpUnacceptableMessageLimitBreachedTests() + { + SpyRequeueCommandProcessor commandProcessor = new(); + var provider = new CommandProcessorProvider(commandProcessor); + + _channel = new FakeChannel(); + var messageMapperRegistry = new MessageMapperRegistry( + null, + new SimpleMessageMapperFactoryAsync(_ => new MyEventMessageMapperAsync())); + messageMapperRegistry.RegisterAsync(); + + _messagePump = new MessagePumpBlocking(provider, messageMapperRegistry, null) + { + Channel = _channel, TimeoutInMilliseconds = 5000, RequeueCount = 3, UnacceptableMessageLimit = 3 + }; + + var unacceptableMessage1 = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_UNACCEPTABLE), new MessageBody("")); + var unacceptableMessage2 = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_UNACCEPTABLE), new MessageBody("")); + var unacceptableMessage3 = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_UNACCEPTABLE), new MessageBody("")); + var unacceptableMessage4 = new Message(new MessageHeader(Guid.NewGuid(), "MyTopic", MessageType.MT_UNACCEPTABLE), new MessageBody("")); + + _channel.Enqueue(unacceptableMessage1); + _channel.Enqueue(unacceptableMessage2); + _channel.Enqueue(unacceptableMessage3); + _channel.Enqueue(unacceptableMessage4); + } + + [Fact] + public async Task When_An_Unacceptable_Message_Limit_Is_Reached() + { + var task = Task.Factory.StartNew(() => _messagePump.Run(), TaskCreationOptions.LongRunning); + + await Task.WhenAll(task); + + //should_have_acknowledge_the_3_messages + _channel.AcknowledgeCount.Should().Be(3); + //should_dispose_the_input_channel + _channel.DisposeHappened.Should().BeTrue(); + } + } +} + From d392c748707cba1f8dbdafb10c9f34c0f7eb1f3d Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Wed, 10 Jan 2024 18:06:36 +0000 Subject: [PATCH 10/15] Update Kafka sample to use async pipeline --- ...andler.cs => GreetingEventHandlerAsync.cs} | 8 ++- ....cs => GreetingEventMessageMapperAsync.cs} | 36 +++++----- .../GreetingsReceiverConsole/Program.cs | 22 +++--- .../GreetingsSender/Program.cs | 69 ++++++++++--------- .../GreetingsSender/TimedMessageGenerator.cs | 4 +- 5 files changed, 72 insertions(+), 67 deletions(-) rename samples/KafkaSchemaRegistry/Greetings/Ports/CommandHandlers/{GreetingEventHandler.cs => GreetingEventHandlerAsync.cs} (82%) rename samples/KafkaSchemaRegistry/Greetings/Ports/Mappers/{GreetingEventMessageMapper.cs => GreetingEventMessageMapperAsync.cs} (63%) diff --git a/samples/KafkaSchemaRegistry/Greetings/Ports/CommandHandlers/GreetingEventHandler.cs b/samples/KafkaSchemaRegistry/Greetings/Ports/CommandHandlers/GreetingEventHandlerAsync.cs similarity index 82% rename from samples/KafkaSchemaRegistry/Greetings/Ports/CommandHandlers/GreetingEventHandler.cs rename to samples/KafkaSchemaRegistry/Greetings/Ports/CommandHandlers/GreetingEventHandlerAsync.cs index a5f5fa2a1b..108fce926a 100644 --- a/samples/KafkaSchemaRegistry/Greetings/Ports/CommandHandlers/GreetingEventHandler.cs +++ b/samples/KafkaSchemaRegistry/Greetings/Ports/CommandHandlers/GreetingEventHandlerAsync.cs @@ -23,21 +23,23 @@ THE SOFTWARE. */ #endregion using System; +using System.Threading; +using System.Threading.Tasks; using Greetings.Ports.Commands; using Paramore.Brighter; namespace Greetings.Ports.CommandHandlers { - public class GreetingEventHandler : RequestHandler + public class GreetingEventHandlerAsync : RequestHandlerAsync { - public override GreetingEvent Handle(GreetingEvent @event) + public override async Task HandleAsync(GreetingEvent @event, CancellationToken cancellationToken = default) { Console.WriteLine("Received Greeting. Message Follows"); Console.WriteLine("----------------------------------"); Console.WriteLine(@event.Greeting); Console.WriteLine("----------------------------------"); Console.WriteLine("Message Ends"); - return base.Handle(@event); + return await base.HandleAsync(@event, cancellationToken); } } } diff --git a/samples/KafkaSchemaRegistry/Greetings/Ports/Mappers/GreetingEventMessageMapper.cs b/samples/KafkaSchemaRegistry/Greetings/Ports/Mappers/GreetingEventMessageMapperAsync.cs similarity index 63% rename from samples/KafkaSchemaRegistry/Greetings/Ports/Mappers/GreetingEventMessageMapper.cs rename to samples/KafkaSchemaRegistry/Greetings/Ports/Mappers/GreetingEventMessageMapperAsync.cs index 68546c8500..55d64fea12 100644 --- a/samples/KafkaSchemaRegistry/Greetings/Ports/Mappers/GreetingEventMessageMapper.cs +++ b/samples/KafkaSchemaRegistry/Greetings/Ports/Mappers/GreetingEventMessageMapperAsync.cs @@ -23,9 +23,9 @@ THE SOFTWARE. */ #endregion using System.Net.Mime; +using System.Threading.Tasks; using Greetings.Ports.Commands; using Confluent.Kafka; -using Confluent.Kafka.SyncOverAsync; using Confluent.SchemaRegistry; using Confluent.SchemaRegistry.Serdes; using Paramore.Brighter; @@ -33,38 +33,36 @@ THE SOFTWARE. */ namespace Greetings.Ports.Mappers { - public class GreetingEventMessageMapper : IAmAMessageMapper + public class GreetingEventMessageMapperAsync(ISchemaRegistryClient schemaRegistryClient) + : IAmAMessageMapperAsync { - private readonly ISchemaRegistryClient _schemaRegistryClient; private readonly string _partitionKey = "KafkaTestQueueExample_Partition_One"; - private SerializationContext _serializationContext; + private readonly SerializationContext _serializationContext = new(MessageComponentType.Value, Topic); private const string Topic = "greeting.event"; - public GreetingEventMessageMapper(ISchemaRegistryClient schemaRegistryClient) - { - _schemaRegistryClient = schemaRegistryClient; - //We care about ensuring that we serialize the body using the Confluent tooling, as it registers and validates schema - _serializationContext = new SerializationContext(MessageComponentType.Value, Topic); - } - - public Message MapToMessage(GreetingEvent request) + public async Task MapToMessage(GreetingEvent request) { var header = new MessageHeader(messageId: request.Id, topic: Topic, messageType: MessageType.MT_EVENT); //This uses the Confluent JSON serializer, which wraps Newtonsoft but also performs schema registration and validation - var serializer = new JsonSerializer(_schemaRegistryClient, ConfluentJsonSerializationConfig.SerdesJsonSerializerConfig(), ConfluentJsonSerializationConfig.NJsonSchemaGeneratorSettings()).AsSyncOverAsync(); - var s = serializer.Serialize(request, _serializationContext); + var serializer = new JsonSerializer( + schemaRegistryClient, + ConfluentJsonSerializationConfig.SerdesJsonSerializerConfig(), + ConfluentJsonSerializationConfig.NJsonSchemaGeneratorSettings() + ); + + var s = await serializer.SerializeAsync(request, _serializationContext); var body = new MessageBody(s, MediaTypeNames.Application.Octet, CharacterEncoding.Raw); header.PartitionKey = _partitionKey; - var message = new Message(header, body); - return message; + return new Message(header, body); } - public GreetingEvent MapToRequest(Message message) + public async Task MapToRequest(Message message) { - var deserializer = new JsonDeserializer().AsSyncOverAsync(); + var deserializer = new JsonDeserializer(); //This uses the Confluent JSON serializer, which wraps Newtonsoft but also performs schema registration and validation - var greetingCommand = deserializer.Deserialize(message.Body.Bytes, message.Body.Bytes is null, _serializationContext); + var greetingCommand + = await deserializer.DeserializeAsync(message.Body.Bytes, message.Body.Bytes is null, _serializationContext); return greetingCommand; } diff --git a/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs b/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs index 2d76782d81..3dad8cdaed 100644 --- a/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs +++ b/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs @@ -42,7 +42,7 @@ namespace GreetingsReceiverConsole { public class Program { - public static async Task Main(string[] args) + public static Task Main(string[] args) { var host = Host.CreateDefaultBuilder(args) .ConfigureHostConfiguration(configurationBuilder => @@ -68,7 +68,8 @@ public static async Task Main(string[] args) timeoutInMilliseconds: 100, offsetDefault: AutoOffsetReset.Earliest, commitBatchSize: 5, - sweepUncommittedOffsetsIntervalMs: 10000) + sweepUncommittedOffsetsIntervalMs: 10000, + isAsync: true) }; //We take a direct dependency on the schema registry in the message mapper @@ -76,16 +77,17 @@ public static async Task Main(string[] args) var cachedSchemaRegistryClient = new CachedSchemaRegistryClient(schemaRegistryConfig); services.AddSingleton(cachedSchemaRegistryClient); - - //create the gateway - var consumerFactory = new KafkaMessageConsumerFactory( - new KafkaMessagingGatewayConfiguration { Name = "paramore.brighter", BootStrapServers = new[] { "localhost:9092" } } - ); - services.AddServiceActivator(options => { options.Subscriptions = subscriptions; - options.ChannelFactory = new ChannelFactory(consumerFactory); + options.ChannelFactory = new ChannelFactory( + new KafkaMessageConsumerFactory( + new KafkaMessagingGatewayConfiguration + { + Name = "paramore.brighter", + BootStrapServers = new[] { "localhost:9092" } + } + )); }).AutoFromAssemblies(); @@ -94,7 +96,7 @@ public static async Task Main(string[] args) .UseConsoleLifetime() .Build(); - await host.RunAsync(); + return host.RunAsync(); } } } diff --git a/samples/KafkaSchemaRegistry/GreetingsSender/Program.cs b/samples/KafkaSchemaRegistry/GreetingsSender/Program.cs index 027005bbe3..6163e25ec8 100644 --- a/samples/KafkaSchemaRegistry/GreetingsSender/Program.cs +++ b/samples/KafkaSchemaRegistry/GreetingsSender/Program.cs @@ -27,7 +27,6 @@ THE SOFTWARE. */ using System; using System.IO; using System.Threading.Tasks; -using System.Transactions; using Confluent.SchemaRegistry; using Greetings.Ports.Commands; using Microsoft.Extensions.Configuration; @@ -42,9 +41,9 @@ THE SOFTWARE. */ namespace GreetingsSender { - internal static class Program + public static class Program { - static async Task Main(string[] args) + public static Task Main(string[] args) { var host = Host.CreateDefaultBuilder(args) .ConfigureHostConfiguration(configurationBuilder => @@ -53,7 +52,7 @@ static async Task Main(string[] args) configurationBuilder.AddJsonFile("appsettings.json", optional: true); configurationBuilder.AddCommandLine(args); }) - .ConfigureLogging((context, builder) => + .ConfigureLogging((_, builder) => { builder.ClearProviders(); builder.AddConsole(); @@ -61,32 +60,6 @@ static async Task Main(string[] args) }) .ConfigureServices((hostContext, services) => { - var retryPolicy = Policy.Handle().WaitAndRetry(new[] - { - TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), - TimeSpan.FromMilliseconds(150) - }); - - var circuitBreakerPolicy = - Policy.Handle().CircuitBreaker(1, TimeSpan.FromMilliseconds(500)); - - var retryPolicyAsync = Policy.Handle().WaitAndRetryAsync(new[] - { - TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), - TimeSpan.FromMilliseconds(150) - }); - - var circuitBreakerPolicyAsync = Policy.Handle() - .CircuitBreakerAsync(1, TimeSpan.FromMilliseconds(500)); - - var policyRegistry = new PolicyRegistry - { - {CommandProcessor.RETRYPOLICY, retryPolicy}, - {CommandProcessor.CIRCUITBREAKER, circuitBreakerPolicy}, - {CommandProcessor.RETRYPOLICYASYNC, retryPolicyAsync}, - {CommandProcessor.CIRCUITBREAKERASYNC, circuitBreakerPolicyAsync} - }; - //We take a direct dependency on the schema registry in the message mapper var schemaRegistryConfig = new SchemaRegistryConfig { Url = "http://localhost:8081"}; var cachedSchemaRegistryClient = new CachedSchemaRegistryClient(schemaRegistryConfig); @@ -98,7 +71,7 @@ static async Task Main(string[] args) Name = "paramore.brighter.greetingsender", BootStrapServers = new[] {"localhost:9092"} }, - new KafkaPublication[] + new[] { new KafkaPublication { @@ -112,7 +85,7 @@ static async Task Main(string[] args) services.AddBrighter(options => { - options.PolicyRegistry = policyRegistry; + options.PolicyRegistry = RegisterPolicies(); }) .UseExternalBus((configure) => { @@ -125,7 +98,37 @@ static async Task Main(string[] args) .UseConsoleLifetime() .Build(); - await host.RunAsync(); + return host.RunAsync(); + } + + private static PolicyRegistry RegisterPolicies() + { + var retryPolicy = Policy.Handle().WaitAndRetry(new[] + { + TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), + TimeSpan.FromMilliseconds(150) + }); + + var circuitBreakerPolicy = + Policy.Handle().CircuitBreaker(1, TimeSpan.FromMilliseconds(500)); + + var retryPolicyAsync = Policy.Handle().WaitAndRetryAsync(new[] + { + TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), + TimeSpan.FromMilliseconds(150) + }); + + var circuitBreakerPolicyAsync = Policy.Handle() + .CircuitBreakerAsync(1, TimeSpan.FromMilliseconds(500)); + + var policyRegistry = new PolicyRegistry + { + {CommandProcessor.RETRYPOLICY, retryPolicy}, + {CommandProcessor.CIRCUITBREAKER, circuitBreakerPolicy}, + {CommandProcessor.RETRYPOLICYASYNC, retryPolicyAsync}, + {CommandProcessor.CIRCUITBREAKERASYNC, circuitBreakerPolicyAsync} + }; + return policyRegistry; } } } diff --git a/samples/KafkaSchemaRegistry/GreetingsSender/TimedMessageGenerator.cs b/samples/KafkaSchemaRegistry/GreetingsSender/TimedMessageGenerator.cs index 6a58314b1d..b89e3a5f89 100644 --- a/samples/KafkaSchemaRegistry/GreetingsSender/TimedMessageGenerator.cs +++ b/samples/KafkaSchemaRegistry/GreetingsSender/TimedMessageGenerator.cs @@ -41,13 +41,13 @@ public Task StopAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - private void DoWork(object state) + private async void DoWork(object state) { _iteration++; var greetingEvent = new GreetingEvent{ Id = Guid.NewGuid(), Greeting = $"Hello # {_iteration}"}; - _processor.Post(greetingEvent); + await _processor.PostAsync(greetingEvent); _logger.LogInformation("Sending message with id {Id} and greeting {Request}", greetingEvent.Id, greetingEvent.Greeting); From a5b330fffbd009e3c7158f49c3b22cbcbf6ad550 Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Tue, 23 Jan 2024 18:52:04 +0000 Subject: [PATCH 11/15] Moe to async for new mapper approach --- .../Handlers/AddGreetingHandlerAsync.cs | 35 +++++----- .../Handlers/AddPersonHandlerAsync.cs | 12 +--- .../Handlers/DeletePersonHandlerAsync.cs | 18 ++--- .../FIndGreetingsForPersonHandlerAsync.cs | 12 +--- .../Handlers/FindPersonByNameHandlerAsync.cs | 12 +--- .../Mappers/GreetingMadeMessageMapper.cs | 23 ------- .../Mappers/GreetingMadeMessageMapperAsync.cs | 28 ++++++++ .../Mappers/GreetingMadeMessageMapper.cs | 19 ------ .../Mappers/GreetingMadeMessageMapperAsync.cs | 24 +++++++ .../SalutationReceivedMessageMapper.cs | 22 ------ .../SalutationReceivedMessageMapperAsync.cs | 27 ++++++++ .../SalutationAnalytics/Program.cs | 2 +- .../Handlers/GreetingMadeHandler.cs | 67 ------------------- .../Handlers/GreetingMadeHandlerAsync.cs | 60 +++++++++++++++++ .../SalutationPorts/Policies/Retry.cs | 12 ++-- .../Policies/SalutationPolicy.cs | 4 +- .../Handlers/AddGreetingHandlerAsync.cs | 36 ++++------ .../Handlers/AddPersonHandlerAsync.cs | 13 +--- .../Handlers/DeletePersonHandlerAsync.cs | 13 +--- .../FIndGreetingsForPersonHandlerAsync.cs | 14 +--- .../Handlers/FindPersonByNameHandlerAsync.cs | 15 +---- .../Mappers/GreetingMadeMessageMapper.cs | 23 ------- .../Mappers/GreetingMadeMessageMapperAsync.cs | 28 ++++++++ .../Mappers/GreetingMadeMessageMapper.cs | 19 ------ .../Mappers/GreetingMadeMessageMapperAsync.cs | 24 +++++++ .../SalutationReceivedMessageMapper.cs | 22 ------ .../SalutationReceivedMessageMapperAsync.cs | 27 ++++++++ .../Handlers/GreetingMadeHandler.cs | 45 ++++++------- .../SalutationPorts/Policies/Retry.cs | 13 ++-- .../Policies/SalutationPolicy.cs | 4 +- .../Handlers/AddGreetingHandlerAsync.cs | 34 ++++------ .../Handlers/AddPersonHandlerAsync.cs | 15 ++--- .../Handlers/DeletePersonHandlerAsync.cs | 15 ++--- .../FIndGreetingsForPersonHandlerAsync.cs | 12 +--- .../Handlers/FindPersonByNameHandlerAsync.cs | 12 +--- .../Mappers/GreetingMadeMessageMapper.cs | 23 ------- .../Mappers/GreetingMadeMessageMapperAsync.cs | 28 ++++++++ .../Mappers/GreetingMadeMessageMapper.cs | 19 ------ .../Mappers/GreetingMadeMessageMapperAsync.cs | 24 +++++++ .../SalutationReceivedMessageMapper.cs | 22 ------ .../SalutationReceivedMessageMapperAsync.cs | 27 ++++++++ .../Handlers/GreetingMadeHandler.cs | 45 ++++++------- 42 files changed, 436 insertions(+), 513 deletions(-) delete mode 100644 samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs create mode 100644 samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs delete mode 100644 samples/WebAPI_Dapper/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs create mode 100644 samples/WebAPI_Dapper/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs delete mode 100644 samples/WebAPI_Dapper/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs create mode 100644 samples/WebAPI_Dapper/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs delete mode 100644 samples/WebAPI_Dapper/SalutationPorts/Handlers/GreetingMadeHandler.cs create mode 100644 samples/WebAPI_Dapper/SalutationPorts/Handlers/GreetingMadeHandlerAsync.cs delete mode 100644 samples/WebAPI_Dynamo/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs create mode 100644 samples/WebAPI_Dynamo/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs delete mode 100644 samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs create mode 100644 samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs delete mode 100644 samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs create mode 100644 samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs delete mode 100644 samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs create mode 100644 samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs delete mode 100644 samples/WebAPI_EFCore/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs create mode 100644 samples/WebAPI_EFCore/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs delete mode 100644 samples/WebAPI_EFCore/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs create mode 100644 samples/WebAPI_EFCore/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs diff --git a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs index 88e87c9c07..01630bb8ed 100644 --- a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs +++ b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs @@ -13,20 +13,15 @@ namespace GreetingsPorts.Handlers { - public class AddGreetingHandlerAsync: RequestHandlerAsync + public class AddGreetingHandlerAsync( + IAmATransactionConnectionProvider transactionProvider, + IAmACommandProcessor postBox, + ILogger logger) + : RequestHandlerAsync { - private readonly IAmACommandProcessor _postBox; - private readonly ILogger _logger; - private readonly IAmATransactionConnectionProvider _transactionProvider; + //We want to take the dependency on the same instance that will be used via the Outbox, so use the marker interface - public AddGreetingHandlerAsync(IAmATransactionConnectionProvider transactionProvider, IAmACommandProcessor postBox, ILogger logger) - { - _transactionProvider = transactionProvider; //We want to take the dependency on the same instance that will be used via the Outbox, so use the marker interface - _postBox = postBox; - _logger = logger; - } - [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task HandleAsync(AddGreeting addGreeting, CancellationToken cancellationToken = default) @@ -36,8 +31,8 @@ public override async Task HandleAsync(AddGreeting addGreeting, Can //We use the unit of work to grab connection and transaction, because Outbox needs //to share them 'behind the scenes' - var conn = await _transactionProvider.GetConnectionAsync(cancellationToken); - var tx = await _transactionProvider.GetTransactionAsync(cancellationToken); + var conn = await transactionProvider.GetConnectionAsync(cancellationToken); + var tx = await transactionProvider.GetTransactionAsync(cancellationToken); try { var people = await conn.QueryAsync( @@ -58,30 +53,30 @@ await conn.ExecuteAsync( tx); //Now write the message we want to send to the Db in the same transaction. - posts.Add(await _postBox.DepositPostAsync( + posts.Add(await postBox.DepositPostAsync( new GreetingMade(greeting.Greet()), - _transactionProvider, + transactionProvider, cancellationToken: cancellationToken)); //commit both new greeting and outgoing message - await _transactionProvider.CommitAsync(cancellationToken); + await transactionProvider.CommitAsync(cancellationToken); } } catch (Exception e) { - _logger.LogError(e, "Exception thrown handling Add Greeting request"); + logger.LogError(e, "Exception thrown handling Add Greeting request"); //it went wrong, rollback the entity change and the downstream message - await _transactionProvider.RollbackAsync(cancellationToken); + await transactionProvider.RollbackAsync(cancellationToken); return await base.HandleAsync(addGreeting, cancellationToken); } finally { - _transactionProvider.Close(); + transactionProvider.Close(); } //Send this message via a transport. We need the ids to send just the messages here, not all outstanding ones. //Alternatively, you can let the Sweeper do this, but at the cost of increased latency - await _postBox.ClearOutboxAsync(posts, cancellationToken:cancellationToken); + await postBox.ClearOutboxAsync(posts, cancellationToken:cancellationToken); return await base.HandleAsync(addGreeting, cancellationToken); } diff --git a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs index f5e0d276d9..72c59588f4 100644 --- a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs +++ b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs @@ -8,20 +8,14 @@ namespace GreetingsPorts.Handlers { - public class AddPersonHandlerAsync : RequestHandlerAsync + public class AddPersonHandlerAsync(IAmARelationalDbConnectionProvider relationalDbConnectionProvider) + : RequestHandlerAsync { - private readonly IAmARelationalDbConnectionProvider _relationalDbConnectionProvider; - - public AddPersonHandlerAsync(IAmARelationalDbConnectionProvider relationalDbConnectionProvider) - { - _relationalDbConnectionProvider = relationalDbConnectionProvider; - } - [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task HandleAsync(AddPerson addPerson, CancellationToken cancellationToken = default) { - await using var connection = await _relationalDbConnectionProvider.GetConnectionAsync(cancellationToken); + await using var connection = await relationalDbConnectionProvider.GetConnectionAsync(cancellationToken); await connection.ExecuteAsync("insert into Person (Name) values (@Name)", new {Name = addPerson.Name}); return await base.HandleAsync(addPerson, cancellationToken); } diff --git a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs index fe2731a534..0ce8e0b3db 100644 --- a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs +++ b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs @@ -12,22 +12,16 @@ namespace GreetingsPorts.Handlers { - public class DeletePersonHandlerAsync : RequestHandlerAsync + public class DeletePersonHandlerAsync( + IAmARelationalDbConnectionProvider relationalDbConnectionProvider, + ILogger logger) + : RequestHandlerAsync { - private readonly IAmARelationalDbConnectionProvider _relationalDbConnectionProvider; - private readonly ILogger _logger; - - public DeletePersonHandlerAsync(IAmARelationalDbConnectionProvider relationalDbConnectionProvider, ILogger logger) - { - _relationalDbConnectionProvider = relationalDbConnectionProvider; - _logger = logger; - } - [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task HandleAsync(DeletePerson deletePerson, CancellationToken cancellationToken = default) { - var connection = await _relationalDbConnectionProvider.GetConnectionAsync(cancellationToken); + var connection = await relationalDbConnectionProvider.GetConnectionAsync(cancellationToken); var tx = await connection.BeginTransactionAsync(cancellationToken); try { @@ -54,7 +48,7 @@ await connection.ExecuteAsync("delete from Person where Id = @Id", } catch (Exception e) { - _logger.LogError(e, "Exception thrown handling Add Greeting request"); + logger.LogError(e, "Exception thrown handling Add Greeting request"); //it went wrong, rollback the entity change and the downstream message await tx.RollbackAsync(cancellationToken); return await base.HandleAsync(deletePerson, cancellationToken); diff --git a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs index 45752cff49..63a7ec5204 100644 --- a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs +++ b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs @@ -14,15 +14,9 @@ namespace GreetingsPorts.Handlers { - public class FIndGreetingsForPersonHandlerAsync : QueryHandlerAsync + public class FIndGreetingsForPersonHandlerAsync(IAmARelationalDbConnectionProvider relationalDbConnectionProvider) + : QueryHandlerAsync { - private readonly IAmARelationalDbConnectionProvider _relationalDbConnectionProvider; - - public FIndGreetingsForPersonHandlerAsync(IAmARelationalDbConnectionProvider relationalDbConnectionProvider) - { - _relationalDbConnectionProvider = relationalDbConnectionProvider; - } - [QueryLogging(0)] [RetryableQuery(1, Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task ExecuteAsync(FindGreetingsForPerson query, CancellationToken cancellationToken = new CancellationToken()) @@ -34,7 +28,7 @@ public FIndGreetingsForPersonHandlerAsync(IAmARelationalDbConnectionProvider rel var sql = @"select p.Id, p.Name, g.Id, g.Message from Person p inner join Greeting g on g.Recipient_Id = p.Id"; - await using var connection = await _relationalDbConnectionProvider.GetConnectionAsync(cancellationToken); + await using var connection = await relationalDbConnectionProvider.GetConnectionAsync(cancellationToken); var people = await connection.QueryAsync(sql, (person, greeting) => { person.Greetings.Add(greeting); diff --git a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs index 7ada5290dc..6ffbba43da 100644 --- a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs +++ b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs @@ -13,20 +13,14 @@ namespace GreetingsPorts.Handlers { - public class FindPersonByNameHandlerAsync : QueryHandlerAsync + public class FindPersonByNameHandlerAsync(IAmARelationalDbConnectionProvider relationalDbConnectionProvider) + : QueryHandlerAsync { - private readonly IAmARelationalDbConnectionProvider _relationalDbConnectionProvider; - - public FindPersonByNameHandlerAsync(IAmARelationalDbConnectionProvider relationalDbConnectionProvider) - { - _relationalDbConnectionProvider = relationalDbConnectionProvider; - } - [QueryLogging(0)] [RetryableQuery(1, Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task ExecuteAsync(FindPersonByName query, CancellationToken cancellationToken = new CancellationToken()) { - await using var connection = await _relationalDbConnectionProvider .GetConnectionAsync(cancellationToken); + await using var connection = await relationalDbConnectionProvider .GetConnectionAsync(cancellationToken); var people = await connection.QueryAsync("select * from Person where name = @name", new {name = query.Name}); var person = people.SingleOrDefault(); diff --git a/samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs b/samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs deleted file mode 100644 index 19dc67d0a8..0000000000 --- a/samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json; -using GreetingsPorts.Requests; -using Paramore.Brighter; - - -namespace GreetingsWeb.Mappers -{ - public class GreetingMadeMessageMapper : IAmAMessageMapper - { - public Message MapToMessage(GreetingMade request) - { - var header = new MessageHeader(messageId: request.Id, topic: "GreetingMade", messageType: MessageType.MT_EVENT); - var body = new MessageBody(System.Text.Json.JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.General))); - var message = new Message(header, body); - return message; - } - - public GreetingMade MapToRequest(Message message) - { - throw new System.NotImplementedException(); - } - } -} diff --git a/samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs b/samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs new file mode 100644 index 0000000000..619789d036 --- /dev/null +++ b/samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs @@ -0,0 +1,28 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using GreetingsPorts.Requests; +using Paramore.Brighter; + + +namespace GreetingsWeb.Mappers +{ + public class GreetingMadeMessageMapperAsync : IAmAMessageMapperAsync + { + public async Task MapToMessage(GreetingMade request) + { + //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using + //TaskCompletionSource for a Task over sync instead + var header = new MessageHeader(messageId: request.Id, topic: "GreetingMade", messageType: MessageType.MT_EVENT); + using var ms = new MemoryStream(); + await JsonSerializer.SerializeAsync(ms, request, new JsonSerializerOptions(JsonSerializerDefaults.General)); + var body = new MessageBody(ms.ToArray()); + return new Message(header, body); + } + + public Task MapToRequest(Message message) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs b/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs deleted file mode 100644 index f96c47a9c7..0000000000 --- a/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Text.Json; -using Paramore.Brighter; -using SalutationPorts.Requests; - -namespace SalutationAnalytics.Mappers -{ - public class GreetingMadeMessageMapper : IAmAMessageMapper - { - public Message MapToMessage(GreetingMade request) - { - throw new System.NotImplementedException(); - } - - public GreetingMade MapToRequest(Message message) - { - return JsonSerializer.Deserialize(message.Body.Value, JsonSerialisationOptions.Options); - } - } -} diff --git a/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs b/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs new file mode 100644 index 0000000000..c63a222a59 --- /dev/null +++ b/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using Paramore.Brighter; +using SalutationPorts.Requests; + +namespace SalutationAnalytics.Mappers +{ + public class GreetingMadeMessageMapperAsync : IAmAMessageMapperAsync + { + public Task MapToMessage(GreetingMade request) + { + throw new System.NotImplementedException(); + } + + public async Task MapToRequest(Message message) + { + //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using + //TaskCompletionSource for a Task over sync instead + using var ms = new MemoryStream(message.Body.Bytes); + return await JsonSerializer.DeserializeAsync(ms, JsonSerialisationOptions.Options); + } + } +} diff --git a/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs b/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs deleted file mode 100644 index bdbb2c1061..0000000000 --- a/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json; -using Paramore.Brighter; -using SalutationPorts.Requests; - -namespace SalutationAnalytics.Mappers -{ - public class SalutationReceivedMessageMapper : IAmAMessageMapper - { - public Message MapToMessage(SalutationReceived request) - { - var header = new MessageHeader(messageId: request.Id, topic: "SalutationReceived", messageType: MessageType.MT_EVENT); - var body = new MessageBody(System.Text.Json.JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.General))); - var message = new Message(header, body); - return message; - } - - public SalutationReceived MapToRequest(Message message) - { - throw new System.NotImplementedException(); - } - } -} diff --git a/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs b/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs new file mode 100644 index 0000000000..4785897a68 --- /dev/null +++ b/samples/WebAPI_Dapper/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs @@ -0,0 +1,27 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using Paramore.Brighter; +using SalutationPorts.Requests; + +namespace SalutationAnalytics.Mappers +{ + public class SalutationReceivedMessageMapperAsync : IAmAMessageMapperAsync + { + public async Task MapToMessage(SalutationReceived request) + { + //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using + //TaskCompletionSource for a Task over sync instead + var header = new MessageHeader(messageId: request.Id, topic: "SalutationReceived", messageType: MessageType.MT_EVENT); + using var ms = new MemoryStream(); + await JsonSerializer.SerializeAsync(ms, request, new JsonSerializerOptions(JsonSerializerDefaults.General)); + var body = new MessageBody(ms.ToArray()); + return new Message(header, body); + } + + public Task MapToRequest(Message message) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs b/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs index 26d7119a90..8e6b971992 100644 --- a/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs +++ b/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs @@ -31,7 +31,7 @@ namespace SalutationAnalytics { - class Program + internal class Program { public static async Task Main(string[] args) { diff --git a/samples/WebAPI_Dapper/SalutationPorts/Handlers/GreetingMadeHandler.cs b/samples/WebAPI_Dapper/SalutationPorts/Handlers/GreetingMadeHandler.cs deleted file mode 100644 index 0fca6ec34d..0000000000 --- a/samples/WebAPI_Dapper/SalutationPorts/Handlers/GreetingMadeHandler.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Common; -using Dapper; -using Microsoft.Extensions.Logging; -using Paramore.Brighter; -using Paramore.Brighter.Inbox.Attributes; -using Paramore.Brighter.Logging.Attributes; -using Paramore.Brighter.Policies.Attributes; -using SalutationEntities; -using SalutationPorts.Requests; - -namespace SalutationPorts.Handlers -{ - public class GreetingMadeHandler : RequestHandler - { - private readonly IAmABoxTransactionProvider _transactionConnectionProvider; - private readonly IAmACommandProcessor _postBox; - private readonly ILogger _logger; - - public GreetingMadeHandler(IAmABoxTransactionProvider transactionConnectionProvider, IAmACommandProcessor postBox, ILogger logger) - { - _transactionConnectionProvider = transactionConnectionProvider; - _postBox = postBox; - _logger = logger; - } - - [UseInbox(step:0, contextKey: typeof(GreetingMadeHandler), onceOnly: true )] - [RequestLogging(step: 1, timing: HandlerTiming.Before)] - [UsePolicy(step:2, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICY)] - public override GreetingMade Handle(GreetingMade @event) - { - var posts = new List(); - - var tx = _transactionConnectionProvider.GetTransaction(); - var conn = tx.Connection; - try - { - var salutation = new Salutation(@event.Greeting); - - conn.Execute( - "insert into Salutation (greeting) values (@greeting)", - new {greeting = salutation.Greeting}, - tx); - - posts.Add(_postBox.DepositPost( - new SalutationReceived(DateTimeOffset.Now), - _transactionConnectionProvider)); - - _transactionConnectionProvider.Commit(); - } - catch (Exception e) - { - _logger.LogError(e, "Could not save salutation"); - - //if it went wrong rollback entity write and Outbox write - _transactionConnectionProvider.Rollback(); - - return base.Handle(@event); - } - - _postBox.ClearOutbox(posts.ToArray()); - - return base.Handle(@event); - } - } -} diff --git a/samples/WebAPI_Dapper/SalutationPorts/Handlers/GreetingMadeHandlerAsync.cs b/samples/WebAPI_Dapper/SalutationPorts/Handlers/GreetingMadeHandlerAsync.cs new file mode 100644 index 0000000000..78d85318f2 --- /dev/null +++ b/samples/WebAPI_Dapper/SalutationPorts/Handlers/GreetingMadeHandlerAsync.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; +using Dapper; +using Microsoft.Extensions.Logging; +using Paramore.Brighter; +using Paramore.Brighter.Inbox.Attributes; +using Paramore.Brighter.Logging.Attributes; +using Paramore.Brighter.Policies.Attributes; +using SalutationEntities; +using SalutationPorts.Requests; + +namespace SalutationPorts.Handlers +{ + public class GreetingMadeHandlerAsync( + IAmABoxTransactionProvider transactionConnectionProvider, + IAmACommandProcessor postBox, + ILogger logger) + : RequestHandlerAsync + { + [UseInboxAsync(step:0, contextKey: typeof(GreetingMadeHandlerAsync), onceOnly: true )] + [RequestLoggingAsync(step: 1, timing: HandlerTiming.Before)] + [UsePolicyAsync(step:2, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] + public override async Task HandleAsync(GreetingMade @event, CancellationToken cancellationToken = default) + { + var posts = new List(); + + var tx = await transactionConnectionProvider.GetTransactionAsync(cancellationToken); + var conn = tx.Connection; + try + { + var salutation = new Salutation(@event.Greeting); + + await conn.ExecuteAsync( + "insert into Salutation (greeting) values (@greeting)", + new {greeting = salutation.Greeting}, + tx); + + posts.Add(await postBox.DepositPostAsync(new SalutationReceived(DateTimeOffset.Now), transactionConnectionProvider, cancellationToken: cancellationToken)); + + await transactionConnectionProvider.CommitAsync(cancellationToken); + } + catch (Exception e) + { + logger.LogError(e, "Could not save salutation"); + + //if it went wrong rollback entity write and Outbox write + await transactionConnectionProvider.RollbackAsync(cancellationToken); + + return await base.HandleAsync(@event, cancellationToken); + } + + postBox.ClearOutbox(posts.ToArray()); + + return await base.HandleAsync(@event, cancellationToken); + } + } +} diff --git a/samples/WebAPI_Dapper/SalutationPorts/Policies/Retry.cs b/samples/WebAPI_Dapper/SalutationPorts/Policies/Retry.cs index 58013a5a3f..89274e51e3 100644 --- a/samples/WebAPI_Dapper/SalutationPorts/Policies/Retry.cs +++ b/samples/WebAPI_Dapper/SalutationPorts/Policies/Retry.cs @@ -7,21 +7,21 @@ namespace SalutationPorts.Policies { public static class Retry { - public const string RETRYPOLICY = "SalutationPorts.Policies.RetryPolicy"; - public const string EXPONENTIAL_RETRYPOLICY = "SalutationPorts.Policies.ExponenttialRetryPolicy"; + public const string RETRYPOLICYASYNC = "SalutationPorts.Policies.RetryPolicy"; + public const string EXPONENTIAL_RETRYPOLICYASYNC = "SalutationPorts.Policies.ExponenttialRetryPolicy"; - public static RetryPolicy GetSimpleHandlerRetryPolicy() + public static AsyncRetryPolicy GetSimpleHandlerRetryPolicyAsync() { - return Policy.Handle().WaitAndRetry(new[] + return Policy.Handle().WaitAndRetryAsync(new[] { TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(150) }); } - public static RetryPolicy GetExponentialHandlerRetryPolicy() + public static AsyncRetryPolicy GetExponentialHandlerRetryPolicyAsync() { var delay = Backoff.ExponentialBackoff(TimeSpan.FromMilliseconds(100), retryCount: 5, fastFirst: true); - return Policy.Handle().WaitAndRetry(delay); + return Policy.Handle().WaitAndRetryAsync(delay); } } } diff --git a/samples/WebAPI_Dapper/SalutationPorts/Policies/SalutationPolicy.cs b/samples/WebAPI_Dapper/SalutationPorts/Policies/SalutationPolicy.cs index 28024f22a7..38daa8c9cb 100644 --- a/samples/WebAPI_Dapper/SalutationPorts/Policies/SalutationPolicy.cs +++ b/samples/WebAPI_Dapper/SalutationPorts/Policies/SalutationPolicy.cs @@ -11,8 +11,8 @@ public SalutationPolicy() private void AddSalutationPolicies() { - Add(Retry.RETRYPOLICY, Retry.GetSimpleHandlerRetryPolicy()); - Add(Retry.EXPONENTIAL_RETRYPOLICY, Retry.GetExponentialHandlerRetryPolicy()); + Add(Retry.RETRYPOLICYASYNC, Retry.GetSimpleHandlerRetryPolicyAsync()); + Add(Retry.EXPONENTIAL_RETRYPOLICYASYNC, Retry.GetExponentialHandlerRetryPolicyAsync()); } } } diff --git a/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs b/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs index 38ed398c55..bef4df9148 100644 --- a/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs +++ b/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs @@ -14,20 +14,12 @@ namespace GreetingsPorts.Handlers { - public class AddGreetingHandlerAsync: RequestHandlerAsync + public class AddGreetingHandlerAsync( + IAmADynamoDbTransactionProvider transactionProvider, + IAmACommandProcessor postBox, + ILogger logger) + : RequestHandlerAsync { - private readonly IAmADynamoDbTransactionProvider _transactionProvider; - private readonly IAmACommandProcessor _postBox; - private readonly ILogger _logger; - - - public AddGreetingHandlerAsync(IAmADynamoDbTransactionProvider transactionProvider, IAmACommandProcessor postBox, ILogger logger) - { - _transactionProvider = transactionProvider; - _postBox = postBox; - _logger = logger; - } - [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task HandleAsync(AddGreeting addGreeting, CancellationToken cancellationToken = default) @@ -36,8 +28,8 @@ public override async Task HandleAsync(AddGreeting addGreeting, Can //We use the unit of work to grab connection and transaction, because Outbox needs //to share them 'behind the scenes' - var context = new DynamoDBContext(_transactionProvider.DynamoDb); - var transaction = await _transactionProvider.GetTransactionAsync(cancellationToken); + var context = new DynamoDBContext(transactionProvider.DynamoDb); + var transaction = await transactionProvider.GetTransactionAsync(cancellationToken); try { var person = await context.LoadAsync(addGreeting.Name, cancellationToken); @@ -55,29 +47,29 @@ public override async Task HandleAsync(AddGreeting addGreeting, Can }); //Now write the message we want to send to the Db in the same transaction. - posts.Add(await _postBox.DepositPostAsync( + posts.Add(await postBox.DepositPostAsync( new GreetingMade(addGreeting.Greeting), - _transactionProvider, + transactionProvider, cancellationToken: cancellationToken)); //commit both new greeting and outgoing message - await _transactionProvider.CommitAsync(cancellationToken); + await transactionProvider.CommitAsync(cancellationToken); } catch (Exception e) { - _logger.LogError(e, "Exception thrown handling Add Greeting request"); + logger.LogError(e, "Exception thrown handling Add Greeting request"); //it went wrong, rollback the entity change and the downstream message - await _transactionProvider.RollbackAsync(cancellationToken); + await transactionProvider.RollbackAsync(cancellationToken); return await base.HandleAsync(addGreeting, cancellationToken); } finally { - _transactionProvider.Close(); + transactionProvider.Close(); } //Send this message via a transport. We need the ids to send just the messages here, not all outstanding ones. //Alternatively, you can let the Sweeper do this, but at the cost of increased latency - await _postBox.ClearOutboxAsync(posts, cancellationToken:cancellationToken); + await postBox.ClearOutboxAsync(posts, cancellationToken:cancellationToken); return await base.HandleAsync(addGreeting, cancellationToken); } diff --git a/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs b/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs index 90df9aa8ad..421e15e039 100644 --- a/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs +++ b/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs @@ -1,7 +1,6 @@ using System.Threading; using System.Threading.Tasks; using Amazon.DynamoDBv2.DataModel; -using Amazon.DynamoDBv2.Model; using GreetingsEntities; using GreetingsPorts.Requests; using Paramore.Brighter; @@ -11,20 +10,14 @@ namespace GreetingsPorts.Handlers { - public class AddPersonHandlerAsync : RequestHandlerAsync + public class AddPersonHandlerAsync(IAmADynamoDbConnectionProvider dynamoDbConnectionProvider) + : RequestHandlerAsync { - private readonly IAmADynamoDbConnectionProvider _dynamoDbConnectionProvider; - - public AddPersonHandlerAsync(IAmADynamoDbConnectionProvider dynamoDbConnectionProvider) - { - _dynamoDbConnectionProvider = dynamoDbConnectionProvider; - } - [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task HandleAsync(AddPerson addPerson, CancellationToken cancellationToken = default) { - var context = new DynamoDBContext(_dynamoDbConnectionProvider.DynamoDb); + var context = new DynamoDBContext(dynamoDbConnectionProvider.DynamoDb); await context.SaveAsync(new Person { Name = addPerson.Name }, cancellationToken); return await base.HandleAsync(addPerson, cancellationToken); diff --git a/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs b/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs index af1fdbecc2..c47b94b004 100644 --- a/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs +++ b/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs @@ -1,7 +1,6 @@ using System.Threading; using System.Threading.Tasks; using Amazon.DynamoDBv2.DataModel; -using Amazon.DynamoDBv2.Model; using GreetingsPorts.Requests; using Paramore.Brighter; using Paramore.Brighter.DynamoDb; @@ -10,20 +9,14 @@ namespace GreetingsPorts.Handlers { - public class DeletePersonHandlerAsync : RequestHandlerAsync + public class DeletePersonHandlerAsync(IAmADynamoDbConnectionProvider dynamoDbConnectionProvider) + : RequestHandlerAsync { - private readonly IAmADynamoDbConnectionProvider _dynamoDbConnectionProvider; - - public DeletePersonHandlerAsync(IAmADynamoDbConnectionProvider dynamoDbConnectionProvider) - { - _dynamoDbConnectionProvider = dynamoDbConnectionProvider; - } - [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task HandleAsync(DeletePerson deletePerson, CancellationToken cancellationToken = default) { - var context = new DynamoDBContext(_dynamoDbConnectionProvider.DynamoDb); + var context = new DynamoDBContext(dynamoDbConnectionProvider.DynamoDb); await context.DeleteAsync(deletePerson.Name, cancellationToken); return await base.HandleAsync(deletePerson, cancellationToken); diff --git a/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs b/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs index feb9290b80..9dfc767f25 100644 --- a/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs +++ b/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs @@ -2,12 +2,10 @@ using System.Threading; using System.Threading.Tasks; using Amazon.DynamoDBv2.DataModel; -using Amazon.DynamoDBv2.Model; using GreetingsEntities; using GreetingsPorts.Policies; using GreetingsPorts.Requests; using GreetingsPorts.Responses; -using Paramore.Brighter; using Paramore.Brighter.DynamoDb; using Paramore.Darker; using Paramore.Darker.Policies; @@ -15,20 +13,14 @@ namespace GreetingsPorts.Handlers { - public class FIndGreetingsForPersonHandlerAsync : QueryHandlerAsync + public class FIndGreetingsForPersonHandlerAsync(IAmADynamoDbConnectionProvider dynamoDbConnectionProvider) + : QueryHandlerAsync { - private readonly IAmADynamoDbConnectionProvider _dynamoDbConnectionProvider; - - public FIndGreetingsForPersonHandlerAsync(IAmADynamoDbConnectionProvider dynamoDbConnectionProvider) - { - _dynamoDbConnectionProvider = dynamoDbConnectionProvider; - } - [QueryLogging(0)] [RetryableQuery(1, Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task ExecuteAsync(FindGreetingsForPerson query, CancellationToken cancellationToken = new CancellationToken()) { - var context = new DynamoDBContext(_dynamoDbConnectionProvider.DynamoDb); + var context = new DynamoDBContext(dynamoDbConnectionProvider.DynamoDb); var person = await context.LoadAsync(query.Name, cancellationToken); diff --git a/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs b/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs index b12ab3abc6..e4ce2923ce 100644 --- a/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs +++ b/samples/WebAPI_Dynamo/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs @@ -1,14 +1,11 @@ using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Amazon.DynamoDBv2.DataModel; -using Amazon.DynamoDBv2.Model; using GreetingsEntities; using GreetingsPorts.Policies; using GreetingsPorts.Requests; using GreetingsPorts.Responses; -using Paramore.Brighter; using Paramore.Brighter.DynamoDb; using Paramore.Darker; using Paramore.Darker.Policies; @@ -16,20 +13,14 @@ namespace GreetingsPorts.Handlers { - public class FindPersonByNameHandlerAsync : QueryHandlerAsync + public class FindPersonByNameHandlerAsync(IAmADynamoDbConnectionProvider dynamoDbConnectionProvider) + : QueryHandlerAsync { - private readonly IAmADynamoDbConnectionProvider _dynamoDbConnectionProvider; - - public FindPersonByNameHandlerAsync(IAmADynamoDbConnectionProvider dynamoDbConnectionProvider) - { - _dynamoDbConnectionProvider = dynamoDbConnectionProvider; - } - [QueryLogging(0)] [RetryableQuery(1, Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task ExecuteAsync(FindPersonByName query, CancellationToken cancellationToken = new CancellationToken()) { - var context = new DynamoDBContext(_dynamoDbConnectionProvider.DynamoDb); + var context = new DynamoDBContext(dynamoDbConnectionProvider.DynamoDb); var person = await context.LoadAsync(query.Name, cancellationToken); diff --git a/samples/WebAPI_Dynamo/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs b/samples/WebAPI_Dynamo/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs deleted file mode 100644 index 19dc67d0a8..0000000000 --- a/samples/WebAPI_Dynamo/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json; -using GreetingsPorts.Requests; -using Paramore.Brighter; - - -namespace GreetingsWeb.Mappers -{ - public class GreetingMadeMessageMapper : IAmAMessageMapper - { - public Message MapToMessage(GreetingMade request) - { - var header = new MessageHeader(messageId: request.Id, topic: "GreetingMade", messageType: MessageType.MT_EVENT); - var body = new MessageBody(System.Text.Json.JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.General))); - var message = new Message(header, body); - return message; - } - - public GreetingMade MapToRequest(Message message) - { - throw new System.NotImplementedException(); - } - } -} diff --git a/samples/WebAPI_Dynamo/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs b/samples/WebAPI_Dynamo/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs new file mode 100644 index 0000000000..619789d036 --- /dev/null +++ b/samples/WebAPI_Dynamo/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs @@ -0,0 +1,28 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using GreetingsPorts.Requests; +using Paramore.Brighter; + + +namespace GreetingsWeb.Mappers +{ + public class GreetingMadeMessageMapperAsync : IAmAMessageMapperAsync + { + public async Task MapToMessage(GreetingMade request) + { + //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using + //TaskCompletionSource for a Task over sync instead + var header = new MessageHeader(messageId: request.Id, topic: "GreetingMade", messageType: MessageType.MT_EVENT); + using var ms = new MemoryStream(); + await JsonSerializer.SerializeAsync(ms, request, new JsonSerializerOptions(JsonSerializerDefaults.General)); + var body = new MessageBody(ms.ToArray()); + return new Message(header, body); + } + + public Task MapToRequest(Message message) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs b/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs deleted file mode 100644 index f96c47a9c7..0000000000 --- a/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Text.Json; -using Paramore.Brighter; -using SalutationPorts.Requests; - -namespace SalutationAnalytics.Mappers -{ - public class GreetingMadeMessageMapper : IAmAMessageMapper - { - public Message MapToMessage(GreetingMade request) - { - throw new System.NotImplementedException(); - } - - public GreetingMade MapToRequest(Message message) - { - return JsonSerializer.Deserialize(message.Body.Value, JsonSerialisationOptions.Options); - } - } -} diff --git a/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs b/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs new file mode 100644 index 0000000000..c63a222a59 --- /dev/null +++ b/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using Paramore.Brighter; +using SalutationPorts.Requests; + +namespace SalutationAnalytics.Mappers +{ + public class GreetingMadeMessageMapperAsync : IAmAMessageMapperAsync + { + public Task MapToMessage(GreetingMade request) + { + throw new System.NotImplementedException(); + } + + public async Task MapToRequest(Message message) + { + //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using + //TaskCompletionSource for a Task over sync instead + using var ms = new MemoryStream(message.Body.Bytes); + return await JsonSerializer.DeserializeAsync(ms, JsonSerialisationOptions.Options); + } + } +} diff --git a/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs b/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs deleted file mode 100644 index bdbb2c1061..0000000000 --- a/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json; -using Paramore.Brighter; -using SalutationPorts.Requests; - -namespace SalutationAnalytics.Mappers -{ - public class SalutationReceivedMessageMapper : IAmAMessageMapper - { - public Message MapToMessage(SalutationReceived request) - { - var header = new MessageHeader(messageId: request.Id, topic: "SalutationReceived", messageType: MessageType.MT_EVENT); - var body = new MessageBody(System.Text.Json.JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.General))); - var message = new Message(header, body); - return message; - } - - public SalutationReceived MapToRequest(Message message) - { - throw new System.NotImplementedException(); - } - } -} diff --git a/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs b/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs new file mode 100644 index 0000000000..bdf74e9c3d --- /dev/null +++ b/samples/WebAPI_Dynamo/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs @@ -0,0 +1,27 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using Paramore.Brighter; +using SalutationPorts.Requests; + +namespace SalutationAnalytics.Mappers +{ + public class SalutationReceivedMessageMapperAsync : IAmAMessageMapperAsync + { + public async Task MapToMessage(SalutationReceived request) + { + //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using + //TaskCompletionSource for a Task over sync instead + var header = new MessageHeader(messageId: request.Id, topic: "SalutationReceived", messageType: MessageType.MT_EVENT); + using var ms = new MemoryStream(); + await JsonSerializer.SerializeAsync(ms, request, new JsonSerializerOptions(JsonSerializerDefaults.General)); + var body = new MessageBody(ms.ToArray()); + return new Message(header, body); + } + + public Task MapToRequest(Message message) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/samples/WebAPI_Dynamo/SalutationPorts/Handlers/GreetingMadeHandler.cs b/samples/WebAPI_Dynamo/SalutationPorts/Handlers/GreetingMadeHandler.cs index 89a91a2802..af1002b6ef 100644 --- a/samples/WebAPI_Dynamo/SalutationPorts/Handlers/GreetingMadeHandler.cs +++ b/samples/WebAPI_Dynamo/SalutationPorts/Handlers/GreetingMadeHandler.cs @@ -15,27 +15,20 @@ namespace SalutationPorts.Handlers { - public class GreetingMadeHandler : RequestHandler + public class GreetingMadeHandlerAsync( + IAmADynamoDbTransactionProvider transactionProvider, + IAmACommandProcessor postBox, + ILogger logger) + : RequestHandlerAsync { - private readonly IAmADynamoDbTransactionProvider _transactionProvider; - private readonly IAmACommandProcessor _postBox; - private readonly ILogger _logger; - - public GreetingMadeHandler(IAmADynamoDbTransactionProvider transactionProvider, IAmACommandProcessor postBox, ILogger logger) - { - _transactionProvider = transactionProvider; - _postBox = postBox; - _logger = logger; - } - - [UseInbox(step:0, contextKey: typeof(GreetingMadeHandler), onceOnly: true )] - [RequestLogging(step: 1, timing: HandlerTiming.Before)] - [UsePolicy(step:2, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICY)] - public override GreetingMade Handle(GreetingMade @event) + [UseInboxAsync(step:0, contextKey: typeof(GreetingMadeHandlerAsync), onceOnly: true )] + [RequestLoggingAsync(step: 1, timing: HandlerTiming.Before)] + [UsePolicyAsync(step:2, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICY_ASYNC)] + public override async Task HandleAsync(GreetingMade @event, CancellationToken cancellationToken = default) { var posts = new List(); - var context = new DynamoDBContext(_transactionProvider.DynamoDb); - var tx = _transactionProvider.GetTransaction(); + var context = new DynamoDBContext(transactionProvider.DynamoDb); + var tx = await transactionProvider.GetTransactionAsync(cancellationToken); try { var salutation = new Salutation { Greeting = @event.Greeting }; @@ -46,25 +39,25 @@ public override GreetingMade Handle(GreetingMade @event) Put = new Put { TableName = "Salutations", Item = attributes } }); - posts.Add(_postBox.DepositPost(new SalutationReceived(DateTimeOffset.Now), _transactionProvider)); + posts.Add(await postBox.DepositPostAsync(new SalutationReceived(DateTimeOffset.Now), transactionProvider, cancellationToken: cancellationToken)); - _transactionProvider.Commit(); + await transactionProvider.CommitAsync(cancellationToken); } catch (Exception e) { - _logger.LogError(e, "Could not save salutation"); - _transactionProvider.Rollback(); + logger.LogError(e, "Could not save salutation"); + await transactionProvider.RollbackAsync(cancellationToken); - return base.Handle(@event); + return await base.HandleAsync(@event, cancellationToken); } finally { - _transactionProvider.Close(); + transactionProvider.Close(); } - _postBox.ClearOutboxAsync(posts); + await postBox.ClearOutboxAsync(posts, cancellationToken: cancellationToken); - return base.Handle(@event); + return await base.HandleAsync(@event,cancellationToken); } } } diff --git a/samples/WebAPI_Dynamo/SalutationPorts/Policies/Retry.cs b/samples/WebAPI_Dynamo/SalutationPorts/Policies/Retry.cs index 58013a5a3f..cece04ce1b 100644 --- a/samples/WebAPI_Dynamo/SalutationPorts/Policies/Retry.cs +++ b/samples/WebAPI_Dynamo/SalutationPorts/Policies/Retry.cs @@ -7,21 +7,20 @@ namespace SalutationPorts.Policies { public static class Retry { - public const string RETRYPOLICY = "SalutationPorts.Policies.RetryPolicy"; - public const string EXPONENTIAL_RETRYPOLICY = "SalutationPorts.Policies.ExponenttialRetryPolicy"; - - public static RetryPolicy GetSimpleHandlerRetryPolicy() + public const string RETRYPOLICYASYNC = "SalutationPorts.Policies.RetryPolicy"; + public const string EXPONENTIAL_RETRYPOLICY_ASYNC = "SalutationPorts.Policies.ExponenttialRetryPolicy"; + public static AsyncRetryPolicy GetSimpleHandlerRetryPolicyAsync() { - return Policy.Handle().WaitAndRetry(new[] + return Policy.Handle().WaitAndRetryAsync(new[] { TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(150) }); } - public static RetryPolicy GetExponentialHandlerRetryPolicy() + public static AsyncRetryPolicy GetExponentialHandlerRetryPolicyAsync() { var delay = Backoff.ExponentialBackoff(TimeSpan.FromMilliseconds(100), retryCount: 5, fastFirst: true); - return Policy.Handle().WaitAndRetry(delay); + return Policy.Handle().WaitAndRetryAsync(delay); } } } diff --git a/samples/WebAPI_Dynamo/SalutationPorts/Policies/SalutationPolicy.cs b/samples/WebAPI_Dynamo/SalutationPorts/Policies/SalutationPolicy.cs index 28024f22a7..23ff0b0f22 100644 --- a/samples/WebAPI_Dynamo/SalutationPorts/Policies/SalutationPolicy.cs +++ b/samples/WebAPI_Dynamo/SalutationPorts/Policies/SalutationPolicy.cs @@ -11,8 +11,8 @@ public SalutationPolicy() private void AddSalutationPolicies() { - Add(Retry.RETRYPOLICY, Retry.GetSimpleHandlerRetryPolicy()); - Add(Retry.EXPONENTIAL_RETRYPOLICY, Retry.GetExponentialHandlerRetryPolicy()); + Add(Retry.RETRYPOLICYASYNC, Retry.GetSimpleHandlerRetryPolicyAsync()); + Add(Retry.EXPONENTIAL_RETRYPOLICY_ASYNC, Retry.GetExponentialHandlerRetryPolicyAsync()); } } } diff --git a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs index 7115622fd5..0fb005515d 100644 --- a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs +++ b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs @@ -13,28 +13,22 @@ namespace GreetingsPorts.Handlers { - public class AddGreetingHandlerAsync: RequestHandlerAsync + public class AddGreetingHandlerAsync( + GreetingsEntityGateway uow, + IAmATransactionConnectionProvider provider, + IAmACommandProcessor postBox) + : RequestHandlerAsync { - private readonly GreetingsEntityGateway _uow; - private readonly IAmACommandProcessor _postBox; - private readonly IAmATransactionConnectionProvider _transactionProvider; - - public AddGreetingHandlerAsync(GreetingsEntityGateway uow, IAmATransactionConnectionProvider provider, IAmACommandProcessor postBox) - { - _uow = uow; - _postBox = postBox; - _transactionProvider = provider; - } [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task HandleAsync(AddGreeting addGreeting, CancellationToken cancellationToken = default) { var posts = new List(); - await _transactionProvider.GetTransactionAsync(cancellationToken); + await provider.GetTransactionAsync(cancellationToken); try { - var person = await _uow.People + var person = await uow.People .Where(p => p.Name == addGreeting.Name) .SingleAsync(cancellationToken); @@ -43,31 +37,31 @@ public override async Task HandleAsync(AddGreeting addGreeting, Can person.AddGreeting(greeting); //Now write the message we want to send to the Db in the same transaction. - posts.Add(await _postBox.DepositPostAsync( + posts.Add(await postBox.DepositPostAsync( new GreetingMade(greeting.Greet()), - _transactionProvider, + provider, cancellationToken: cancellationToken)); //write the changed entity to the Db - await _uow.SaveChangesAsync(cancellationToken); + await uow.SaveChangesAsync(cancellationToken); //write new person and the associated message to the Db - await _transactionProvider.CommitAsync(cancellationToken); + await provider.CommitAsync(cancellationToken); } catch (Exception) { //it went wrong, rollback the entity change and the downstream message - await _transactionProvider.RollbackAsync(cancellationToken); + await provider.RollbackAsync(cancellationToken); return await base.HandleAsync(addGreeting, cancellationToken); } finally { - _transactionProvider.Close(); + provider.Close(); } //Send this message via a transport. We need the ids to send just the messages here, not all outstanding ones. //Alternatively, you can let the Sweeper do this, but at the cost of increased latency - await _postBox.ClearOutboxAsync(posts, cancellationToken:cancellationToken); + await postBox.ClearOutboxAsync(posts, cancellationToken:cancellationToken); return await base.HandleAsync(addGreeting, cancellationToken); } diff --git a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs index 177459edb6..1de4e9a74c 100644 --- a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs +++ b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs @@ -1,30 +1,23 @@ using System.Threading; using System.Threading.Tasks; using GreetingsEntities; -using GreetingsPorts.EntityGateway; using GreetingsPorts.Requests; +using Microsoft.EntityFrameworkCore; using Paramore.Brighter; using Paramore.Brighter.Logging.Attributes; using Paramore.Brighter.Policies.Attributes; namespace GreetingsPorts.Handlers { - public class AddPersonHandlerAsync : RequestHandlerAsync + public class AddPersonHandlerAsync(DbContext uow) : RequestHandlerAsync { - private readonly GreetingsEntityGateway _uow; - - public AddPersonHandlerAsync(GreetingsEntityGateway uow) - { - _uow = uow; - } - [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task HandleAsync(AddPerson addPerson, CancellationToken cancellationToken = default) { - _uow.Add(new Person(addPerson.Name)); + uow.Add(new Person(addPerson.Name)); - await _uow.SaveChangesAsync(cancellationToken); + await uow.SaveChangesAsync(cancellationToken); return await base.HandleAsync(addPerson, cancellationToken); } diff --git a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs index 83b0f3d526..d41c045f88 100644 --- a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs +++ b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/DeletePersonHandlerAsync.cs @@ -10,27 +10,20 @@ namespace GreetingsPorts.Handlers { - public class DeletePersonHandlerAsync : RequestHandlerAsync + public class DeletePersonHandlerAsync(GreetingsEntityGateway uow) : RequestHandlerAsync { - private readonly GreetingsEntityGateway _uow; - - public DeletePersonHandlerAsync(GreetingsEntityGateway uow) - { - _uow = uow; - } - [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task HandleAsync(DeletePerson deletePerson, CancellationToken cancellationToken = default) { - var person = await _uow.People + var person = await uow.People .Include(p => p.Greetings) .Where(p => p.Name == deletePerson.Name) .SingleAsync(cancellationToken); - _uow.Remove(person); + uow.Remove(person); - await _uow.SaveChangesAsync(cancellationToken); + await uow.SaveChangesAsync(cancellationToken); return await base.HandleAsync(deletePerson, cancellationToken); } diff --git a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs index b8ee3ca531..368fa75c08 100644 --- a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs +++ b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/FIndGreetingsForPersonHandlerAsync.cs @@ -12,20 +12,14 @@ namespace GreetingsPorts.Handlers { - public class FIndGreetingsForPersonHandlerAsync : QueryHandlerAsync + public class FIndGreetingsForPersonHandlerAsync(GreetingsEntityGateway uow) + : QueryHandlerAsync { - private readonly GreetingsEntityGateway _uow; - - public FIndGreetingsForPersonHandlerAsync(GreetingsEntityGateway uow) - { - _uow = uow; - } - [QueryLogging(0)] [RetryableQuery(1, Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task ExecuteAsync(FindGreetingsForPerson query, CancellationToken cancellationToken = new CancellationToken()) { - var person = await _uow.People + var person = await uow.People .Include(p => p.Greetings) .Where(p => p.Name == query.Name) .SingleAsync(cancellationToken); diff --git a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs index 7ba8438e24..cfe5b0e432 100644 --- a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs +++ b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/FindPersonByNameHandlerAsync.cs @@ -12,20 +12,14 @@ namespace GreetingsPorts.Handlers { - public class FindPersonByNameHandlerAsync : QueryHandlerAsync + public class FindPersonByNameHandlerAsync(GreetingsEntityGateway uow) + : QueryHandlerAsync { - private readonly GreetingsEntityGateway _uow; - - public FindPersonByNameHandlerAsync(GreetingsEntityGateway uow) - { - _uow = uow; - } - [QueryLogging(0)] [RetryableQuery(1, Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task ExecuteAsync(FindPersonByName query, CancellationToken cancellationToken = new CancellationToken()) { - return await _uow.People + return await uow.People .Where(p => p.Name == query.Name) .Select(p => new FindPersonResult(p)) .SingleAsync(cancellationToken); diff --git a/samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs b/samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs deleted file mode 100644 index 19dc67d0a8..0000000000 --- a/samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapper.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json; -using GreetingsPorts.Requests; -using Paramore.Brighter; - - -namespace GreetingsWeb.Mappers -{ - public class GreetingMadeMessageMapper : IAmAMessageMapper - { - public Message MapToMessage(GreetingMade request) - { - var header = new MessageHeader(messageId: request.Id, topic: "GreetingMade", messageType: MessageType.MT_EVENT); - var body = new MessageBody(System.Text.Json.JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.General))); - var message = new Message(header, body); - return message; - } - - public GreetingMade MapToRequest(Message message) - { - throw new System.NotImplementedException(); - } - } -} diff --git a/samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs b/samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs new file mode 100644 index 0000000000..619789d036 --- /dev/null +++ b/samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs @@ -0,0 +1,28 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using GreetingsPorts.Requests; +using Paramore.Brighter; + + +namespace GreetingsWeb.Mappers +{ + public class GreetingMadeMessageMapperAsync : IAmAMessageMapperAsync + { + public async Task MapToMessage(GreetingMade request) + { + //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using + //TaskCompletionSource for a Task over sync instead + var header = new MessageHeader(messageId: request.Id, topic: "GreetingMade", messageType: MessageType.MT_EVENT); + using var ms = new MemoryStream(); + await JsonSerializer.SerializeAsync(ms, request, new JsonSerializerOptions(JsonSerializerDefaults.General)); + var body = new MessageBody(ms.ToArray()); + return new Message(header, body); + } + + public Task MapToRequest(Message message) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs b/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs deleted file mode 100644 index f96c47a9c7..0000000000 --- a/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/GreetingMadeMessageMapper.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Text.Json; -using Paramore.Brighter; -using SalutationPorts.Requests; - -namespace SalutationAnalytics.Mappers -{ - public class GreetingMadeMessageMapper : IAmAMessageMapper - { - public Message MapToMessage(GreetingMade request) - { - throw new System.NotImplementedException(); - } - - public GreetingMade MapToRequest(Message message) - { - return JsonSerializer.Deserialize(message.Body.Value, JsonSerialisationOptions.Options); - } - } -} diff --git a/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs b/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs new file mode 100644 index 0000000000..c63a222a59 --- /dev/null +++ b/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/GreetingMadeMessageMapperAsync.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using Paramore.Brighter; +using SalutationPorts.Requests; + +namespace SalutationAnalytics.Mappers +{ + public class GreetingMadeMessageMapperAsync : IAmAMessageMapperAsync + { + public Task MapToMessage(GreetingMade request) + { + throw new System.NotImplementedException(); + } + + public async Task MapToRequest(Message message) + { + //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using + //TaskCompletionSource for a Task over sync instead + using var ms = new MemoryStream(message.Body.Bytes); + return await JsonSerializer.DeserializeAsync(ms, JsonSerialisationOptions.Options); + } + } +} diff --git a/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs b/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs deleted file mode 100644 index bdbb2c1061..0000000000 --- a/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/SalutationReceivedMessageMapper.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json; -using Paramore.Brighter; -using SalutationPorts.Requests; - -namespace SalutationAnalytics.Mappers -{ - public class SalutationReceivedMessageMapper : IAmAMessageMapper - { - public Message MapToMessage(SalutationReceived request) - { - var header = new MessageHeader(messageId: request.Id, topic: "SalutationReceived", messageType: MessageType.MT_EVENT); - var body = new MessageBody(System.Text.Json.JsonSerializer.Serialize(request, new JsonSerializerOptions(JsonSerializerDefaults.General))); - var message = new Message(header, body); - return message; - } - - public SalutationReceived MapToRequest(Message message) - { - throw new System.NotImplementedException(); - } - } -} diff --git a/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs b/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs new file mode 100644 index 0000000000..bdf74e9c3d --- /dev/null +++ b/samples/WebAPI_EFCore/SalutationAnalytics/Mappers/SalutationReceivedMessageMapperAsync.cs @@ -0,0 +1,27 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using Paramore.Brighter; +using SalutationPorts.Requests; + +namespace SalutationAnalytics.Mappers +{ + public class SalutationReceivedMessageMapperAsync : IAmAMessageMapperAsync + { + public async Task MapToMessage(SalutationReceived request) + { + //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using + //TaskCompletionSource for a Task over sync instead + var header = new MessageHeader(messageId: request.Id, topic: "SalutationReceived", messageType: MessageType.MT_EVENT); + using var ms = new MemoryStream(); + await JsonSerializer.SerializeAsync(ms, request, new JsonSerializerOptions(JsonSerializerDefaults.General)); + var body = new MessageBody(ms.ToArray()); + return new Message(header, body); + } + + public Task MapToRequest(Message message) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/samples/WebAPI_EFCore/SalutationPorts/Handlers/GreetingMadeHandler.cs b/samples/WebAPI_EFCore/SalutationPorts/Handlers/GreetingMadeHandler.cs index ecb171a58d..1caabf9e25 100644 --- a/samples/WebAPI_EFCore/SalutationPorts/Handlers/GreetingMadeHandler.cs +++ b/samples/WebAPI_EFCore/SalutationPorts/Handlers/GreetingMadeHandler.cs @@ -12,47 +12,40 @@ namespace SalutationPorts.Handlers { - public class GreetingMadeHandler : RequestHandler + public class GreetingMadeHandlerAsync( + SalutationsEntityGateway uow, + IAmATransactionConnectionProvider provider, + IAmACommandProcessor postBox) + : RequestHandlerAsync { - private readonly SalutationsEntityGateway _uow; - private readonly IAmACommandProcessor _postBox; - private readonly IAmATransactionConnectionProvider _transactionProvider; - - public GreetingMadeHandler(SalutationsEntityGateway uow, IAmATransactionConnectionProvider provider, IAmACommandProcessor postBox) - { - _uow = uow; - _postBox = postBox; - _transactionProvider = provider; - } - - [UseInbox(step:0, contextKey: typeof(GreetingMadeHandler), onceOnly: true )] - [RequestLogging(step: 1, timing: HandlerTiming.Before)] - [UsePolicy(step:2, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICY)] - public override GreetingMade Handle(GreetingMade @event) + [UseInboxAsync(step:0, contextKey: typeof(GreetingMadeHandlerAsync), onceOnly: true )] + [RequestLoggingAsync(step: 1, timing: HandlerTiming.Before)] + [UsePolicyAsync(step:2, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICY)] + public override async Task HandleAsync(GreetingMade @event, CancellationToken cancellationToken = default) { var posts = new List(); - var tx =_transactionProvider.GetTransaction(); + var tx = await provider.GetTransactionAsync(cancellationToken); try { var salutation = new Salutation(@event.Greeting); - _uow.Salutations.Add(salutation); + uow.Salutations.Add(salutation); - posts.Add(_postBox.DepositPost( + posts.Add(await postBox.DepositPostAsync( new SalutationReceived(DateTimeOffset.Now), - _transactionProvider) + provider, cancellationToken: cancellationToken) ); - _uow.SaveChanges(); + await uow.SaveChangesAsync(cancellationToken); - _transactionProvider.Commit(); + await provider.CommitAsync(cancellationToken); } catch (Exception e) { Console.WriteLine(e); - _transactionProvider.Rollback(); + await provider.RollbackAsync(cancellationToken); Console.WriteLine("Salutation analytical record not saved"); @@ -60,12 +53,12 @@ public override GreetingMade Handle(GreetingMade @event) } finally { - _transactionProvider.Close(); + provider.Close(); } - _postBox.ClearOutbox(posts.ToArray()); + postBox.ClearOutbox(posts.ToArray()); - return base.Handle(@event); + return await base.HandleAsync(@event,cancellationToken); } } } From 0512c30c1c2c72cd170ff3d0671b8ee9749c8f4d Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Tue, 23 Jan 2024 19:06:35 +0000 Subject: [PATCH 12/15] Move solution projects to top level statements --- .../SalutationAnalytics/Program.cs | 818 +++++++++--------- .../SalutationAnalytics/Program.cs | 409 +++++---- .../SalutationAnalytics/Program.cs | 338 ++++---- 3 files changed, 774 insertions(+), 791 deletions(-) diff --git a/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs b/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs index 8e6b971992..1bad2b3798 100644 --- a/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs +++ b/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Threading.Tasks; using Confluent.Kafka; using Confluent.SchemaRegistry; using FluentMigrator.Runner; @@ -29,445 +28,438 @@ using SalutationPorts.Requests; using ChannelFactory = Paramore.Brighter.MessagingGateway.RMQ.ChannelFactory; -namespace SalutationAnalytics +var host = CreateHostBuilder(args).Build(); +host.CheckDbIsUp(); +host.MigrateDatabase(); +host.CreateInbox(); +host.CreateOutbox(HasBinaryMessagePayload()); +await host.RunAsync(); +return; + +static void AddSchemaRegistryMaybe(IServiceCollection services, MessagingTransport messagingTransport) { - internal class Program - { - public static async Task Main(string[] args) - { - var host = CreateHostBuilder(args).Build(); - host.CheckDbIsUp(); - host.MigrateDatabase(); - host.CreateInbox(); - host.CreateOutbox(HasBinaryMessagePayload()); - await host.RunAsync(); - } + if (messagingTransport != MessagingTransport.Kafka) return; - private static void AddSchemaRegistryMaybe(IServiceCollection services, MessagingTransport messagingTransport) - { - if (messagingTransport != MessagingTransport.Kafka) return; - - var schemaRegistryConfig = new SchemaRegistryConfig { Url = "http://localhost:8081" }; - var cachedSchemaRegistryClient = new CachedSchemaRegistryClient(schemaRegistryConfig); - services.AddSingleton(cachedSchemaRegistryClient); - } + var schemaRegistryConfig = new SchemaRegistryConfig { Url = "http://localhost:8081" }; + var cachedSchemaRegistryClient = new CachedSchemaRegistryClient(schemaRegistryConfig); + services.AddSingleton(cachedSchemaRegistryClient); +} - private static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureHostConfiguration(configurationBuilder => - { - configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); - configurationBuilder.AddJsonFile("appsettings.json", optional: true); - configurationBuilder.AddJsonFile($"appsettings.{GetEnvironment()}.json", optional: true); - configurationBuilder.AddEnvironmentVariables(prefix: "ASPNETCORE_"); //NOTE: Although not web, we use this to grab the environment - configurationBuilder.AddEnvironmentVariables(prefix: "BRIGHTER_"); - configurationBuilder.AddCommandLine(args); - }) - .ConfigureLogging((context, builder) => - { - builder.AddConsole(); - builder.AddDebug(); - }) - .ConfigureServices((hostContext, services) => - { - ConfigureMigration(hostContext, services); - ConfigureDapper(hostContext, services); - ConfigureBrighter(hostContext, services); - }) - .UseConsoleLifetime(); +static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureHostConfiguration(configurationBuilder => + { + configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); + configurationBuilder.AddJsonFile("appsettings.json", optional: true); + configurationBuilder.AddJsonFile($"appsettings.{GetEnvironment()}.json", optional: true); + configurationBuilder + .AddEnvironmentVariables( + prefix: "ASPNETCORE_"); //NOTE: Although not web, we use this to grab the environment + configurationBuilder.AddEnvironmentVariables(prefix: "BRIGHTER_"); + configurationBuilder.AddCommandLine(args); + }) + .ConfigureLogging((context, builder) => + { + builder.AddConsole(); + builder.AddDebug(); + }) + .ConfigureServices((hostContext, services) => + { + ConfigureMigration(hostContext, services); + ConfigureDapper(hostContext, services); + ConfigureBrighter(hostContext, services); + }) + .UseConsoleLifetime(); - private static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services) - { - var messagingTransport = GetTransportType(hostContext.Configuration[MessagingGlobals.BRIGHTER_TRANSPORT]); - - AddSchemaRegistryMaybe(services, messagingTransport); - - Subscription[] subscriptions = GetSubscriptions(messagingTransport); - - var relationalDatabaseConfiguration = new RelationalDatabaseConfiguration(DbConnectionString(hostContext)); - services.AddSingleton(relationalDatabaseConfiguration); - - var outboxConfiguration = new RelationalDatabaseConfiguration( - DbConnectionString(hostContext), - binaryMessagePayload: messagingTransport == MessagingTransport.Kafka +static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services) +{ + var messagingTransport = GetTransportType(hostContext.Configuration[MessagingGlobals.BRIGHTER_TRANSPORT]); + + AddSchemaRegistryMaybe(services, messagingTransport); + + Subscription[] subscriptions = GetSubscriptions(messagingTransport); + + var relationalDatabaseConfiguration = new RelationalDatabaseConfiguration(DbConnectionString(hostContext)); + services.AddSingleton(relationalDatabaseConfiguration); + + var outboxConfiguration = new RelationalDatabaseConfiguration( + DbConnectionString(hostContext), + binaryMessagePayload: messagingTransport == MessagingTransport.Kafka + ); + services.AddSingleton(outboxConfiguration); + + (IAmAnOutbox outbox, Type connectionProvider, Type transactionProvider) makeOutbox = + OutboxExtensions.MakeOutbox(hostContext, GetDatabaseType(hostContext), outboxConfiguration, services); + + services.AddServiceActivator(options => + { + options.Subscriptions = subscriptions; + options.ChannelFactory = GetChannelFactory(messagingTransport); + options.UseScoped = true; + options.HandlerLifetime = ServiceLifetime.Scoped; + options.MapperLifetime = ServiceLifetime.Singleton; + options.CommandProcessorLifetime = ServiceLifetime.Scoped; + options.PolicyRegistry = new SalutationPolicy(); + options.InboxConfiguration = new InboxConfiguration( + CreateInbox(hostContext, relationalDatabaseConfiguration), + scope: InboxScope.Commands, + onceOnly: true, + actionOnExists: OnceOnlyAction.Throw ); - services.AddSingleton(outboxConfiguration); - - (IAmAnOutbox outbox, Type connectionProvider, Type transactionProvider) makeOutbox = - OutboxExtensions.MakeOutbox(hostContext, GetDatabaseType(hostContext), outboxConfiguration, services); + }) + .ConfigureJsonSerialisation((options) => + { + //We don't strictly need this, but added as an example + options.PropertyNameCaseInsensitive = true; + }) + .UseExternalBus((config) => + { + config.ProducerRegistry = ConfigureProducerRegistry(messagingTransport); + config.Outbox = makeOutbox.outbox; + config.ConnectionProvider = makeOutbox.connectionProvider; + config.TransactionProvider = makeOutbox.transactionProvider; + }) + .AutoFromAssemblies(); - services.AddServiceActivator(options => - { - options.Subscriptions = subscriptions; - options.ChannelFactory = GetChannelFactory(messagingTransport); - options.UseScoped = true; - options.HandlerLifetime = ServiceLifetime.Scoped; - options.MapperLifetime = ServiceLifetime.Singleton; - options.CommandProcessorLifetime = ServiceLifetime.Scoped; - options.PolicyRegistry = new SalutationPolicy(); - options.InboxConfiguration = new InboxConfiguration( - CreateInbox(hostContext, relationalDatabaseConfiguration), - scope: InboxScope.Commands, - onceOnly: true, - actionOnExists: OnceOnlyAction.Throw - - ); - }) - .ConfigureJsonSerialisation((options) => - { - //We don't strictly need this, but added as an example - options.PropertyNameCaseInsensitive = true; - }) - .UseExternalBus((config) => - { - config.ProducerRegistry = ConfigureProducerRegistry(messagingTransport); - config.Outbox = makeOutbox.outbox; - config.ConnectionProvider = makeOutbox.connectionProvider; - config.TransactionProvider = makeOutbox.transactionProvider; - }) - .AutoFromAssemblies(); - - services.AddHostedService(); - } - - private static void ConfigureMigration(HostBuilderContext hostBuilderContext, IServiceCollection services) - { - //dev is always Sqlite - if (hostBuilderContext.HostingEnvironment.IsDevelopment()) - ConfigureSqlite(hostBuilderContext, services); - else - ConfigureProductionDatabase(hostBuilderContext, GetDatabaseType(hostBuilderContext), services); - } + services.AddHostedService(); +} - private static void ConfigureProductionDatabase( - HostBuilderContext hostBuilderContext, - DatabaseType databaseType, - IServiceCollection services) - { - switch (databaseType) - { - case DatabaseType.MySql: - ConfigureMySql(hostBuilderContext, services); - break; - case DatabaseType.MsSql: - ConfigureMsSql(hostBuilderContext, services); - break; - case DatabaseType.Postgres: - ConfigurePostgreSql(hostBuilderContext, services); - break; - case DatabaseType.Sqlite: - ConfigureSqlite(hostBuilderContext, services); - break; - default: - throw new ArgumentOutOfRangeException(nameof(databaseType), "Database type is not supported"); - } - } - - private static void ConfigureMySql(HostBuilderContext hostBuilderContext, IServiceCollection services) - { - services - .AddFluentMigratorCore() - .ConfigureRunner(c => c.AddMySql5() - .WithGlobalConnectionString(DbConnectionString(hostBuilderContext)) - .ScanIn(typeof(Salutations_Migrations.Migrations.SqlInitialMigrations).Assembly).For.Migrations() - ); - } - - private static void ConfigureMsSql(HostBuilderContext hostBuilderContext, IServiceCollection services) - { - services - .AddFluentMigratorCore() - .ConfigureRunner(c => c.AddSqlServer() - .WithGlobalConnectionString(DbConnectionString(hostBuilderContext)) - .ScanIn(typeof(Salutations_Migrations.Migrations.SqlInitialMigrations).Assembly).For.Migrations() - ); - } - - private static void ConfigurePostgreSql(HostBuilderContext hostBuilderContext, IServiceCollection services) - { - services - .AddFluentMigratorCore() - .ConfigureRunner(c => c.AddPostgres() - .ConfigureGlobalProcessorOptions(opt => opt.ProviderSwitches = "Force Quote=false") - .WithGlobalConnectionString(DbConnectionString(hostBuilderContext)) - .ScanIn(typeof(Salutations_Migrations.Migrations.SqlInitialMigrations).Assembly).For.Migrations() - ); - } +static void ConfigureMigration(HostBuilderContext hostBuilderContext, IServiceCollection services) +{ + //dev is always Sqlite + if (hostBuilderContext.HostingEnvironment.IsDevelopment()) + ConfigureSqlite(hostBuilderContext, services); + else + ConfigureProductionDatabase(hostBuilderContext, GetDatabaseType(hostBuilderContext), services); +} - private static void ConfigureSqlite(HostBuilderContext hostBuilderContext, IServiceCollection services) - { - services - .AddFluentMigratorCore() - .ConfigureRunner(c => - { - c.AddSQLite() - .WithGlobalConnectionString(DbConnectionString(hostBuilderContext)) - .ScanIn(typeof(Salutations_Migrations.Migrations.SqlInitialMigrations).Assembly).For.Migrations(); - }); - } +static void ConfigureProductionDatabase( + HostBuilderContext hostBuilderContext, + DatabaseType databaseType, + IServiceCollection services) +{ + switch (databaseType) + { + case DatabaseType.MySql: + ConfigureMySql(hostBuilderContext, services); + break; + case DatabaseType.MsSql: + ConfigureMsSql(hostBuilderContext, services); + break; + case DatabaseType.Postgres: + ConfigurePostgreSql(hostBuilderContext, services); + break; + case DatabaseType.Sqlite: + ConfigureSqlite(hostBuilderContext, services); + break; + default: + throw new ArgumentOutOfRangeException(nameof(databaseType), "Database type is not supported"); + } +} - private static void ConfigureDapper(HostBuilderContext hostBuilderContext, IServiceCollection services) - { - ConfigureDapperByHost(GetDatabaseType(hostBuilderContext), services); - } +static void ConfigureMySql(HostBuilderContext hostBuilderContext, IServiceCollection services) +{ + services + .AddFluentMigratorCore() + .ConfigureRunner(c => c.AddMySql5() + .WithGlobalConnectionString(DbConnectionString(hostBuilderContext)) + .ScanIn(typeof(Salutations_Migrations.Migrations.SqlInitialMigrations).Assembly).For.Migrations() + ); +} - private static void ConfigureDapperByHost(DatabaseType databaseType, IServiceCollection services) - { - switch (databaseType) - { - case DatabaseType.Sqlite: - ConfigureDapperSqlite(services); - break; - case DatabaseType.MySql: - ConfigureDapperMySql(services); - break; - case DatabaseType.MsSql: - ConfigureDapperMsSql(services); - break; - case DatabaseType.Postgres: - ConfigureDapperPostgreSql(services); - break; - default: - throw new ArgumentOutOfRangeException(nameof(databaseType), "Database type is not supported"); - } - } +static void ConfigureMsSql(HostBuilderContext hostBuilderContext, IServiceCollection services) +{ + services + .AddFluentMigratorCore() + .ConfigureRunner(c => c.AddSqlServer() + .WithGlobalConnectionString(DbConnectionString(hostBuilderContext)) + .ScanIn(typeof(Salutations_Migrations.Migrations.SqlInitialMigrations).Assembly).For.Migrations() + ); +} - private static void ConfigureDapperSqlite(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - } +static void ConfigurePostgreSql(HostBuilderContext hostBuilderContext, IServiceCollection services) +{ + services + .AddFluentMigratorCore() + .ConfigureRunner(c => c.AddPostgres() + .ConfigureGlobalProcessorOptions(opt => opt.ProviderSwitches = "Force Quote=false") + .WithGlobalConnectionString(DbConnectionString(hostBuilderContext)) + .ScanIn(typeof(Salutations_Migrations.Migrations.SqlInitialMigrations).Assembly).For.Migrations() + ); +} - private static void ConfigureDapperMySql(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - } +static void ConfigureSqlite(HostBuilderContext hostBuilderContext, IServiceCollection services) +{ + services + .AddFluentMigratorCore() + .ConfigureRunner(c => + { + c.AddSQLite() + .WithGlobalConnectionString(DbConnectionString(hostBuilderContext)) + .ScanIn(typeof(Salutations_Migrations.Migrations.SqlInitialMigrations).Assembly).For.Migrations(); + }); +} - private static void ConfigureDapperMsSql(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - } +static void ConfigureDapper(HostBuilderContext hostBuilderContext, IServiceCollection services) +{ + ConfigureDapperByHost(GetDatabaseType(hostBuilderContext), services); +} - private static void ConfigureDapperPostgreSql(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - } - - private static IAmAnInbox CreateInbox(HostBuilderContext hostContext, IAmARelationalDatabaseConfiguration configuration) - { - if (hostContext.HostingEnvironment.IsDevelopment()) - { - return new SqliteInbox(configuration); - } +static void ConfigureDapperByHost(DatabaseType databaseType, IServiceCollection services) +{ + switch (databaseType) + { + case DatabaseType.Sqlite: + ConfigureDapperSqlite(services); + break; + case DatabaseType.MySql: + ConfigureDapperMySql(services); + break; + case DatabaseType.MsSql: + ConfigureDapperMsSql(services); + break; + case DatabaseType.Postgres: + ConfigureDapperPostgreSql(services); + break; + default: + throw new ArgumentOutOfRangeException(nameof(databaseType), "Database type is not supported"); + } +} - return CreateProductionInbox(GetDatabaseType(hostContext), configuration); - } - - private static IAmAProducerRegistry ConfigureProducerRegistry(MessagingTransport messagingTransport) - { - return messagingTransport switch - { - MessagingTransport.Rmq => GetRmqProducerRegistry(), - MessagingTransport.Kafka => GetKafkaProducerRegistry(), - _ => throw new ArgumentOutOfRangeException(nameof(messagingTransport), "Messaging transport is not supported") - }; - } +static void ConfigureDapperSqlite(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); +} - private static IAmAnInbox CreateProductionInbox(DatabaseType databaseType, IAmARelationalDatabaseConfiguration configuration) - { - return databaseType switch - { - DatabaseType.Sqlite => new SqliteInbox(configuration), - DatabaseType.MySql => new MySqlInbox(configuration), - DatabaseType.MsSql => new MsSqlInbox(configuration), - DatabaseType.Postgres => new PostgreSqlInbox(configuration), - _ => throw new ArgumentOutOfRangeException(nameof(databaseType), "Database type is not supported") - }; - } +static void ConfigureDapperMySql(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); +} - private static string DbConnectionString(HostBuilderContext hostContext) - { - //NOTE: Sqlite needs to use a shared cache to allow Db writes to the Outbox as well as entities - return hostContext.HostingEnvironment.IsDevelopment() - ? GetDevDbConnectionString() - :GetConnectionString(hostContext, GetDatabaseType(hostContext)); - } +static void ConfigureDapperMsSql(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); +} - private static DatabaseType GetDatabaseType(HostBuilderContext hostContext) - { - return hostContext.Configuration[DatabaseGlobals.DATABASE_TYPE_ENV] switch - { - DatabaseGlobals.MYSQL => DatabaseType.MySql, - DatabaseGlobals.MSSQL => DatabaseType.MsSql, - DatabaseGlobals.POSTGRESSQL => DatabaseType.Postgres, - DatabaseGlobals.SQLITE => DatabaseType.Sqlite, - _ => throw new InvalidOperationException("Could not determine the database type") - }; - } - - private static IAmAChannelFactory GetChannelFactory(MessagingTransport messagingTransport) - { - return messagingTransport switch +static void ConfigureDapperPostgreSql(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); +} + +static IAmAnInbox CreateInbox(HostBuilderContext hostContext, IAmARelationalDatabaseConfiguration configuration) +{ + if (hostContext.HostingEnvironment.IsDevelopment()) + { + return new SqliteInbox(configuration); + } + + return CreateProductionInbox(GetDatabaseType(hostContext), configuration); +} + +static IAmAProducerRegistry ConfigureProducerRegistry(MessagingTransport messagingTransport) +{ + return messagingTransport switch + { + MessagingTransport.Rmq => GetRmqProducerRegistry(), + MessagingTransport.Kafka => GetKafkaProducerRegistry(), + _ => throw new ArgumentOutOfRangeException(nameof(messagingTransport), "Messaging transport is not supported") + }; +} + +static IAmAnInbox CreateProductionInbox(DatabaseType databaseType, IAmARelationalDatabaseConfiguration configuration) +{ + return databaseType switch + { + DatabaseType.Sqlite => new SqliteInbox(configuration), + DatabaseType.MySql => new MySqlInbox(configuration), + DatabaseType.MsSql => new MsSqlInbox(configuration), + DatabaseType.Postgres => new PostgreSqlInbox(configuration), + _ => throw new ArgumentOutOfRangeException(nameof(databaseType), "Database type is not supported") + }; +} + +static string DbConnectionString(HostBuilderContext hostContext) +{ + //NOTE: Sqlite needs to use a shared cache to allow Db writes to the Outbox as well as entities + return hostContext.HostingEnvironment.IsDevelopment() + ? GetDevDbConnectionString() + : GetConnectionString(hostContext, GetDatabaseType(hostContext)); +} + +static DatabaseType GetDatabaseType(HostBuilderContext hostContext) +{ + return hostContext.Configuration[DatabaseGlobals.DATABASE_TYPE_ENV] switch + { + DatabaseGlobals.MYSQL => DatabaseType.MySql, + DatabaseGlobals.MSSQL => DatabaseType.MsSql, + DatabaseGlobals.POSTGRESSQL => DatabaseType.Postgres, + DatabaseGlobals.SQLITE => DatabaseType.Sqlite, + _ => throw new InvalidOperationException("Could not determine the database type") + }; +} + +static IAmAChannelFactory GetChannelFactory(MessagingTransport messagingTransport) +{ + return messagingTransport switch + { + MessagingTransport.Rmq => GetRmqChannelFactory(), + MessagingTransport.Kafka => GetKafkaChannelFactory(), + _ => throw new ArgumentOutOfRangeException(nameof(messagingTransport), "Messaging transport is not supported") + }; +} + +static string GetEnvironment() +{ + //NOTE: Hosting Context will always return Production outside of ASPNET_CORE at this point, so grab it directly + return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); +} + +static string GetConnectionString(HostBuilderContext hostContext, DatabaseType databaseType) +{ + return databaseType switch + { + DatabaseType.MySql => hostContext.Configuration.GetConnectionString("SalutationsMySql"), + DatabaseType.MsSql => hostContext.Configuration.GetConnectionString("SalutationsMsSql"), + DatabaseType.Postgres => hostContext.Configuration.GetConnectionString("SalutationsPostgreSql"), + DatabaseType.Sqlite => GetDevDbConnectionString(), + _ => throw new InvalidOperationException("Could not determine the database type") + }; +} + +static string GetDevDbConnectionString() +{ + return "Filename=Salutations.db;Cache=Shared"; +} + +static IAmAChannelFactory GetKafkaChannelFactory() +{ + return new Paramore.Brighter.MessagingGateway.Kafka.ChannelFactory( + new KafkaMessageConsumerFactory( + new KafkaMessagingGatewayConfiguration { - MessagingTransport.Rmq => GetRmqChannelFactory(), - MessagingTransport.Kafka => GetKafkaChannelFactory(), - _ => throw new ArgumentOutOfRangeException(nameof(messagingTransport), "Messaging transport is not supported") - }; - } + Name = "paramore.brighter", BootStrapServers = new[] { "localhost:9092" } + } + ) + ); +} - private static string GetEnvironment() - { - //NOTE: Hosting Context will always return Production outside of ASPNET_CORE at this point, so grab it directly - return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); - } - - private static string GetConnectionString(HostBuilderContext hostContext, DatabaseType databaseType) - { - return databaseType switch +static IAmAProducerRegistry GetKafkaProducerRegistry() +{ + var producerRegistry = new KafkaProducerRegistryFactory( + new KafkaMessagingGatewayConfiguration { - DatabaseType.MySql => hostContext.Configuration.GetConnectionString("SalutationsMySql"), - DatabaseType.MsSql => hostContext.Configuration.GetConnectionString("SalutationsMsSql"), - DatabaseType.Postgres => hostContext.Configuration.GetConnectionString("SalutationsPostgreSql"), - DatabaseType.Sqlite => GetDevDbConnectionString(), - _ => throw new InvalidOperationException("Could not determine the database type") - }; - } - private static string GetDevDbConnectionString() - { - return "Filename=Salutations.db;Cache=Shared"; - } - - private static IAmAChannelFactory GetKafkaChannelFactory() - { - return new Paramore.Brighter.MessagingGateway.Kafka.ChannelFactory( - new KafkaMessageConsumerFactory( - new KafkaMessagingGatewayConfiguration - { - Name = "paramore.brighter", - BootStrapServers = new[] { "localhost:9092" } - } - ) - ); - } - - private static IAmAProducerRegistry GetKafkaProducerRegistry() - { - var producerRegistry = new KafkaProducerRegistryFactory( - new KafkaMessagingGatewayConfiguration - { - Name = "paramore.brighter.greetingsender", BootStrapServers = new[] { "localhost:9092" } - }, - new KafkaPublication[] - { - new KafkaPublication - { - Topic = new RoutingKey("SalutationReceived"), - MessageSendMaxRetries = 3, - MessageTimeoutMs = 1000, - MaxInFlightRequestsPerConnection = 1, - MakeChannels = OnMissingChannel.Create - } - }) - .Create(); - - return producerRegistry; - } - - private static IAmAChannelFactory GetRmqChannelFactory() - { - return new ChannelFactory(new RmqMessageConsumerFactory(new RmqMessagingGatewayConnection - { - AmpqUri = new AmqpUriSpecification(new Uri($"amqp://guest:guest@localhost:5672")), - Exchange = new Exchange("paramore.brighter.exchange") - }) - ); - } - - private static IAmAProducerRegistry GetRmqProducerRegistry() - { - var producerRegistry = new RmqProducerRegistryFactory( - new RmqMessagingGatewayConnection - { - AmpqUri = new AmqpUriSpecification(new Uri($"amqp://guest:guest@localhost:5672")), - Exchange = new Exchange("paramore.brighter.exchange") - }, - new RmqPublication[] + Name = "paramore.brighter.greetingsender", BootStrapServers = new[] { "localhost:9092" } + }, + new KafkaPublication[] + { + new KafkaPublication { - new RmqPublication - { - Topic = new RoutingKey("SalutationReceived"), - MaxOutStandingMessages = 5, - MaxOutStandingCheckIntervalMilliSeconds = 500, - WaitForConfirmsTimeOutInMilliseconds = 1000, - MakeChannels = OnMissingChannel.Create - } + Topic = new RoutingKey("SalutationReceived"), + MessageSendMaxRetries = 3, + MessageTimeoutMs = 1000, + MaxInFlightRequestsPerConnection = 1, + MakeChannels = OnMissingChannel.Create } - ).Create(); - return producerRegistry; - } - - private static Subscription[] GetRmqSubscriptions() - { - var subscriptions = new Subscription[] - { - new RmqSubscription( - new SubscriptionName("paramore.sample.salutationanalytics"), - new ChannelName("SalutationAnalytics"), - new RoutingKey("GreetingMade"), - runAsync: false, - timeoutInMilliseconds: 200, - isDurable: true, - makeChannels: OnMissingChannel - .Create), //change to OnMissingChannel.Validate if you have infrastructure declared elsewhere - }; - return subscriptions; - } - - private static Subscription[] GetSubscriptions(MessagingTransport messagingTransport) - { - return messagingTransport switch - { - MessagingTransport.Rmq => GetRmqSubscriptions(), - MessagingTransport.Kafka => GetKafkaSubscriptions(), - _ => throw new ArgumentOutOfRangeException(nameof(messagingTransport), "Messaging transport is not supported") - }; - } + }) + .Create(); + + return producerRegistry; +} - private static Subscription[] GetKafkaSubscriptions() +static IAmAChannelFactory GetRmqChannelFactory() +{ + return new ChannelFactory(new RmqMessageConsumerFactory(new RmqMessagingGatewayConnection { - var subscriptions = new KafkaSubscription[] - { - new KafkaSubscription( - new SubscriptionName("paramore.sample.salutationanalytics"), - channelName: new ChannelName("SalutationAnalytics"), - routingKey: new RoutingKey("GreetingMade"), - groupId: "kafka-GreetingsReceiverConsole-Sample", - timeoutInMilliseconds: 100, - offsetDefault: AutoOffsetReset.Earliest, - commitBatchSize: 5, - sweepUncommittedOffsetsIntervalMs: 10000, - makeChannels: OnMissingChannel.Create) - }; - return subscriptions; - } + AmpqUri = new AmqpUriSpecification(new Uri($"amqp://guest:guest@localhost:5672")), + Exchange = new Exchange("paramore.brighter.exchange") + }) + ); +} - private static MessagingTransport GetTransportType(string brighterTransport) +static IAmAProducerRegistry GetRmqProducerRegistry() +{ + var producerRegistry = new RmqProducerRegistryFactory( + new RmqMessagingGatewayConnection { - return brighterTransport switch - { - MessagingGlobals.RMQ => MessagingTransport.Rmq, - MessagingGlobals.KAFKA => MessagingTransport.Kafka, - _ => throw new ArgumentOutOfRangeException(nameof(MessagingGlobals.BRIGHTER_TRANSPORT), - "Messaging transport is not supported") - }; - } - - private static bool HasBinaryMessagePayload() + AmpqUri = new AmqpUriSpecification(new Uri($"amqp://guest:guest@localhost:5672")), + Exchange = new Exchange("paramore.brighter.exchange") + }, + new RmqPublication[] { - return GetTransportType(Environment.GetEnvironmentVariable("BRIGHTER_TRANSPORT")) == MessagingTransport.Kafka; + new RmqPublication + { + Topic = new RoutingKey("SalutationReceived"), + MaxOutStandingMessages = 5, + MaxOutStandingCheckIntervalMilliSeconds = 500, + WaitForConfirmsTimeOutInMilliseconds = 1000, + MakeChannels = OnMissingChannel.Create + } } - } + ).Create(); + return producerRegistry; +} + +static Subscription[] GetRmqSubscriptions() +{ + var subscriptions = new Subscription[] + { + new RmqSubscription( + new SubscriptionName("paramore.sample.salutationanalytics"), + new ChannelName("SalutationAnalytics"), + new RoutingKey("GreetingMade"), + runAsync: false, + timeoutInMilliseconds: 200, + isDurable: true, + makeChannels: OnMissingChannel + .Create), //change to OnMissingChannel.Validate if you have infrastructure declared elsewhere + }; + return subscriptions; +} + +static Subscription[] GetSubscriptions(MessagingTransport messagingTransport) +{ + return messagingTransport switch + { + MessagingTransport.Rmq => GetRmqSubscriptions(), + MessagingTransport.Kafka => GetKafkaSubscriptions(), + _ => throw new ArgumentOutOfRangeException(nameof(messagingTransport), "Messaging transport is not supported") + }; +} + +static Subscription[] GetKafkaSubscriptions() +{ + var subscriptions = new KafkaSubscription[] + { + new KafkaSubscription( + new SubscriptionName("paramore.sample.salutationanalytics"), + channelName: new ChannelName("SalutationAnalytics"), + routingKey: new RoutingKey("GreetingMade"), + groupId: "kafka-GreetingsReceiverConsole-Sample", + timeoutInMilliseconds: 100, + offsetDefault: AutoOffsetReset.Earliest, + commitBatchSize: 5, + sweepUncommittedOffsetsIntervalMs: 10000, + makeChannels: OnMissingChannel.Create) + }; + return subscriptions; +} + +static MessagingTransport GetTransportType(string brighterTransport) +{ + return brighterTransport switch + { + MessagingGlobals.RMQ => MessagingTransport.Rmq, + MessagingGlobals.KAFKA => MessagingTransport.Kafka, + _ => throw new ArgumentOutOfRangeException(nameof(MessagingGlobals.BRIGHTER_TRANSPORT), + "Messaging transport is not supported") + }; +} + +static bool HasBinaryMessagePayload() +{ + return GetTransportType(Environment.GetEnvironmentVariable("BRIGHTER_TRANSPORT")) == MessagingTransport.Kafka; } diff --git a/samples/WebAPI_Dynamo/SalutationAnalytics/Program.cs b/samples/WebAPI_Dynamo/SalutationAnalytics/Program.cs index 22dec0b091..9c96f6d7dd 100644 --- a/samples/WebAPI_Dynamo/SalutationAnalytics/Program.cs +++ b/samples/WebAPI_Dynamo/SalutationAnalytics/Program.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading.Tasks; using Amazon; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; @@ -23,237 +22,237 @@ using SalutationPorts.Policies; using SalutationPorts.Requests; -namespace SalutationAnalytics + +await CreateHostBuilder(args).Build().RunAsync(); +return; + +static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureHostConfiguration(configurationBuilder => + { + configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); + configurationBuilder.AddJsonFile("appsettings.json", optional: true); + configurationBuilder.AddJsonFile($"appsettings.{GetEnvironment()}.json", optional: true); + configurationBuilder + .AddEnvironmentVariables( + prefix: "ASPNETCORE_"); //NOTE: Although not web, we use this to grab the environment + configurationBuilder.AddEnvironmentVariables(prefix: "BRIGHTER_"); + configurationBuilder.AddCommandLine(args); + }) + .ConfigureLogging((context, builder) => + { + builder.AddConsole(); + builder.AddDebug(); + }) + .ConfigureServices((hostContext, services) => + { + var credentials = CreateCredentials(); + IAmazonDynamoDB client = CreateAndRegisterClient(credentials, hostContext, services); + ConfigureDynamo(client, hostContext, services); + ConfigureBrighter(credentials, client, hostContext, services); + }) + .UseConsoleLifetime(); + +static void ConfigureBrighter( + AWSCredentials awsCredentials, + IAmazonDynamoDB dynamoDb, + HostBuilderContext hostContext, + IServiceCollection services) { - static class Program + var subscriptions = new Subscription[] { - public static async Task Main(string[] args) - { - await CreateHostBuilder(args).Build().RunAsync(); - } + new RmqSubscription( + new SubscriptionName("paramore.sample.salutationanalytics"), + new ChannelName("SalutationAnalytics"), + new RoutingKey("GreetingMade"), + runAsync: false, + timeoutInMilliseconds: 200, + isDurable: true, + makeChannels: OnMissingChannel + .Create), //change to OnMissingChannel.Validate if you have infrastructure declared elsewhere + }; - private static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureHostConfiguration(configurationBuilder => - { - configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); - configurationBuilder.AddJsonFile("appsettings.json", optional: true); - configurationBuilder.AddJsonFile($"appsettings.{GetEnvironment()}.json", optional: true); - configurationBuilder.AddEnvironmentVariables(prefix: "ASPNETCORE_"); //NOTE: Although not web, we use this to grab the environment - configurationBuilder.AddEnvironmentVariables(prefix: "BRIGHTER_"); - configurationBuilder.AddCommandLine(args); - }) - .ConfigureLogging((context, builder) => - { - builder.AddConsole(); - builder.AddDebug(); - }) - .ConfigureServices((hostContext, services) => - { - var credentials = CreateCredentials(); - IAmazonDynamoDB client = CreateAndRegisterClient(credentials, hostContext, services); - ConfigureDynamo(client, hostContext, services); - ConfigureBrighter(credentials, client, hostContext, services); - }) - .UseConsoleLifetime(); - - private static void ConfigureBrighter( - AWSCredentials awsCredentials, - IAmazonDynamoDB dynamoDb, - HostBuilderContext hostContext, - IServiceCollection services) - { - var subscriptions = new Subscription[] - { - new RmqSubscription( - new SubscriptionName("paramore.sample.salutationanalytics"), - new ChannelName("SalutationAnalytics"), - new RoutingKey("GreetingMade"), - runAsync: false, - timeoutInMilliseconds: 200, - isDurable: true, - makeChannels: OnMissingChannel.Create), //change to OnMissingChannel.Validate if you have infrastructure declared elsewhere - }; - - var host = hostContext.HostingEnvironment.IsDevelopment() ? "localhost" : "rabbitmq"; - - var rmqConnection = new RmqMessagingGatewayConnection - { - AmpqUri = new AmqpUriSpecification(new Uri($"amqp://guest:guest@{host}:5672")), Exchange = new Exchange("paramore.brighter.exchange") - }; - - var rmqMessageConsumerFactory = new RmqMessageConsumerFactory(rmqConnection); - - var producerRegistry = new RmqProducerRegistryFactory( - rmqConnection, - new RmqPublication[] - { - new RmqPublication - { - Topic = new RoutingKey("SalutationReceived"), - MaxOutStandingMessages = 5, - MaxOutStandingCheckIntervalMilliSeconds = 500, - WaitForConfirmsTimeOutInMilliseconds = 1000, - MakeChannels = OnMissingChannel.Create - } - } - ).Create(); - - services.AddServiceActivator(options => - { - options.Subscriptions = subscriptions; - options.ChannelFactory = new ChannelFactory(rmqMessageConsumerFactory); - options.UseScoped = true; - options.HandlerLifetime = ServiceLifetime.Scoped; - options.MapperLifetime = ServiceLifetime.Singleton; - options.CommandProcessorLifetime = ServiceLifetime.Scoped; - options.PolicyRegistry = new SalutationPolicy(); - options.InboxConfiguration = new InboxConfiguration( - ConfigureInbox(awsCredentials, dynamoDb), - scope: InboxScope.Commands, - onceOnly: true, - actionOnExists: OnceOnlyAction.Throw - ); - }) - .ConfigureJsonSerialisation((options) => - { - //We don't strictly need this, but added as an example - options.PropertyNameCaseInsensitive = true; - }) - .UseExternalBus((configure) => - { - configure.ProducerRegistry = producerRegistry; - configure.Outbox = ConfigureOutbox(awsCredentials, dynamoDb); - configure.ConnectionProvider = typeof(DynamoDbUnitOfWork); - configure.TransactionProvider = typeof(DynamoDbUnitOfWork); - } - ) - .AutoFromAssemblies(); - - services.AddHostedService(); - } + var host = hostContext.HostingEnvironment.IsDevelopment() ? "localhost" : "rabbitmq"; + var rmqConnection = new RmqMessagingGatewayConnection + { + AmpqUri = new AmqpUriSpecification(new Uri($"amqp://guest:guest@{host}:5672")), + Exchange = new Exchange("paramore.brighter.exchange") + }; - private static void ConfigureDynamo(IAmazonDynamoDB dynamoDb, HostBuilderContext hostBuilderContext, IServiceCollection services) + var rmqMessageConsumerFactory = new RmqMessageConsumerFactory(rmqConnection); + + var producerRegistry = new RmqProducerRegistryFactory( + rmqConnection, + new RmqPublication[] { - CreateEntityStore(dynamoDb); - CreateOutbox(dynamoDb, services); - CreateInbox(dynamoDb, services); + new RmqPublication + { + Topic = new RoutingKey("SalutationReceived"), + MaxOutStandingMessages = 5, + MaxOutStandingCheckIntervalMilliSeconds = 500, + WaitForConfirmsTimeOutInMilliseconds = 1000, + MakeChannels = OnMissingChannel.Create + } } + ).Create(); - private static IAmazonDynamoDB CreateAndRegisterClient(AWSCredentials credentials, HostBuilderContext hostBuilderContext, IServiceCollection services) + services.AddServiceActivator(options => + { + options.Subscriptions = subscriptions; + options.ChannelFactory = new ChannelFactory(rmqMessageConsumerFactory); + options.UseScoped = true; + options.HandlerLifetime = ServiceLifetime.Scoped; + options.MapperLifetime = ServiceLifetime.Singleton; + options.CommandProcessorLifetime = ServiceLifetime.Scoped; + options.PolicyRegistry = new SalutationPolicy(); + options.InboxConfiguration = new InboxConfiguration( + ConfigureInbox(awsCredentials, dynamoDb), + scope: InboxScope.Commands, + onceOnly: true, + actionOnExists: OnceOnlyAction.Throw + ); + }) + .ConfigureJsonSerialisation((options) => { - if (hostBuilderContext.HostingEnvironment.IsDevelopment()) + //We don't strictly need this, but added as an example + options.PropertyNameCaseInsensitive = true; + }) + .UseExternalBus((configure) => { - return CreateAndRegisterLocalClient(credentials, services); + configure.ProducerRegistry = producerRegistry; + configure.Outbox = ConfigureOutbox(awsCredentials, dynamoDb); + configure.ConnectionProvider = typeof(DynamoDbUnitOfWork); + configure.TransactionProvider = typeof(DynamoDbUnitOfWork); } + ) + .AutoFromAssemblies(); - return CreateAndRegisterRemoteClient(services); - } + services.AddHostedService(); +} - private static AWSCredentials CreateCredentials() - { - return new BasicAWSCredentials("FakeAccessKey", "FakeSecretKey"); - } - private static IAmazonDynamoDB CreateAndRegisterLocalClient(AWSCredentials credentials, IServiceCollection services) - { +static void ConfigureDynamo(IAmazonDynamoDB dynamoDb, HostBuilderContext hostBuilderContext, + IServiceCollection services) +{ + CreateEntityStore(dynamoDb); + CreateOutbox(dynamoDb, services); + CreateInbox(dynamoDb, services); +} - var clientConfig = new AmazonDynamoDBConfig { ServiceURL = "http://localhost:8000" }; +static IAmazonDynamoDB CreateAndRegisterClient(AWSCredentials credentials, HostBuilderContext hostBuilderContext, + IServiceCollection services) +{ + if (hostBuilderContext.HostingEnvironment.IsDevelopment()) + { + return CreateAndRegisterLocalClient(credentials, services); + } - var dynamoDb = new AmazonDynamoDBClient(credentials, clientConfig); - services.Add(new ServiceDescriptor(typeof(IAmazonDynamoDB), dynamoDb)); + return CreateAndRegisterRemoteClient(services); +} - var dynamoDbConfiguration = new DynamoDbConfiguration(credentials, RegionEndpoint.EUWest1); - services.Add(new ServiceDescriptor(typeof(DynamoDbConfiguration), dynamoDbConfiguration)); +static AWSCredentials CreateCredentials() +{ + return new BasicAWSCredentials("FakeAccessKey", "FakeSecretKey"); +} - return dynamoDb; - } +static IAmazonDynamoDB CreateAndRegisterLocalClient(AWSCredentials credentials, IServiceCollection services) +{ + var clientConfig = new AmazonDynamoDBConfig { ServiceURL = "http://localhost:8000" }; - private static IAmazonDynamoDB CreateAndRegisterRemoteClient(IServiceCollection services) - { - throw new NotImplementedException(); - } + var dynamoDb = new AmazonDynamoDBClient(credentials, clientConfig); + services.Add(new ServiceDescriptor(typeof(IAmazonDynamoDB), dynamoDb)); - private static void CreateEntityStore(IAmazonDynamoDB client) - { - var tableRequestFactory = new DynamoDbTableFactory(); - var dbTableBuilder = new DynamoDbTableBuilder(client); - - CreateTableRequest tableRequest = tableRequestFactory.GenerateCreateTableRequest( - new DynamoDbCreateProvisionedThroughput - ( - new ProvisionedThroughput { ReadCapacityUnits = 10, WriteCapacityUnits = 10 } - ) - ); + var dynamoDbConfiguration = new DynamoDbConfiguration(credentials, RegionEndpoint.EUWest1); + services.Add(new ServiceDescriptor(typeof(DynamoDbConfiguration), dynamoDbConfiguration)); - var entityTableName = tableRequest.TableName; - (bool exist, IEnumerable tables) hasTables = dbTableBuilder.HasTables(new string[] { entityTableName }).Result; - if (!hasTables.exist) - { - var buildTable = dbTableBuilder.Build(tableRequest).Result; - dbTableBuilder.EnsureTablesReady(new[] { tableRequest.TableName }, TableStatus.ACTIVE).Wait(); - } - } + return dynamoDb; +} - private static void CreateOutbox(IAmazonDynamoDB client, IServiceCollection services) - { - var tableRequestFactory = new DynamoDbTableFactory(); - var dbTableBuilder = new DynamoDbTableBuilder(client); - - var createTableRequest = new DynamoDbTableFactory().GenerateCreateTableRequest( - new DynamoDbCreateProvisionedThroughput( - new ProvisionedThroughput { ReadCapacityUnits = 10, WriteCapacityUnits = 10 }, - new Dictionary - { - { "Outstanding", new ProvisionedThroughput { ReadCapacityUnits = 10, WriteCapacityUnits = 10 } }, - { "Delivered", new ProvisionedThroughput { ReadCapacityUnits = 10, WriteCapacityUnits = 10 } } - } - )); - var outboxTableName = createTableRequest.TableName; - (bool exist, IEnumerable tables) hasTables = dbTableBuilder.HasTables(new string[] { outboxTableName }).Result; - if (!hasTables.exist) - { - var buildTable = dbTableBuilder.Build(createTableRequest).Result; - dbTableBuilder.EnsureTablesReady(new[] { createTableRequest.TableName }, TableStatus.ACTIVE).Wait(); - } - } +static IAmazonDynamoDB CreateAndRegisterRemoteClient(IServiceCollection services) +{ + throw new NotImplementedException(); +} - private static void CreateInbox(IAmazonDynamoDB client, IServiceCollection services) - { - var tableRequestFactory = new DynamoDbTableFactory(); - var dbTableBuilder = new DynamoDbTableBuilder(client); - - var createTableRequest = new DynamoDbTableFactory().GenerateCreateTableRequest>( - new DynamoDbCreateProvisionedThroughput( - new ProvisionedThroughput{ReadCapacityUnits = 10, WriteCapacityUnits = 10}, - new Dictionary() - )); - - var tableName = createTableRequest.TableName; - (bool exist, IEnumerable tables) hasTables = dbTableBuilder.HasTables(new string[] {tableName}).Result; - if (!hasTables.exist) +static void CreateEntityStore(IAmazonDynamoDB client) +{ + var tableRequestFactory = new DynamoDbTableFactory(); + var dbTableBuilder = new DynamoDbTableBuilder(client); + + CreateTableRequest tableRequest = tableRequestFactory.GenerateCreateTableRequest( + new DynamoDbCreateProvisionedThroughput + ( + new ProvisionedThroughput { ReadCapacityUnits = 10, WriteCapacityUnits = 10 } + ) + ); + + var entityTableName = tableRequest.TableName; + (bool exist, IEnumerable tables) hasTables = + dbTableBuilder.HasTables(new string[] { entityTableName }).Result; + if (!hasTables.exist) + { + var buildTable = dbTableBuilder.Build(tableRequest).Result; + dbTableBuilder.EnsureTablesReady(new[] { tableRequest.TableName }, TableStatus.ACTIVE).Wait(); + } +} + +static void CreateOutbox(IAmazonDynamoDB client, IServiceCollection services) +{ + var tableRequestFactory = new DynamoDbTableFactory(); + var dbTableBuilder = new DynamoDbTableBuilder(client); + + var createTableRequest = new DynamoDbTableFactory().GenerateCreateTableRequest( + new DynamoDbCreateProvisionedThroughput( + new ProvisionedThroughput { ReadCapacityUnits = 10, WriteCapacityUnits = 10 }, + new Dictionary { - var buildTable = dbTableBuilder.Build(createTableRequest).Result; - dbTableBuilder.EnsureTablesReady(new[] {createTableRequest.TableName}, TableStatus.ACTIVE).Wait(); + { "Outstanding", new ProvisionedThroughput { ReadCapacityUnits = 10, WriteCapacityUnits = 10 } }, + { "Delivered", new ProvisionedThroughput { ReadCapacityUnits = 10, WriteCapacityUnits = 10 } } } - } + )); + var outboxTableName = createTableRequest.TableName; + (bool exist, IEnumerable tables) hasTables = + dbTableBuilder.HasTables(new string[] { outboxTableName }).Result; + if (!hasTables.exist) + { + var buildTable = dbTableBuilder.Build(createTableRequest).Result; + dbTableBuilder.EnsureTablesReady(new[] { createTableRequest.TableName }, TableStatus.ACTIVE).Wait(); + } +} - private static IAmAnInbox ConfigureInbox(AWSCredentials credentials, IAmazonDynamoDB dynamoDb) - { - return new DynamoDbInbox(dynamoDb, new DynamoDbInboxConfiguration(credentials, RegionEndpoint.EUWest1)); - } - - private static IAmAnOutbox ConfigureOutbox(AWSCredentials credentials, IAmazonDynamoDB dynamoDb) - { - return new DynamoDbOutbox(dynamoDb, new DynamoDbConfiguration(credentials, RegionEndpoint.EUWest1)); - } +static void CreateInbox(IAmazonDynamoDB client, IServiceCollection services) +{ + var tableRequestFactory = new DynamoDbTableFactory(); + var dbTableBuilder = new DynamoDbTableBuilder(client); + var createTableRequest = new DynamoDbTableFactory().GenerateCreateTableRequest>( + new DynamoDbCreateProvisionedThroughput( + new ProvisionedThroughput { ReadCapacityUnits = 10, WriteCapacityUnits = 10 }, + new Dictionary() + )); - private static string GetEnvironment() - { - //NOTE: Hosting Context will always return Production outside of ASPNET_CORE at this point, so grab it directly - return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); - } + var tableName = createTableRequest.TableName; + (bool exist, IEnumerable tables) hasTables = dbTableBuilder.HasTables(new string[] { tableName }).Result; + if (!hasTables.exist) + { + var buildTable = dbTableBuilder.Build(createTableRequest).Result; + dbTableBuilder.EnsureTablesReady(new[] { createTableRequest.TableName }, TableStatus.ACTIVE).Wait(); } } + +static IAmAnInbox ConfigureInbox(AWSCredentials credentials, IAmazonDynamoDB dynamoDb) +{ + return new DynamoDbInbox(dynamoDb, new DynamoDbInboxConfiguration(credentials, RegionEndpoint.EUWest1)); +} + +static IAmAnOutbox ConfigureOutbox(AWSCredentials credentials, IAmazonDynamoDB dynamoDb) +{ + return new DynamoDbOutbox(dynamoDb, new DynamoDbConfiguration(credentials, RegionEndpoint.EUWest1)); +} + + +static string GetEnvironment() +{ + //NOTE: Hosting Context will always return Production outside of ASPNET_CORE at this point, so grab it directly + return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); +} diff --git a/samples/WebAPI_EFCore/SalutationAnalytics/Program.cs b/samples/WebAPI_EFCore/SalutationAnalytics/Program.cs index c68f8e6ce2..ccd2f7adc6 100644 --- a/samples/WebAPI_EFCore/SalutationAnalytics/Program.cs +++ b/samples/WebAPI_EFCore/SalutationAnalytics/Program.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -25,199 +24,192 @@ using SalutationPorts.Policies; using SalutationPorts.Requests; -namespace SalutationAnalytics +var host = CreateHostBuilder(args).Build(); +host.CheckDbIsUp(); +host.MigrateDatabase(); +host.CreateInbox(); +host.CreateOutbox(); +await host.RunAsync(); +return; + +static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureHostConfiguration(configurationBuilder => + { + configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); + configurationBuilder.AddJsonFile("appsettings.json", optional: true); + configurationBuilder.AddJsonFile($"appsettings.{GetEnvironment()}.json", optional: true); + configurationBuilder + .AddEnvironmentVariables( + prefix: "ASPNETCORE_"); //NOTE: Although not web, we use this to grab the environment + configurationBuilder.AddEnvironmentVariables(prefix: "BRIGHTER_"); + configurationBuilder.AddCommandLine(args); + }) + .ConfigureLogging((context, builder) => + { + builder.AddConsole(); + builder.AddDebug(); + }) + .ConfigureServices((hostContext, services) => + { + ConfigureEFCore(hostContext, services); + ConfigureBrighter(hostContext, services); + }) + .UseConsoleLifetime(); + +static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services) { - class Program + (IAmAnOutbox outbox, Type transactionProvider, Type connectionProvider) = MakeOutbox(hostContext); + var outboxConfiguration = new RelationalDatabaseConfiguration(DbConnectionString(hostContext)); + services.AddSingleton(outboxConfiguration); + + IAmAProducerRegistry producerRegistry = ConfigureProducerRegistry(); + + var subscriptions = new Subscription[] { - public static async Task Main(string[] args) - { - var host = CreateHostBuilder(args).Build(); - host.CheckDbIsUp(); - host.MigrateDatabase(); - host.CreateInbox(); - host.CreateOutbox(); - await host.RunAsync(); - } + new RmqSubscription( + new SubscriptionName("paramore.sample.salutationanalytics"), + new ChannelName("SalutationAnalytics"), + new RoutingKey("GreetingMade"), + runAsync: false, + timeoutInMilliseconds: 200, + isDurable: true, + makeChannels: OnMissingChannel + .Create), //change to OnMissingChannel.Validate if you have infrastructure declared elsewhere + }; - private static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureHostConfiguration(configurationBuilder => - { - configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); - configurationBuilder.AddJsonFile("appsettings.json", optional: true); - configurationBuilder.AddJsonFile($"appsettings.{GetEnvironment()}.json", optional: true); - configurationBuilder - .AddEnvironmentVariables( - prefix: "ASPNETCORE_"); //NOTE: Although not web, we use this to grab the environment - configurationBuilder.AddEnvironmentVariables(prefix: "BRIGHTER_"); - configurationBuilder.AddCommandLine(args); - }) - .ConfigureLogging((context, builder) => - { - builder.AddConsole(); - builder.AddDebug(); - }) - .ConfigureServices((hostContext, services) => - { - ConfigureEFCore(hostContext, services); - ConfigureBrighter(hostContext, services); - }) - .UseConsoleLifetime(); + var rmqMessageConsumerFactory = new RmqMessageConsumerFactory(new RmqMessagingGatewayConnection + { + AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")), + Exchange = new Exchange("paramore.brighter.exchange"), + }); - private static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services) + services.AddServiceActivator(options => { - (IAmAnOutbox outbox, Type transactionProvider, Type connectionProvider) = MakeOutbox(hostContext); - var outboxConfiguration = new RelationalDatabaseConfiguration(DbConnectionString(hostContext)); - services.AddSingleton(outboxConfiguration); - - IAmAProducerRegistry producerRegistry = ConfigureProducerRegistry(); - - var subscriptions = new Subscription[] - { - new RmqSubscription( - new SubscriptionName("paramore.sample.salutationanalytics"), - new ChannelName("SalutationAnalytics"), - new RoutingKey("GreetingMade"), - runAsync: false, - timeoutInMilliseconds: 200, - isDurable: true, - makeChannels: OnMissingChannel - .Create), //change to OnMissingChannel.Validate if you have infrastructure declared elsewhere - }; - - var rmqMessageConsumerFactory = new RmqMessageConsumerFactory(new RmqMessagingGatewayConnection - { - AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")), - Exchange = new Exchange("paramore.brighter.exchange"), - }); - - services.AddServiceActivator(options => - { - options.Subscriptions = subscriptions; - options.ChannelFactory = new ChannelFactory(rmqMessageConsumerFactory); - options.UseScoped = true; - options.HandlerLifetime = ServiceLifetime.Scoped; - options.MapperLifetime = ServiceLifetime.Singleton; - options.CommandProcessorLifetime = ServiceLifetime.Scoped; - options.PolicyRegistry = new SalutationPolicy(); - options.InboxConfiguration = new InboxConfiguration( - ConfigureInbox(hostContext), - scope: InboxScope.Commands, - onceOnly: true, - actionOnExists: OnceOnlyAction.Throw - ); - }) - .UseExternalBus((configure) => - { - configure.ProducerRegistry = producerRegistry; - configure.Outbox = outbox; - configure.TransactionProvider = transactionProvider; - configure.ConnectionProvider = connectionProvider; - }) - .AutoFromAssemblies(); + options.Subscriptions = subscriptions; + options.ChannelFactory = new ChannelFactory(rmqMessageConsumerFactory); + options.UseScoped = true; + options.HandlerLifetime = ServiceLifetime.Scoped; + options.MapperLifetime = ServiceLifetime.Singleton; + options.CommandProcessorLifetime = ServiceLifetime.Scoped; + options.PolicyRegistry = new SalutationPolicy(); + options.InboxConfiguration = new InboxConfiguration( + ConfigureInbox(hostContext), + scope: InboxScope.Commands, + onceOnly: true, + actionOnExists: OnceOnlyAction.Throw + ); + }) + .UseExternalBus((configure) => + { + configure.ProducerRegistry = producerRegistry; + configure.Outbox = outbox; + configure.TransactionProvider = transactionProvider; + configure.ConnectionProvider = connectionProvider; + }) + .AutoFromAssemblies(); - services.AddHostedService(); - } + services.AddHostedService(); +} - private static string GetEnvironment() - { - //NOTE: Hosting Context will always return Production outside of ASPNET_CORE at this point, so grab it directly - return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); - } +static string GetEnvironment() +{ + //NOTE: Hosting Context will always return Production outside of ASPNET_CORE at this point, so grab it directly + return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); +} - private static void ConfigureEFCore(HostBuilderContext hostContext, IServiceCollection services) - { - string connectionString = DbConnectionString(hostContext); +static void ConfigureEFCore(HostBuilderContext hostContext, IServiceCollection services) +{ + string connectionString = DbConnectionString(hostContext); - if (hostContext.HostingEnvironment.IsDevelopment()) + if (hostContext.HostingEnvironment.IsDevelopment()) + { + services.AddDbContext( + builder => { - services.AddDbContext( - builder => + builder.UseSqlite(connectionString, + optionsBuilder => { - builder.UseSqlite(connectionString, - optionsBuilder => - { - optionsBuilder.MigrationsAssembly("Salutations_Migrations"); - }); + optionsBuilder.MigrationsAssembly("Salutations_Migrations"); }); - } - else - { - services.AddDbContextPool(builder => - { - builder - .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), optionsBuilder => - { - optionsBuilder.MigrationsAssembly("Salutations_MySqlMigrations"); - }) - .EnableDetailedErrors() - .EnableSensitiveDataLogging(); - }); - } - } - - private static IAmAnInbox ConfigureInbox(HostBuilderContext hostContext) - { - if (hostContext.HostingEnvironment.IsDevelopment()) - { - return new SqliteInbox(new RelationalDatabaseConfiguration(DbConnectionString(hostContext), - SchemaCreation.INBOX_TABLE_NAME)); - } - - return new MySqlInbox(new RelationalDatabaseConfiguration(DbConnectionString(hostContext), - SchemaCreation.INBOX_TABLE_NAME)); - } - - private static IAmAProducerRegistry ConfigureProducerRegistry() + }); + } + else + { + services.AddDbContextPool(builder => { - var producerRegistry = new RmqProducerRegistryFactory( - new RmqMessagingGatewayConnection - { - AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")), - Exchange = new Exchange("paramore.brighter.exchange"), - }, - new RmqPublication[] + builder + .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), optionsBuilder => { - new RmqPublication - { - Topic = new RoutingKey("SalutationReceived"), - MaxOutStandingMessages = 5, - MaxOutStandingCheckIntervalMilliSeconds = 500, - WaitForConfirmsTimeOutInMilliseconds = 1000, - MakeChannels = OnMissingChannel.Create - } - } - ).Create(); - - return producerRegistry; - } + optionsBuilder.MigrationsAssembly("Salutations_MySqlMigrations"); + }) + .EnableDetailedErrors() + .EnableSensitiveDataLogging(); + }); + } +} +static IAmAnInbox ConfigureInbox(HostBuilderContext hostContext) +{ + if (hostContext.HostingEnvironment.IsDevelopment()) + { + return new SqliteInbox(new RelationalDatabaseConfiguration(DbConnectionString(hostContext), + SchemaCreation.INBOX_TABLE_NAME)); + } + + return new MySqlInbox(new RelationalDatabaseConfiguration(DbConnectionString(hostContext), + SchemaCreation.INBOX_TABLE_NAME)); +} - private static string DbConnectionString(HostBuilderContext hostContext) +static IAmAProducerRegistry ConfigureProducerRegistry() +{ + var producerRegistry = new RmqProducerRegistryFactory( + new RmqMessagingGatewayConnection { - //NOTE: Sqlite needs to use a shared cache to allow Db writes to the Outbox as well as entities - return hostContext.HostingEnvironment.IsDevelopment() - ? "Filename=Salutations.db;Cache=Shared" - : hostContext.Configuration.GetConnectionString("Salutations"); - } - - private static (IAmAnOutbox outbox, Type transactionProvider, Type connectionProvider) MakeOutbox(HostBuilderContext hostContext) + AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")), + Exchange = new Exchange("paramore.brighter.exchange"), + }, + new RmqPublication[] { - if (hostContext.HostingEnvironment.IsDevelopment()) + new RmqPublication { - var outbox = new SqliteOutbox(new RelationalDatabaseConfiguration(DbConnectionString(hostContext))); - var transactionProvider = typeof(SqliteEntityFrameworkConnectionProvider); - var connectionProvider = typeof(SqliteConnectionProvider); - return (outbox, transactionProvider, connectionProvider); - } - else - { - var outbox = new MySqlOutbox(new RelationalDatabaseConfiguration(DbConnectionString(hostContext))); - var transactionProvider = typeof(MySqlEntityFrameworkConnectionProvider); - var connectionProvider = typeof(MySqlConnectionProvider); - return (outbox, transactionProvider, connectionProvider); + Topic = new RoutingKey("SalutationReceived"), + MaxOutStandingMessages = 5, + MaxOutStandingCheckIntervalMilliSeconds = 500, + WaitForConfirmsTimeOutInMilliseconds = 1000, + MakeChannels = OnMissingChannel.Create } } + ).Create(); + + return producerRegistry; +} + + +static string DbConnectionString(HostBuilderContext hostContext) +{ + //NOTE: Sqlite needs to use a shared cache to allow Db writes to the Outbox as well as entities + return hostContext.HostingEnvironment.IsDevelopment() + ? "Filename=Salutations.db;Cache=Shared" + : hostContext.Configuration.GetConnectionString("Salutations"); +} + +static (IAmAnOutbox outbox, Type transactionProvider, Type connectionProvider) MakeOutbox( + HostBuilderContext hostContext) +{ + if (hostContext.HostingEnvironment.IsDevelopment()) + { + var outbox = new SqliteOutbox(new RelationalDatabaseConfiguration(DbConnectionString(hostContext))); + var transactionProvider = typeof(SqliteEntityFrameworkConnectionProvider); + var connectionProvider = typeof(SqliteConnectionProvider); + return (outbox, transactionProvider, connectionProvider); + } + else + { + var outbox = new MySqlOutbox(new RelationalDatabaseConfiguration(DbConnectionString(hostContext))); + var transactionProvider = typeof(MySqlEntityFrameworkConnectionProvider); + var connectionProvider = typeof(MySqlConnectionProvider); + return (outbox, transactionProvider, connectionProvider); } } From 7fb2046ab5aeb8107571de5d84c759852163fec5 Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Tue, 23 Jan 2024 19:09:59 +0000 Subject: [PATCH 13/15] Move web projects to top level statements --- samples/WebAPI_Dapper/GreetingsWeb/Program.cs | 83 +++++++++---------- samples/WebAPI_Dynamo/GreetingsWeb/Program.cs | 69 +++++++-------- samples/WebAPI_EFCore/GreetingsWeb/Program.cs | 81 +++++++++--------- 3 files changed, 106 insertions(+), 127 deletions(-) diff --git a/samples/WebAPI_Dapper/GreetingsWeb/Program.cs b/samples/WebAPI_Dapper/GreetingsWeb/Program.cs index 8e6dfbe0b6..84142328ed 100644 --- a/samples/WebAPI_Dapper/GreetingsWeb/Program.cs +++ b/samples/WebAPI_Dapper/GreetingsWeb/Program.cs @@ -1,54 +1,47 @@ using System.IO; using FluentMigrator.Runner; +using GreetingsWeb; using GreetingsWeb.Database; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace GreetingsWeb -{ - public class Program - { - public static void Main(string[] args) - { - var host = CreateHostBuilder(args).Build(); - - host.CheckDbIsUp(); - host.MigrateDatabase(); - host.CreateOutbox(host.HasBinaryMessagePayload()); - - host.Run(); - } +var host = CreateHostBuilder(args).Build(); + +host.CheckDbIsUp(); +host.MigrateDatabase(); +host.CreateOutbox(host.HasBinaryMessagePayload()); + +host.Run(); +return; - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration((context, configBuilder) => - { - var env = context.HostingEnvironment; - configBuilder.AddJsonFile("appsettings.json", optional: false); - configBuilder.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); - configBuilder.AddEnvironmentVariables(prefix:"BRIGHTER_"); - }) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseKestrel(); - webBuilder.UseContentRoot(Directory.GetCurrentDirectory()); - webBuilder.CaptureStartupErrors(true); - webBuilder.UseSetting("detailedErrors", "true"); - webBuilder.ConfigureLogging((hostingContext, logging) => - { - logging.AddConsole(); - logging.AddDebug(); - logging.AddFluentMigratorConsole(); - }); - webBuilder.UseDefaultServiceProvider((context, options) => - { - var isDevelopment = context.HostingEnvironment.IsDevelopment(); - options.ValidateScopes = isDevelopment; - options.ValidateOnBuild = isDevelopment; - }); - webBuilder.UseStartup(); - }); - } -} +static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((context, configBuilder) => + { + var env = context.HostingEnvironment; + configBuilder.AddJsonFile("appsettings.json", optional: false); + configBuilder.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); + configBuilder.AddEnvironmentVariables(prefix: "BRIGHTER_"); + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseKestrel(); + webBuilder.UseContentRoot(Directory.GetCurrentDirectory()); + webBuilder.CaptureStartupErrors(true); + webBuilder.UseSetting("detailedErrors", "true"); + webBuilder.ConfigureLogging((hostingContext, logging) => + { + logging.AddConsole(); + logging.AddDebug(); + logging.AddFluentMigratorConsole(); + }); + webBuilder.UseDefaultServiceProvider((context, options) => + { + var isDevelopment = context.HostingEnvironment.IsDevelopment(); + options.ValidateScopes = isDevelopment; + options.ValidateOnBuild = isDevelopment; + }); + webBuilder.UseStartup(); + }); diff --git a/samples/WebAPI_Dynamo/GreetingsWeb/Program.cs b/samples/WebAPI_Dynamo/GreetingsWeb/Program.cs index d899d44f2c..d30fdca5e1 100644 --- a/samples/WebAPI_Dynamo/GreetingsWeb/Program.cs +++ b/samples/WebAPI_Dynamo/GreetingsWeb/Program.cs @@ -1,45 +1,38 @@ using System.IO; +using GreetingsWeb; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace GreetingsWeb -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } +CreateHostBuilder(args).Build().Run(); +return; - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration((context, configBuilder) => - { - var env = context.HostingEnvironment; - configBuilder.AddJsonFile("appsettings.json", optional: false); - configBuilder.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); - configBuilder.AddEnvironmentVariables(prefix:"BRIGHTER_"); - }) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseKestrel(); - webBuilder.UseContentRoot(Directory.GetCurrentDirectory()); - webBuilder.CaptureStartupErrors(true); - webBuilder.UseSetting("detailedErrors", "true"); - webBuilder.ConfigureLogging((hostingContext, logging) => - { - logging.AddConsole(); - logging.AddDebug(); - }); - webBuilder.UseDefaultServiceProvider((context, options) => - { - var isDevelopment = context.HostingEnvironment.IsDevelopment(); - options.ValidateScopes = isDevelopment; - options.ValidateOnBuild = isDevelopment; - }); - webBuilder.UseStartup(); - }); - } -} +static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((context, configBuilder) => + { + var env = context.HostingEnvironment; + configBuilder.AddJsonFile("appsettings.json", optional: false); + configBuilder.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); + configBuilder.AddEnvironmentVariables(prefix: "BRIGHTER_"); + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseKestrel(); + webBuilder.UseContentRoot(Directory.GetCurrentDirectory()); + webBuilder.CaptureStartupErrors(true); + webBuilder.UseSetting("detailedErrors", "true"); + webBuilder.ConfigureLogging((hostingContext, logging) => + { + logging.AddConsole(); + logging.AddDebug(); + }); + webBuilder.UseDefaultServiceProvider((context, options) => + { + var isDevelopment = context.HostingEnvironment.IsDevelopment(); + options.ValidateScopes = isDevelopment; + options.ValidateOnBuild = isDevelopment; + }); + webBuilder.UseStartup(); + }); diff --git a/samples/WebAPI_EFCore/GreetingsWeb/Program.cs b/samples/WebAPI_EFCore/GreetingsWeb/Program.cs index f81ced9c08..5c3320abab 100644 --- a/samples/WebAPI_EFCore/GreetingsWeb/Program.cs +++ b/samples/WebAPI_EFCore/GreetingsWeb/Program.cs @@ -1,52 +1,45 @@ using System.IO; +using GreetingsWeb; using GreetingsWeb.Database; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace GreetingsWeb -{ - public class Program - { - public static void Main(string[] args) - { - var host = CreateHostBuilder(args).Build(); - - host.CheckDbIsUp(); - host.MigrateDatabase(); - host.CreateOutbox(); - - host.Run(); - } +var host = CreateHostBuilder(args).Build(); + +host.CheckDbIsUp(); +host.MigrateDatabase(); +host.CreateOutbox(); + +host.Run(); +return; - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration((context, configBuilder) => - { - var env = context.HostingEnvironment; - configBuilder.AddJsonFile("appsettings.json", optional: false); - configBuilder.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); - configBuilder.AddEnvironmentVariables(prefix:"BRIGHTER_"); - }) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseKestrel(); - webBuilder.UseContentRoot(Directory.GetCurrentDirectory()); - webBuilder.CaptureStartupErrors(true); - webBuilder.UseSetting("detailedErrors", "true"); - webBuilder.ConfigureLogging((hostingContext, logging) => - { - logging.AddConsole(); - logging.AddDebug(); - }); - webBuilder.UseDefaultServiceProvider((context, options) => - { - var isDevelopment = context.HostingEnvironment.IsDevelopment(); - options.ValidateScopes = isDevelopment; - options.ValidateOnBuild = isDevelopment; - }); - webBuilder.UseStartup(); - }); - } -} +static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((context, configBuilder) => + { + var env = context.HostingEnvironment; + configBuilder.AddJsonFile("appsettings.json", optional: false); + configBuilder.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); + configBuilder.AddEnvironmentVariables(prefix: "BRIGHTER_"); + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseKestrel(); + webBuilder.UseContentRoot(Directory.GetCurrentDirectory()); + webBuilder.CaptureStartupErrors(true); + webBuilder.UseSetting("detailedErrors", "true"); + webBuilder.ConfigureLogging((hostingContext, logging) => + { + logging.AddConsole(); + logging.AddDebug(); + }); + webBuilder.UseDefaultServiceProvider((context, options) => + { + var isDevelopment = context.HostingEnvironment.IsDevelopment(); + options.ValidateScopes = isDevelopment; + options.ValidateOnBuild = isDevelopment; + }); + webBuilder.UseStartup(); + }); From 8ba4a2666095325a9ec5d8faa38072fa3159cb1e Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Wed, 24 Jan 2024 11:23:12 +0000 Subject: [PATCH 14/15] Working on async mapper changes; check in for viz --- .../GreetingsReceiverConsole/Program.cs | 102 +++++------ .../GreetingsSender/Program.cs | 158 ++++++++---------- .../CommandHandlers/GreetingEventHandler.cs | 8 +- ....cs => GreetingEventMessageMapperAsync.cs} | 20 ++- .../GreetingsReceiverConsole/Program.cs | 93 +++++------ .../KafkaTaskQueue/GreetingsSender/Program.cs | 144 +++++++--------- .../GreetingsSender/TimedMessageGenerator.cs | 19 +-- .../Handlers/AddGreetingHandlerAsync.cs | 3 - 8 files changed, 251 insertions(+), 296 deletions(-) rename samples/KafkaTaskQueue/Greetings/Ports/Mappers/{GreetingEventMessageMapper.cs => GreetingEventMessageMapperAsync.cs} (66%) diff --git a/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs b/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs index 3dad8cdaed..10b00f2e34 100644 --- a/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs +++ b/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs @@ -23,9 +23,7 @@ THE SOFTWARE. */ #endregion -using System; using System.IO; -using System.Threading.Tasks; using Confluent.Kafka; using Confluent.SchemaRegistry; using Greetings.Ports.Commands; @@ -38,65 +36,55 @@ THE SOFTWARE. */ using Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection; using Paramore.Brighter.ServiceActivator.Extensions.Hosting; -namespace GreetingsReceiverConsole -{ - public class Program +var host = Host.CreateDefaultBuilder(args) + .ConfigureHostConfiguration(configurationBuilder => { - public static Task Main(string[] args) + configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); + configurationBuilder.AddJsonFile("appsettings.json", optional: true); + configurationBuilder.AddCommandLine(args); + }) + .ConfigureLogging((context, builder) => + { + builder.AddConsole(); + builder.AddDebug(); + }) + .ConfigureServices((hostContext, services) => + { + var subscriptions = new KafkaSubscription[] { - var host = Host.CreateDefaultBuilder(args) - .ConfigureHostConfiguration(configurationBuilder => - { - configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); - configurationBuilder.AddJsonFile("appsettings.json", optional: true); - configurationBuilder.AddCommandLine(args); - }) - .ConfigureLogging((context, builder) => - { - builder.AddConsole(); - builder.AddDebug(); - }) - .ConfigureServices((hostContext, services) => - { - var subscriptions = new KafkaSubscription[] - { - new KafkaSubscription( - new SubscriptionName("paramore.example.greeting"), - channelName: new ChannelName("greeting.event"), - routingKey: new RoutingKey("greeting.event"), - groupId: "kafka-GreetingsReceiverConsole-Sample", - timeoutInMilliseconds: 100, - offsetDefault: AutoOffsetReset.Earliest, - commitBatchSize: 5, - sweepUncommittedOffsetsIntervalMs: 10000, - isAsync: true) - }; - - //We take a direct dependency on the schema registry in the message mapper - var schemaRegistryConfig = new SchemaRegistryConfig { Url = "http://localhost:8081"}; - var cachedSchemaRegistryClient = new CachedSchemaRegistryClient(schemaRegistryConfig); - services.AddSingleton(cachedSchemaRegistryClient); + new KafkaSubscription( + new SubscriptionName("paramore.example.greeting"), + channelName: new ChannelName("greeting.event"), + routingKey: new RoutingKey("greeting.event"), + groupId: "kafka-GreetingsReceiverConsole-Sample", + timeoutInMilliseconds: 100, + offsetDefault: AutoOffsetReset.Earliest, + commitBatchSize: 5, + sweepUncommittedOffsetsIntervalMs: 10000, + isAsync: true) + }; - services.AddServiceActivator(options => + //We take a direct dependency on the schema registry in the message mapper + var schemaRegistryConfig = new SchemaRegistryConfig { Url = "http://localhost:8081" }; + var cachedSchemaRegistryClient = new CachedSchemaRegistryClient(schemaRegistryConfig); + services.AddSingleton(cachedSchemaRegistryClient); + + services.AddServiceActivator(options => + { + options.Subscriptions = subscriptions; + options.ChannelFactory = new ChannelFactory( + new KafkaMessageConsumerFactory( + new KafkaMessagingGatewayConfiguration { - options.Subscriptions = subscriptions; - options.ChannelFactory = new ChannelFactory( - new KafkaMessageConsumerFactory( - new KafkaMessagingGatewayConfiguration - { - Name = "paramore.brighter", - BootStrapServers = new[] { "localhost:9092" } - } - )); - }).AutoFromAssemblies(); + Name = "paramore.brighter", BootStrapServers = new[] { "localhost:9092" } + } + )); + }).AutoFromAssemblies(); - services.AddHostedService(); - }) - .UseConsoleLifetime() - .Build(); + services.AddHostedService(); + }) + .UseConsoleLifetime() + .Build(); - return host.RunAsync(); - } - } -} +await host.RunAsync(); diff --git a/samples/KafkaSchemaRegistry/GreetingsSender/Program.cs b/samples/KafkaSchemaRegistry/GreetingsSender/Program.cs index 6163e25ec8..7d40c542d6 100644 --- a/samples/KafkaSchemaRegistry/GreetingsSender/Program.cs +++ b/samples/KafkaSchemaRegistry/GreetingsSender/Program.cs @@ -2,7 +2,7 @@ /* The MIT License (MIT) Copyright © 2017 Wayne Hunsley -Copyright © 2021 Ian Cooper Ian Cooper +Copyright © 2021 Ian Cooper Ian Cooper Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal @@ -26,9 +26,9 @@ THE SOFTWARE. */ using System; using System.IO; -using System.Threading.Tasks; using Confluent.SchemaRegistry; using Greetings.Ports.Commands; +using GreetingsSender; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -39,97 +39,85 @@ THE SOFTWARE. */ using Polly; using Polly.Registry; -namespace GreetingsSender -{ - public static class Program +var host = Host.CreateDefaultBuilder(args) + .ConfigureHostConfiguration(configurationBuilder => { - public static Task Main(string[] args) - { - var host = Host.CreateDefaultBuilder(args) - .ConfigureHostConfiguration(configurationBuilder => - { - configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); - configurationBuilder.AddJsonFile("appsettings.json", optional: true); - configurationBuilder.AddCommandLine(args); - }) - .ConfigureLogging((_, builder) => + configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); + configurationBuilder.AddJsonFile("appsettings.json", optional: true); + configurationBuilder.AddCommandLine(args); + }) + .ConfigureLogging((_, builder) => + { + builder.ClearProviders(); + builder.AddConsole(); + builder.AddDebug(); + }) + .ConfigureServices((hostContext, services) => + { + //We take a direct dependency on the schema registry in the message mapper + var schemaRegistryConfig = new SchemaRegistryConfig { Url = "http://localhost:8081" }; + var cachedSchemaRegistryClient = new CachedSchemaRegistryClient(schemaRegistryConfig); + services.AddSingleton(cachedSchemaRegistryClient); + + var producerRegistry = new KafkaProducerRegistryFactory( + new KafkaMessagingGatewayConfiguration { - builder.ClearProviders(); - builder.AddConsole(); - builder.AddDebug(); - }) - .ConfigureServices((hostContext, services) => + Name = "paramore.brighter.greetingsender", BootStrapServers = new[] { "localhost:9092" } + }, + new[] { - //We take a direct dependency on the schema registry in the message mapper - var schemaRegistryConfig = new SchemaRegistryConfig { Url = "http://localhost:8081"}; - var cachedSchemaRegistryClient = new CachedSchemaRegistryClient(schemaRegistryConfig); - services.AddSingleton(cachedSchemaRegistryClient); - - var producerRegistry = new KafkaProducerRegistryFactory( - new KafkaMessagingGatewayConfiguration - { - Name = "paramore.brighter.greetingsender", - BootStrapServers = new[] {"localhost:9092"} - }, - new[] - { - new KafkaPublication - { - Topic = new RoutingKey("greeting.event"), - MessageSendMaxRetries = 3, - MessageTimeoutMs = 1000, - MaxInFlightRequestsPerConnection = 1 - } - }) - .Create(); - - services.AddBrighter(options => - { - options.PolicyRegistry = RegisterPolicies(); - }) - .UseExternalBus((configure) => - { - configure.ProducerRegistry = producerRegistry; - }) - .MapperRegistryFromAssemblies(typeof(GreetingEvent).Assembly); - - services.AddHostedService(); + new KafkaPublication + { + Topic = new RoutingKey("greeting.event"), + MessageSendMaxRetries = 3, + MessageTimeoutMs = 1000, + MaxInFlightRequestsPerConnection = 1 + } }) - .UseConsoleLifetime() - .Build(); - - return host.RunAsync(); - } + .Create(); - private static PolicyRegistry RegisterPolicies() - { - var retryPolicy = Policy.Handle().WaitAndRetry(new[] + services.AddBrighter(options => { - TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), - TimeSpan.FromMilliseconds(150) - }); + options.PolicyRegistry = RegisterPolicies(); + }) + .UseExternalBus((configure) => + { + configure.ProducerRegistry = producerRegistry; + }) + .MapperRegistryFromAssemblies(typeof(GreetingEvent).Assembly); - var circuitBreakerPolicy = - Policy.Handle().CircuitBreaker(1, TimeSpan.FromMilliseconds(500)); + services.AddHostedService(); + }) + .UseConsoleLifetime() + .Build(); - var retryPolicyAsync = Policy.Handle().WaitAndRetryAsync(new[] - { - TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), - TimeSpan.FromMilliseconds(150) - }); +await host.RunAsync(); +return; - var circuitBreakerPolicyAsync = Policy.Handle() - .CircuitBreakerAsync(1, TimeSpan.FromMilliseconds(500)); +static PolicyRegistry RegisterPolicies() +{ + var retryPolicy = Policy.Handle().WaitAndRetry(new[] + { + TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(150) + }); - var policyRegistry = new PolicyRegistry - { - {CommandProcessor.RETRYPOLICY, retryPolicy}, - {CommandProcessor.CIRCUITBREAKER, circuitBreakerPolicy}, - {CommandProcessor.RETRYPOLICYASYNC, retryPolicyAsync}, - {CommandProcessor.CIRCUITBREAKERASYNC, circuitBreakerPolicyAsync} - }; - return policyRegistry; - } - } -} + var circuitBreakerPolicy = + Policy.Handle().CircuitBreaker(1, TimeSpan.FromMilliseconds(500)); + var retryPolicyAsync = Policy.Handle().WaitAndRetryAsync(new[] + { + TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(150) + }); + + var circuitBreakerPolicyAsync = Policy.Handle() + .CircuitBreakerAsync(1, TimeSpan.FromMilliseconds(500)); + + var policyRegistry = new PolicyRegistry + { + { CommandProcessor.RETRYPOLICY, retryPolicy }, + { CommandProcessor.CIRCUITBREAKER, circuitBreakerPolicy }, + { CommandProcessor.RETRYPOLICYASYNC, retryPolicyAsync }, + { CommandProcessor.CIRCUITBREAKERASYNC, circuitBreakerPolicyAsync } + }; + return policyRegistry; +} diff --git a/samples/KafkaTaskQueue/Greetings/Ports/CommandHandlers/GreetingEventHandler.cs b/samples/KafkaTaskQueue/Greetings/Ports/CommandHandlers/GreetingEventHandler.cs index a5f5fa2a1b..108fce926a 100644 --- a/samples/KafkaTaskQueue/Greetings/Ports/CommandHandlers/GreetingEventHandler.cs +++ b/samples/KafkaTaskQueue/Greetings/Ports/CommandHandlers/GreetingEventHandler.cs @@ -23,21 +23,23 @@ THE SOFTWARE. */ #endregion using System; +using System.Threading; +using System.Threading.Tasks; using Greetings.Ports.Commands; using Paramore.Brighter; namespace Greetings.Ports.CommandHandlers { - public class GreetingEventHandler : RequestHandler + public class GreetingEventHandlerAsync : RequestHandlerAsync { - public override GreetingEvent Handle(GreetingEvent @event) + public override async Task HandleAsync(GreetingEvent @event, CancellationToken cancellationToken = default) { Console.WriteLine("Received Greeting. Message Follows"); Console.WriteLine("----------------------------------"); Console.WriteLine(@event.Greeting); Console.WriteLine("----------------------------------"); Console.WriteLine("Message Ends"); - return base.Handle(@event); + return await base.HandleAsync(@event, cancellationToken); } } } diff --git a/samples/KafkaTaskQueue/Greetings/Ports/Mappers/GreetingEventMessageMapper.cs b/samples/KafkaTaskQueue/Greetings/Ports/Mappers/GreetingEventMessageMapperAsync.cs similarity index 66% rename from samples/KafkaTaskQueue/Greetings/Ports/Mappers/GreetingEventMessageMapper.cs rename to samples/KafkaTaskQueue/Greetings/Ports/Mappers/GreetingEventMessageMapperAsync.cs index d7dc5dc0d9..ea9aa26833 100644 --- a/samples/KafkaTaskQueue/Greetings/Ports/Mappers/GreetingEventMessageMapper.cs +++ b/samples/KafkaTaskQueue/Greetings/Ports/Mappers/GreetingEventMessageMapperAsync.cs @@ -22,18 +22,26 @@ THE SOFTWARE. */ #endregion +using System.IO; using Greetings.Ports.Commands; using System.Text.Json; +using System.Threading.Tasks; using Paramore.Brighter; namespace Greetings.Ports.Mappers { - public class GreetingEventMessageMapper : IAmAMessageMapper + public class GreetingEventMessageMapperAsync : IAmAMessageMapperAsync { - public Message MapToMessage(GreetingEvent request) + //NOTE: Typically you should use the Serdes provided by the Kafka client, but we're using the .NET serializer here + //See the Schema Registry sample for an example of how to use the Confluent Serdes serializer + + public async Task MapToMessage(GreetingEvent request) { + var header = new MessageHeader(messageId: request.Id, topic: "greeting.event", messageType: MessageType.MT_EVENT); - var body = new MessageBody(JsonSerializer.Serialize(request, JsonSerialisationOptions.Options)); + var ms = new MemoryStream(); + await JsonSerializer.SerializeAsync(ms, request, JsonSerialisationOptions.Options); + var body = new MessageBody(ms.ToArray()); //This won't have repeats that need to go to the same partition, but it's a good example of how to set the partition key header.PartitionKey = request.Id.ToString(); @@ -42,10 +50,10 @@ public Message MapToMessage(GreetingEvent request) return message; } - public GreetingEvent MapToRequest(Message message) + public async Task MapToRequest(Message message) { - var greetingCommand = JsonSerializer.Deserialize(message.Body.Value, JsonSerialisationOptions.Options); - + using var ms = new MemoryStream(message.Body.Bytes); + var greetingCommand = await JsonSerializer.DeserializeAsync(ms, JsonSerialisationOptions.Options); return greetingCommand; } } diff --git a/samples/KafkaTaskQueue/GreetingsReceiverConsole/Program.cs b/samples/KafkaTaskQueue/GreetingsReceiverConsole/Program.cs index dcf0c81ac6..264deed9c5 100644 --- a/samples/KafkaTaskQueue/GreetingsReceiverConsole/Program.cs +++ b/samples/KafkaTaskQueue/GreetingsReceiverConsole/Program.cs @@ -23,9 +23,7 @@ THE SOFTWARE. */ #endregion -using System; using System.IO; -using System.Threading.Tasks; using Confluent.Kafka; using Greetings.Ports.Commands; using Microsoft.Extensions.Configuration; @@ -37,58 +35,53 @@ THE SOFTWARE. */ using Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection; using Paramore.Brighter.ServiceActivator.Extensions.Hosting; -namespace GreetingsReceiverConsole -{ - public class Program +var host = Host.CreateDefaultBuilder(args) + .ConfigureHostConfiguration(configurationBuilder => { - public static async Task Main(string[] args) + configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); + configurationBuilder.AddJsonFile("appsettings.json", optional: true); + configurationBuilder.AddCommandLine(args); + }) + .ConfigureLogging((context, builder) => + { + builder.AddConsole(); + builder.AddDebug(); + }) + .ConfigureServices((hostContext, services) => + { + var subscriptions = new KafkaSubscription[] { - var host = Host.CreateDefaultBuilder(args) - .ConfigureHostConfiguration(configurationBuilder => - { - configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); - configurationBuilder.AddJsonFile("appsettings.json", optional: true); - configurationBuilder.AddCommandLine(args); - }) - .ConfigureLogging((context, builder) => - { - builder.AddConsole(); - builder.AddDebug(); - }) - .ConfigureServices((hostContext, services) => - { - var subscriptions = new KafkaSubscription[] - { - new KafkaSubscription( - new SubscriptionName("paramore.example.greeting"), - channelName: new ChannelName("greeting.event"), - routingKey: new RoutingKey("greeting.event"), - groupId: "kafka-GreetingsReceiverConsole-Sample", - numOfPartitions: 3, - timeoutInMilliseconds: 100, - offsetDefault: AutoOffsetReset.Earliest, - commitBatchSize: 5, - sweepUncommittedOffsetsIntervalMs: 10000) - }; + new KafkaSubscription( + new SubscriptionName("paramore.example.greeting"), + channelName: new ChannelName("greeting.event"), + routingKey: new RoutingKey("greeting.event"), + groupId: "kafka-GreetingsReceiverConsole-Sample", + numOfPartitions: 3, + timeoutInMilliseconds: 100, + offsetDefault: AutoOffsetReset.Earliest, + commitBatchSize: 5, + sweepUncommittedOffsetsIntervalMs: 10000, + isAsync:true) + }; - //create the gateway - var consumerFactory = new KafkaMessageConsumerFactory( - new KafkaMessagingGatewayConfiguration { Name = "paramore.brighter", BootStrapServers = new[] { "localhost:9092" } } - ); + //create the gateway + var consumerFactory = new KafkaMessageConsumerFactory( + new KafkaMessagingGatewayConfiguration + { + Name = "paramore.brighter", BootStrapServers = new[] { "localhost:9092" } + } + ); - services.AddServiceActivator(options => - { - options.Subscriptions = subscriptions; - options.ChannelFactory = new ChannelFactory(consumerFactory); - }).AutoFromAssemblies(); + services.AddServiceActivator(options => + { + options.Subscriptions = subscriptions; + options.ChannelFactory = new ChannelFactory(consumerFactory); + }).AutoFromAssemblies(); - services.AddHostedService(); - }) - .UseConsoleLifetime() - .Build(); + services.AddHostedService(); + }) + .UseConsoleLifetime() + .Build(); - await host.RunAsync(); - } - } -} +await host.RunAsync(); diff --git a/samples/KafkaTaskQueue/GreetingsSender/Program.cs b/samples/KafkaTaskQueue/GreetingsSender/Program.cs index 0d0f5493ee..4315c63695 100644 --- a/samples/KafkaTaskQueue/GreetingsSender/Program.cs +++ b/samples/KafkaTaskQueue/GreetingsSender/Program.cs @@ -2,7 +2,7 @@ /* The MIT License (MIT) Copyright © 2017 Wayne Hunsley -Copyright © 2021 Ian Cooper Ian Cooper +Copyright © 2021 Ian Cooper Ian Cooper Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal @@ -25,10 +25,9 @@ THE SOFTWARE. */ #endregion using System; -using System.Data.Common; using System.IO; -using System.Threading.Tasks; using Greetings.Ports.Commands; +using GreetingsSender; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -39,89 +38,76 @@ THE SOFTWARE. */ using Polly; using Polly.Registry; -namespace GreetingsSender -{ - internal static class Program +var host = Host.CreateDefaultBuilder(args) + .ConfigureHostConfiguration(configurationBuilder => { - static async Task Main(string[] args) + configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); + configurationBuilder.AddJsonFile("appsettings.json", optional: true); + configurationBuilder.AddCommandLine(args); + }) + .ConfigureLogging((context, builder) => + { + builder.ClearProviders(); + builder.AddConsole(); + builder.AddDebug(); + }) + .ConfigureServices((hostContext, services) => + { + var retryPolicy = Policy.Handle().WaitAndRetry(new[] { - var host = Host.CreateDefaultBuilder(args) - .ConfigureHostConfiguration(configurationBuilder => - { - configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()); - configurationBuilder.AddJsonFile("appsettings.json", optional: true); - configurationBuilder.AddCommandLine(args); - }) - .ConfigureLogging((context, builder) => - { - builder.ClearProviders(); - builder.AddConsole(); - builder.AddDebug(); - }) - .ConfigureServices((hostContext, services) => - { - var retryPolicy = Policy.Handle().WaitAndRetry(new[] - { - TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), - TimeSpan.FromMilliseconds(150) - }); + TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(150) + }); - var circuitBreakerPolicy = - Policy.Handle().CircuitBreaker(1, TimeSpan.FromMilliseconds(500)); + var circuitBreakerPolicy = + Policy.Handle().CircuitBreaker(1, TimeSpan.FromMilliseconds(500)); - var retryPolicyAsync = Policy.Handle().WaitAndRetryAsync(new[] - { - TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), - TimeSpan.FromMilliseconds(150) - }); - - var circuitBreakerPolicyAsync = Policy.Handle() - .CircuitBreakerAsync(1, TimeSpan.FromMilliseconds(500)); + var retryPolicyAsync = Policy.Handle().WaitAndRetryAsync(new[] + { + TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(150) + }); - var policyRegistry = new PolicyRegistry - { - {CommandProcessor.RETRYPOLICY, retryPolicy}, - {CommandProcessor.CIRCUITBREAKER, circuitBreakerPolicy}, - {CommandProcessor.RETRYPOLICYASYNC, retryPolicyAsync}, - {CommandProcessor.CIRCUITBREAKERASYNC, circuitBreakerPolicyAsync} - }; + var circuitBreakerPolicyAsync = Policy.Handle() + .CircuitBreakerAsync(1, TimeSpan.FromMilliseconds(500)); - var producerRegistry = new KafkaProducerRegistryFactory( - new KafkaMessagingGatewayConfiguration - { - Name = "paramore.brighter.greetingsender", - BootStrapServers = new[] {"localhost:9092"} - }, - new KafkaPublication[] - { - new KafkaPublication - { - Topic = new RoutingKey("greeting.event"), - NumPartitions = 3, - MessageSendMaxRetries = 3, - MessageTimeoutMs = 1000, - MaxInFlightRequestsPerConnection = 1 - } - }) - .Create(); - - services.AddBrighter(options => - { - options.PolicyRegistry = policyRegistry; - }) - .UseExternalBus((configure) => - { - configure.ProducerRegistry = producerRegistry; - }) - .MapperRegistryFromAssemblies(typeof(GreetingEvent).Assembly); + var policyRegistry = new PolicyRegistry + { + { CommandProcessor.RETRYPOLICY, retryPolicy }, + { CommandProcessor.CIRCUITBREAKER, circuitBreakerPolicy }, + { CommandProcessor.RETRYPOLICYASYNC, retryPolicyAsync }, + { CommandProcessor.CIRCUITBREAKERASYNC, circuitBreakerPolicyAsync } + }; - services.AddHostedService(); + var producerRegistry = new KafkaProducerRegistryFactory( + new KafkaMessagingGatewayConfiguration + { + Name = "paramore.brighter.greetingsender", BootStrapServers = new[] { "localhost:9092" } + }, + new KafkaPublication[] + { + new KafkaPublication + { + Topic = new RoutingKey("greeting.event"), + NumPartitions = 3, + MessageSendMaxRetries = 3, + MessageTimeoutMs = 1000, + MaxInFlightRequestsPerConnection = 1 + } }) - .UseConsoleLifetime() - .Build(); + .Create(); + + services.AddBrighter(options => + { + options.PolicyRegistry = policyRegistry; + }) + .UseExternalBus((configure) => + { + configure.ProducerRegistry = producerRegistry; + }) + .MapperRegistryFromAssemblies(typeof(GreetingEvent).Assembly); - await host.RunAsync(); - } - } -} + services.AddHostedService(); + }) + .UseConsoleLifetime() + .Build(); +await host.RunAsync(); diff --git a/samples/KafkaTaskQueue/GreetingsSender/TimedMessageGenerator.cs b/samples/KafkaTaskQueue/GreetingsSender/TimedMessageGenerator.cs index 22c3656c87..e2042f13a5 100644 --- a/samples/KafkaTaskQueue/GreetingsSender/TimedMessageGenerator.cs +++ b/samples/KafkaTaskQueue/GreetingsSender/TimedMessageGenerator.cs @@ -8,22 +8,15 @@ namespace GreetingsSender { - public class TimedMessageGenerator : IHostedService, IDisposable + public class TimedMessageGenerator(IAmACommandProcessor processor, ILogger logger) + : IHostedService, IDisposable { - private readonly IAmACommandProcessor _processor; - private readonly ILogger _logger; private Timer _timer; private long _iteration = 0; - public TimedMessageGenerator(IAmACommandProcessor processor, ILogger logger) - { - _processor = processor; - _logger = logger; - } - public Task StartAsync(CancellationToken cancellationToken) { - _logger.LogInformation("Kafka Message Generator is starting."); + logger.LogInformation("Kafka Message Generator is starting"); _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(500)); @@ -32,7 +25,7 @@ public Task StartAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken) { - _logger.LogInformation("Kafka Message Generator is stopping."); + logger.LogInformation("Kafka Message Generator is stopping"); _timer?.Change(Timeout.Infinite, 0); @@ -45,9 +38,9 @@ private void DoWork(object state) var greetingEvent = new GreetingEvent{ Id = Guid.NewGuid(), Greeting = $"Hello # {_iteration}"}; - _processor.Post(greetingEvent); + processor.Post(greetingEvent); - _logger.LogInformation("Sending message with id {Id} and greeting {Request}", greetingEvent.Id, + logger.LogInformation("Sending message with id {Id} and greeting {Request}", greetingEvent.Id, greetingEvent.Greeting); } diff --git a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs index 01630bb8ed..ef738de55f 100644 --- a/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs +++ b/samples/WebAPI_Dapper/GreetingsPorts/Handlers/AddGreetingHandlerAsync.cs @@ -19,9 +19,6 @@ public class AddGreetingHandlerAsync( ILogger logger) : RequestHandlerAsync { - //We want to take the dependency on the same instance that will be used via the Outbox, so use the marker interface - - [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] public override async Task HandleAsync(AddGreeting addGreeting, CancellationToken cancellationToken = default) From 9096ac0b7edd60a6a7137d50b9af3d617ecd85d4 Mon Sep 17 00:00:00 2001 From: Ian Cooper Date: Wed, 24 Jan 2024 18:39:56 +0000 Subject: [PATCH 15/15] Confirm WebAPI samples work with RMQ + Async Mappers --- .../.idea/httpRequests/http-requests-log.http | 276 +++++++++--------- .../GreetingsReceiverConsole/Program.cs | 2 +- .../GreetingsReceiverConsole/Program.cs | 2 +- .../Mappers/GreetingMadeMessageMapperAsync.cs | 1 + .../Properties/launchSettings.json | 2 +- .../SalutationAnalytics/Program.cs | 3 +- .../SalutationAnalytics/Program.cs | 2 +- .../Handlers/AddPersonHandlerAsync.cs | 4 +- .../Mappers/GreetingMadeMessageMapperAsync.cs | 1 + .../SalutationAnalytics/Program.cs | 4 +- .../Handlers/GreetingMadeHandler.cs | 2 +- .../SalutationPorts/Policies/Retry.cs | 12 +- .../Policies/SalutationPolicy.cs | 4 +- .../KafkaSubscription.cs | 12 +- .../TransformPipelineBuilder.cs | 2 +- .../TransformPipelineBuilderAsync.cs | 2 +- 16 files changed, 173 insertions(+), 158 deletions(-) diff --git a/.idea/.idea.Brighter/.idea/httpRequests/http-requests-log.http b/.idea/.idea.Brighter/.idea/httpRequests/http-requests-log.http index 10e80c8e3b..849c2eb385 100644 --- a/.idea/.idea.Brighter/.idea/httpRequests/http-requests-log.http +++ b/.idea/.idea.Brighter/.idea/httpRequests/http-requests-log.http @@ -2,29 +2,29 @@ POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json Content-Length: 47 Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) Accept-Encoding: br,deflate,gzip,x-gzip { "Greeting" : "I drink, and I know things" } -<> 2023-07-10T160215.200.json +<> 2024-01-24T183712.200.json ### -POST http://localhost:5000/Greetings/Tyrion/new +POST http://localhost:5000/People/new Content-Type: application/json -Content-Length: 47 +Content-Length: 23 Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) Accept-Encoding: br,deflate,gzip,x-gzip { - "Greeting" : "I drink, and I know things" + "Name" : "Tyrion" } -<> 2023-07-10T160214.200.json +<> 2024-01-24T183709.200.json ### @@ -32,59 +32,66 @@ POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json Content-Length: 47 Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) Accept-Encoding: br,deflate,gzip,x-gzip { "Greeting" : "I drink, and I know things" } -<> 2023-07-10T160213.200.json +<> 2024-01-24T183459.200.json ### -POST http://localhost:5000/Greetings/Tyrion/new +GET http://localhost:5000/People/Tyrion +Connection: Keep-Alive +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) +Accept-Encoding: br,deflate,gzip,x-gzip + +<> 2024-01-24T183456.200.json + +### + +POST http://localhost:5000/People/new Content-Type: application/json -Content-Length: 47 +Content-Length: 23 Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) Accept-Encoding: br,deflate,gzip,x-gzip { - "Greeting" : "I drink, and I know things" + "Name" : "Tyrion" } -<> 2023-07-10T160212.200.json - ### POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json Content-Length: 47 Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) Accept-Encoding: br,deflate,gzip,x-gzip { "Greeting" : "I drink, and I know things" } -<> 2023-07-10T160211-1.200.json +<> 2024-01-24T181407.200.json ### -POST http://localhost:5000/Greetings/Tyrion/new +POST http://localhost:5000/People/new Content-Type: application/json -Content-Length: 47 +Content-Length: 23 Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) Accept-Encoding: br,deflate,gzip,x-gzip { - "Greeting" : "I drink, and I know things" + "Name" : "Tyrion" } -<> 2023-07-10T160211.200.json +<> 2024-01-24T181403.200.json ### @@ -92,29 +99,27 @@ POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json Content-Length: 47 Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) Accept-Encoding: br,deflate,gzip,x-gzip { "Greeting" : "I drink, and I know things" } -<> 2023-07-10T160210.200.json - ### POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json Content-Length: 47 Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) Accept-Encoding: br,deflate,gzip,x-gzip { "Greeting" : "I drink, and I know things" } -<> 2023-07-10T160209.200.json +<> 2024-01-24T173125.200.json ### @@ -122,29 +127,38 @@ POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json Content-Length: 47 Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) Accept-Encoding: br,deflate,gzip,x-gzip { "Greeting" : "I drink, and I know things" } -<> 2023-07-10T124554.200.json +<> 2024-01-24T170718.200.json ### -POST http://localhost:5000/Greetings/Tyrion/new +GET http://localhost:5000/People/Tyrion +Connection: Keep-Alive +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) +Accept-Encoding: br,deflate,gzip,x-gzip + +<> 2024-01-24T170711.200.json + +### + +POST http://localhost:5000/People/new Content-Type: application/json -Content-Length: 47 +Content-Length: 23 Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.9) Accept-Encoding: br,deflate,gzip,x-gzip { - "Greeting" : "I drink, and I know things" + "Name" : "Tyrion" } -<> 2023-07-10T124553-1.200.json +<> 2024-01-24T170705.200.json ### @@ -159,7 +173,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-10T124553.200.json +<> 2023-07-10T160215.200.json ### @@ -174,7 +188,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-10T124551.200.json +<> 2023-07-10T160214.200.json ### @@ -189,7 +203,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-10T124550.200.json +<> 2023-07-10T160213.200.json ### @@ -204,7 +218,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-10T124549.200.json +<> 2023-07-10T160212.200.json ### @@ -219,7 +233,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-10T124547.200.json +<> 2023-07-10T160211-1.200.json ### @@ -234,7 +248,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-10T124546.200.json +<> 2023-07-10T160211.200.json ### @@ -249,25 +263,37 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-10T124544.200.json +<> 2023-07-10T160210.200.json ### -GET http://localhost:5000/People/Tyrion +POST http://localhost:5000/Greetings/Tyrion/new +Content-Type: application/json +Content-Length: 47 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -<> 2023-07-10T124540.200.json +{ + "Greeting" : "I drink, and I know things" +} + +<> 2023-07-10T160209.200.json ### -GET http://localhost:5000/Greetings/Tyrion +POST http://localhost:5000/Greetings/Tyrion/new +Content-Type: application/json +Content-Length: 47 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -<> 2023-07-07T200406.200.json +{ + "Greeting" : "I drink, and I know things" +} + +<> 2023-07-10T124554.200.json ### @@ -282,59 +308,67 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-07T200403.200.json +<> 2023-07-10T124553-1.200.json ### -GET http://localhost:5000/People/Tyrion +POST http://localhost:5000/Greetings/Tyrion/new +Content-Type: application/json +Content-Length: 47 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -<> 2023-07-07T200359.200.json +{ + "Greeting" : "I drink, and I know things" +} + +<> 2023-07-10T124553.200.json ### POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json -Content-Length: 50 +Content-Length: 47 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip { - "Greeting" : "I drink, and I know things #1" + "Greeting" : "I drink, and I know things" } +<> 2023-07-10T124551.200.json + ### POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json -Content-Length: 50 +Content-Length: 47 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip { - "Greeting" : "I drink, and I know things #1" + "Greeting" : "I drink, and I know things" } -<> 2023-07-07T195459.200.json +<> 2023-07-10T124550.200.json ### POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json -Content-Length: 52 +Content-Length: 47 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip { - "Greeting" : "I drink, and I know more things" + "Greeting" : "I drink, and I know things" } -<> 2023-07-07T192903.200.json +<> 2023-07-10T124549.200.json ### @@ -349,16 +383,22 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-07T190420.200.json +<> 2023-07-10T124547.200.json ### -GET http://localhost:5000/Greetings/Tyrion +POST http://localhost:5000/Greetings/Tyrion/new +Content-Type: application/json +Content-Length: 47 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -<> 2023-07-07T190355.200.json +{ + "Greeting" : "I drink, and I know things" +} + +<> 2023-07-10T124546.200.json ### @@ -373,7 +413,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-07T190352.200.json +<> 2023-07-10T124544.200.json ### @@ -382,7 +422,7 @@ Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -<> 2023-07-07T190349.200.json +<> 2023-07-10T124540.200.json ### @@ -391,7 +431,7 @@ Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -<> 2023-07-07T165603.200.json +<> 2023-07-07T200406.200.json ### @@ -406,103 +446,83 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-07T165600.200.json +<> 2023-07-07T200403.200.json ### -POST http://localhost:5000/Greetings/Tyrion/new -Content-Type: application/json -Content-Length: 47 +GET http://localhost:5000/People/Tyrion Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -{ - "Greeting" : "I drink, and I know things" -} - -<> 2023-07-07T165559.200.json +<> 2023-07-07T200359.200.json ### POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json -Content-Length: 47 +Content-Length: 50 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip { - "Greeting" : "I drink, and I know things" + "Greeting" : "I drink, and I know things #1" } -<> 2023-07-07T165557.200.json - ### POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json -Content-Length: 47 +Content-Length: 50 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip { - "Greeting" : "I drink, and I know things" + "Greeting" : "I drink, and I know things #1" } -<> 2023-07-07T165555.200.json - -### - -GET http://localhost:5000/Greetings/Tyrion -Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) -Accept-Encoding: br,deflate,gzip,x-gzip - -<> 2023-07-07T165552.200.json +<> 2023-07-07T195459.200.json ### -GET http://localhost:5000/People/Tyrion +POST http://localhost:5000/Greetings/Tyrion/new +Content-Type: application/json +Content-Length: 52 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -<> 2023-07-07T165549.200.json - -### - -GET http://localhost:5000/People/Tyrion -Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) -Accept-Encoding: br,deflate,gzip,x-gzip +{ + "Greeting" : "I drink, and I know more things" +} -<> 2023-07-07T165406.200.json +<> 2023-07-07T192903.200.json ### -POST http://localhost:5000/People/new +POST http://localhost:5000/Greetings/Tyrion/new Content-Type: application/json -Content-Length: 23 +Content-Length: 47 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip { - "Name" : "Tyrion" + "Greeting" : "I drink, and I know things" } -<> 2023-07-07T165401.200.json +<> 2023-07-07T190420.200.json ### -GET http://localhost:5000/People/Tyrion +GET http://localhost:5000/Greetings/Tyrion Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -<> 2023-07-07T165354.500.json +<> 2023-07-07T190355.200.json ### @@ -517,22 +537,25 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-04T212401.200.json +<> 2023-07-07T190352.200.json ### -POST http://localhost:5000/Greetings/Tyrion/new -Content-Type: application/json -Content-Length: 47 +GET http://localhost:5000/People/Tyrion Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -{ - "Greeting" : "I drink, and I know things" -} +<> 2023-07-07T190349.200.json -<> 2023-07-04T212358.200.json +### + +GET http://localhost:5000/Greetings/Tyrion +Connection: Keep-Alive +User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) +Accept-Encoding: br,deflate,gzip,x-gzip + +<> 2023-07-07T165603.200.json ### @@ -547,7 +570,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-04T212356.200.json +<> 2023-07-07T165600.200.json ### @@ -562,7 +585,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-04T211455.200.json +<> 2023-07-07T165559.200.json ### @@ -577,7 +600,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-04T202333.200.json +<> 2023-07-07T165557.200.json ### @@ -592,22 +615,16 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Greeting" : "I drink, and I know things" } -<> 2023-07-04T202332.200.json +<> 2023-07-07T165555.200.json ### -POST http://localhost:5000/Greetings/Tyrion/new -Content-Type: application/json -Content-Length: 47 +GET http://localhost:5000/Greetings/Tyrion Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -{ - "Greeting" : "I drink, and I know things" -} - -<> 2023-07-04T202330.200.json +<> 2023-07-07T165552.200.json ### @@ -616,7 +633,7 @@ Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -<> 2023-07-04T202327.200.json +<> 2023-07-07T165549.200.json ### @@ -625,7 +642,7 @@ Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -<> 2023-07-04T201903.200.json +<> 2023-07-07T165406.200.json ### @@ -640,7 +657,7 @@ Accept-Encoding: br,deflate,gzip,x-gzip "Name" : "Tyrion" } -<> 2023-07-04T201859.200.json +<> 2023-07-07T165401.200.json ### @@ -649,12 +666,7 @@ Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) Accept-Encoding: br,deflate,gzip,x-gzip -### - -GET http://localhost:5000/People/Tyrion -Connection: Keep-Alive -User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.6) -Accept-Encoding: br,deflate,gzip,x-gzip +<> 2023-07-07T165354.500.json ### diff --git a/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs b/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs index 10b00f2e34..9b0443b81a 100644 --- a/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs +++ b/samples/KafkaSchemaRegistry/GreetingsReceiverConsole/Program.cs @@ -61,7 +61,7 @@ THE SOFTWARE. */ offsetDefault: AutoOffsetReset.Earliest, commitBatchSize: 5, sweepUncommittedOffsetsIntervalMs: 10000, - isAsync: true) + runAsync: true) }; //We take a direct dependency on the schema registry in the message mapper diff --git a/samples/KafkaTaskQueue/GreetingsReceiverConsole/Program.cs b/samples/KafkaTaskQueue/GreetingsReceiverConsole/Program.cs index 264deed9c5..ffbb46d09c 100644 --- a/samples/KafkaTaskQueue/GreetingsReceiverConsole/Program.cs +++ b/samples/KafkaTaskQueue/GreetingsReceiverConsole/Program.cs @@ -61,7 +61,7 @@ THE SOFTWARE. */ offsetDefault: AutoOffsetReset.Earliest, commitBatchSize: 5, sweepUncommittedOffsetsIntervalMs: 10000, - isAsync:true) + runAsync:true) }; //create the gateway diff --git a/samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs b/samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs index 619789d036..103cf87b54 100644 --- a/samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs +++ b/samples/WebAPI_Dapper/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs @@ -13,6 +13,7 @@ public async Task MapToMessage(GreetingMade request) { //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using //TaskCompletionSource for a Task over sync instead + //For Kafka Serdes see the Kafka Schema Registry examples var header = new MessageHeader(messageId: request.Id, topic: "GreetingMade", messageType: MessageType.MT_EVENT); using var ms = new MemoryStream(); await JsonSerializer.SerializeAsync(ms, request, new JsonSerializerOptions(JsonSerializerDefaults.General)); diff --git a/samples/WebAPI_Dapper/GreetingsWeb/Properties/launchSettings.json b/samples/WebAPI_Dapper/GreetingsWeb/Properties/launchSettings.json index 14641d72fe..b8d93cfd6d 100644 --- a/samples/WebAPI_Dapper/GreetingsWeb/Properties/launchSettings.json +++ b/samples/WebAPI_Dapper/GreetingsWeb/Properties/launchSettings.json @@ -18,7 +18,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "BRIGHTER_GREETINGS_DATABASE": "Sqlite", - "BRIGHTER_TRANSPORT": "Kafka" + "BRIGHTER_TRANSPORT": "RabbitMQ" } }, "ProductionMySql": { diff --git a/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs b/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs index 1bad2b3798..238d86f666 100644 --- a/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs +++ b/samples/WebAPI_Dapper/SalutationAnalytics/Program.cs @@ -411,7 +411,7 @@ static Subscription[] GetRmqSubscriptions() new SubscriptionName("paramore.sample.salutationanalytics"), new ChannelName("SalutationAnalytics"), new RoutingKey("GreetingMade"), - runAsync: false, + runAsync: true, timeoutInMilliseconds: 200, isDurable: true, makeChannels: OnMissingChannel @@ -443,6 +443,7 @@ static Subscription[] GetKafkaSubscriptions() offsetDefault: AutoOffsetReset.Earliest, commitBatchSize: 5, sweepUncommittedOffsetsIntervalMs: 10000, + runAsync: true, makeChannels: OnMissingChannel.Create) }; return subscriptions; diff --git a/samples/WebAPI_Dynamo/SalutationAnalytics/Program.cs b/samples/WebAPI_Dynamo/SalutationAnalytics/Program.cs index 9c96f6d7dd..5b205c2534 100644 --- a/samples/WebAPI_Dynamo/SalutationAnalytics/Program.cs +++ b/samples/WebAPI_Dynamo/SalutationAnalytics/Program.cs @@ -65,7 +65,7 @@ static void ConfigureBrighter( new SubscriptionName("paramore.sample.salutationanalytics"), new ChannelName("SalutationAnalytics"), new RoutingKey("GreetingMade"), - runAsync: false, + runAsync: true, timeoutInMilliseconds: 200, isDurable: true, makeChannels: OnMissingChannel diff --git a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs index 1de4e9a74c..c2f7b7b41c 100644 --- a/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs +++ b/samples/WebAPI_EFCore/GreetingsPorts/Handlers/AddPersonHandlerAsync.cs @@ -1,15 +1,15 @@ using System.Threading; using System.Threading.Tasks; using GreetingsEntities; +using GreetingsPorts.EntityGateway; using GreetingsPorts.Requests; -using Microsoft.EntityFrameworkCore; using Paramore.Brighter; using Paramore.Brighter.Logging.Attributes; using Paramore.Brighter.Policies.Attributes; namespace GreetingsPorts.Handlers { - public class AddPersonHandlerAsync(DbContext uow) : RequestHandlerAsync + public class AddPersonHandlerAsync(GreetingsEntityGateway uow) : RequestHandlerAsync { [RequestLoggingAsync(0, HandlerTiming.Before)] [UsePolicyAsync(step:1, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICYASYNC)] diff --git a/samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs b/samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs index 619789d036..103cf87b54 100644 --- a/samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs +++ b/samples/WebAPI_EFCore/GreetingsWeb/Mappers/GreetingMadeMessageMapperAsync.cs @@ -13,6 +13,7 @@ public async Task MapToMessage(GreetingMade request) { //NOTE: We are showing an async pipeline here, but it is often overkill by comparison to using //TaskCompletionSource for a Task over sync instead + //For Kafka Serdes see the Kafka Schema Registry examples var header = new MessageHeader(messageId: request.Id, topic: "GreetingMade", messageType: MessageType.MT_EVENT); using var ms = new MemoryStream(); await JsonSerializer.SerializeAsync(ms, request, new JsonSerializerOptions(JsonSerializerDefaults.General)); diff --git a/samples/WebAPI_EFCore/SalutationAnalytics/Program.cs b/samples/WebAPI_EFCore/SalutationAnalytics/Program.cs index ccd2f7adc6..8ddefc31d8 100644 --- a/samples/WebAPI_EFCore/SalutationAnalytics/Program.cs +++ b/samples/WebAPI_EFCore/SalutationAnalytics/Program.cs @@ -71,7 +71,7 @@ static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection new SubscriptionName("paramore.sample.salutationanalytics"), new ChannelName("SalutationAnalytics"), new RoutingKey("GreetingMade"), - runAsync: false, + runAsync: true, timeoutInMilliseconds: 200, isDurable: true, makeChannels: OnMissingChannel @@ -131,7 +131,7 @@ static void ConfigureEFCore(HostBuilderContext hostContext, IServiceCollection s builder.UseSqlite(connectionString, optionsBuilder => { - optionsBuilder.MigrationsAssembly("Salutations_Migrations"); + optionsBuilder.MigrationsAssembly("Salutations_SqliteMigrations"); }); }); } diff --git a/samples/WebAPI_EFCore/SalutationPorts/Handlers/GreetingMadeHandler.cs b/samples/WebAPI_EFCore/SalutationPorts/Handlers/GreetingMadeHandler.cs index 1caabf9e25..b2ff597c61 100644 --- a/samples/WebAPI_EFCore/SalutationPorts/Handlers/GreetingMadeHandler.cs +++ b/samples/WebAPI_EFCore/SalutationPorts/Handlers/GreetingMadeHandler.cs @@ -20,7 +20,7 @@ public class GreetingMadeHandlerAsync( { [UseInboxAsync(step:0, contextKey: typeof(GreetingMadeHandlerAsync), onceOnly: true )] [RequestLoggingAsync(step: 1, timing: HandlerTiming.Before)] - [UsePolicyAsync(step:2, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICY)] + [UsePolicyAsync(step:2, policy: Policies.Retry.EXPONENTIAL_RETRYPOLICY_ASYNC)] public override async Task HandleAsync(GreetingMade @event, CancellationToken cancellationToken = default) { var posts = new List(); diff --git a/samples/WebAPI_EFCore/SalutationPorts/Policies/Retry.cs b/samples/WebAPI_EFCore/SalutationPorts/Policies/Retry.cs index 40ec53c32b..781111e51c 100644 --- a/samples/WebAPI_EFCore/SalutationPorts/Policies/Retry.cs +++ b/samples/WebAPI_EFCore/SalutationPorts/Policies/Retry.cs @@ -7,21 +7,21 @@ namespace SalutationPorts.Policies { public static class Retry { - public const string RETRYPOLICY = "SalutationPorts.Policies.RetryPolicyAsync"; - public const string EXPONENTIAL_RETRYPOLICY = "SalutationPorts.Policies.ExponenttialRetryPolicyAsync"; + public const string RETRYPOLICYASYNC = "SalutationPorts.Policies.RetryPolicyAsync"; + public const string EXPONENTIAL_RETRYPOLICY_ASYNC = "SalutationPorts.Policies.ExponenttialRetryPolicyAsync"; - public static RetryPolicy GetSimpleHandlerRetryPolicy() + public static AsyncRetryPolicy GetSimpleHandlerRetryPolicyAsync() { - return Policy.Handle().WaitAndRetry(new[] + return Policy.Handle().WaitAndRetryAsync(new[] { TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(150) }); } - public static RetryPolicy GetExponentialHandlerRetryPolicy() + public static AsyncRetryPolicy GetExponentialHandlerRetryPolicyAsync() { var delay = Backoff.ExponentialBackoff(TimeSpan.FromMilliseconds(100), retryCount: 5, fastFirst: true); - return Policy.Handle().WaitAndRetry(delay); + return Policy.Handle().WaitAndRetryAsync(delay); } } } diff --git a/samples/WebAPI_EFCore/SalutationPorts/Policies/SalutationPolicy.cs b/samples/WebAPI_EFCore/SalutationPorts/Policies/SalutationPolicy.cs index 28024f22a7..23ff0b0f22 100644 --- a/samples/WebAPI_EFCore/SalutationPorts/Policies/SalutationPolicy.cs +++ b/samples/WebAPI_EFCore/SalutationPorts/Policies/SalutationPolicy.cs @@ -11,8 +11,8 @@ public SalutationPolicy() private void AddSalutationPolicies() { - Add(Retry.RETRYPOLICY, Retry.GetSimpleHandlerRetryPolicy()); - Add(Retry.EXPONENTIAL_RETRYPOLICY, Retry.GetExponentialHandlerRetryPolicy()); + Add(Retry.RETRYPOLICYASYNC, Retry.GetSimpleHandlerRetryPolicyAsync()); + Add(Retry.EXPONENTIAL_RETRYPOLICY_ASYNC, Retry.GetExponentialHandlerRetryPolicyAsync()); } } } diff --git a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaSubscription.cs b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaSubscription.cs index 6605713fb0..e5659b4137 100644 --- a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaSubscription.cs +++ b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaSubscription.cs @@ -124,7 +124,7 @@ public class KafkaSubscription : Subscription /// How often does the consumer poll for a message to be considered alive, after which Kafka will assume dead and rebalance /// How often do we commit offsets that have yet to be saved /// Should we read messages that are not on all replicas? May cause duplicates. - /// Is this channel read asynchronously + /// Is this channel read asynchronously /// How many partitions should this topic have - used if we create the topic /// How many copies of each partition should we have across our broker's nodes - used if we create the topic /// The channel factory to create channels for Consumer. /// Should we make channels if they don't exist, defaults to creating @@ -149,7 +149,7 @@ public KafkaSubscription ( int maxPollIntervalMs = 300000, int sweepUncommittedOffsetsIntervalMs = 30000, IsolationLevel isolationLevel = IsolationLevel.ReadCommitted, - bool isAsync = false, + bool runAsync = false, int numOfPartitions = 1, short replicationFactor = 1, IAmAChannelFactory channelFactory = null, @@ -158,7 +158,7 @@ public KafkaSubscription ( int channelFailureDelay = 1000, PartitionAssignmentStrategy partitionAssignmentStrategy = PartitionAssignmentStrategy.CooperativeSticky) : base(dataType, name, channelName, routingKey, bufferSize, noOfPerformers, timeoutInMilliseconds, requeueCount, - requeueDelayInMilliseconds, unacceptableMessageLimit, isAsync, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay) + requeueDelayInMilliseconds, unacceptableMessageLimit, runAsync, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay) { CommitBatchSize = commitBatchSize; GroupId = groupId; @@ -194,7 +194,7 @@ public class KafkaSubscription : KafkaSubscription where T : IRequest /// How often does the consumer poll for a message to be considered alive, after which Kafka will assume dead and rebalance /// How often do we commit offsets that have yet to be saved /// Should we read messages that are not on all replicas? May cause duplicates. - /// Is this channel read asynchronously + /// Is this channel read asynchronously /// How many partitions should this topic have - used if we create the topic /// How many copies of each partition should we have across our broker's nodes - used if we create the topic /// Should we make channels if they don't exist, defaults to creating @@ -219,7 +219,7 @@ public KafkaSubscription( int maxPollIntervalMs = 300000, int sweepUncommittedOffsetsIntervalMs = 30000, IsolationLevel isolationLevel = IsolationLevel.ReadCommitted, - bool isAsync = false, + bool runAsync = false, int numOfPartitions = 1, short replicationFactor = 1, IAmAChannelFactory channelFactory = null, @@ -229,7 +229,7 @@ public KafkaSubscription( PartitionAssignmentStrategy partitionAssignmentStrategy = PartitionAssignmentStrategy.CooperativeSticky) : base(typeof(T), name, channelName, routingKey, groupId, bufferSize, noOfPerformers, timeoutInMilliseconds, requeueCount, requeueDelayInMilliseconds, unacceptableMessageLimit, offsetDefault, commitBatchSize, - sessionTimeoutMs, maxPollIntervalMs, sweepUncommittedOffsetsIntervalMs, isolationLevel, isAsync, + sessionTimeoutMs, maxPollIntervalMs, sweepUncommittedOffsetsIntervalMs, isolationLevel, runAsync, numOfPartitions, replicationFactor, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay, partitionAssignmentStrategy) { diff --git a/src/Paramore.Brighter/TransformPipelineBuilder.cs b/src/Paramore.Brighter/TransformPipelineBuilder.cs index 8ddba0ee82..a7392f766f 100644 --- a/src/Paramore.Brighter/TransformPipelineBuilder.cs +++ b/src/Paramore.Brighter/TransformPipelineBuilder.cs @@ -195,7 +195,7 @@ public static void ClearPipelineCache() private IAmAMessageMapper FindMessageMapper() where TRequest : class, IRequest { var messageMapper = _mapperRegistry.Get(); - if (messageMapper == null) throw new InvalidOperationException(string.Format("Could not find mapper for {0}", typeof(TRequest).Name)); + if (messageMapper == null) throw new InvalidOperationException(string.Format("Could not find mapper for {0}. Hint: did you set runAsync on the subscription to match the mapper type?", typeof(TRequest).Name)); return messageMapper; } diff --git a/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs b/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs index cf10530d63..ab2adfc5ba 100644 --- a/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs +++ b/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs @@ -195,7 +195,7 @@ public static void ClearPipelineCache() private IAmAMessageMapperAsync FindMessageMapper() where TRequest : class, IRequest { var messageMapper = _mapperRegistryAsync.GetAsync(); - if (messageMapper == null) throw new InvalidOperationException(string.Format("Could not find mapper for {0}", typeof(TRequest).Name)); + if (messageMapper == null) throw new InvalidOperationException(string.Format("Could not find mapper for {0}. Hint: did you set runAsync on the subscription to match the mapper type?", typeof(TRequest).Name)); return messageMapper; }