From bcdb3762560a280f61087614b61059969a21d5ca Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 22 Nov 2022 13:20:47 +0100 Subject: [PATCH] Fixed queue arguments error due to wrong value types Added test for publish overflows Removed support for Unity Container Changed third party package references to ranges Fixed XML documentation --- .../01-PublishSubscribe.csproj | 2 - Examples/01-PublishSubscribe/Program.cs | 14 --- Examples/ExampleLib/ExampleConsoleApp.cs | 2 +- .../Messaging.TapetiExample.csproj | 2 +- Tapeti.Autofac/AutofacDependencyResolver.cs | 7 +- Tapeti.Autofac/Tapeti.Autofac.csproj | 2 +- .../Tapeti.CastleWindsor.csproj | 2 +- .../WindsorDependencyResolver.cs | 1 - .../DataAnnotationsExtension.cs | 1 - .../DataAnnotationsMessageMiddleware.cs | 1 - .../DataAnnotationsPublishMiddleware.cs | 1 - .../SqlConnectionFlowRepository.cs | 1 - Tapeti.Flow.SQL/SqlExceptionHelper.cs | 8 +- .../Annotations/ContinuationAttribute.cs | 1 - Tapeti.Flow/Annotations/StartAttribute.cs | 1 - Tapeti.Flow/ConfigExtensions.cs | 2 + Tapeti.Flow/Default/FlowBindingMiddleware.cs | 2 +- Tapeti.Flow/Default/FlowHandlerContext.cs | 1 - Tapeti.Flow/Default/FlowProvider.cs | 26 ++---- Tapeti.Flow/Default/FlowStarter.cs | 1 - Tapeti.Flow/Default/FlowState.cs | 4 +- Tapeti.Flow/Default/FlowStore.cs | 3 +- .../Default/NonPersistentFlowRepository.cs | 1 - Tapeti.Flow/FlowExtension.cs | 1 - Tapeti.Flow/FlowHelpers/MethodSerializer.cs | 2 - Tapeti.Flow/IFlowHandlerContext.cs | 1 - Tapeti.Flow/IFlowStore.cs | 1 - Tapeti.Flow/ResponseExpectedException.cs | 1 - Tapeti.Flow/Tapeti.Flow.csproj | 2 +- Tapeti.Flow/YieldPointException.cs | 1 - Tapeti.Ninject/NinjectDependencyResolver.cs | 5 +- Tapeti.Ninject/Tapeti.Ninject.csproj | 2 +- Tapeti.Serilog/Tapeti.Serilog.csproj | 2 +- Tapeti.Serilog/TapetiSeriLogger.cs | 3 +- .../SimpleInjectorDependencyResolver.cs | 1 - .../Tapeti.SimpleInjector.csproj | 2 +- Tapeti.Tests/Client/TapetiClientTests.cs | 59 ++++++++++-- Tapeti.Tests/Config/QueueArgumentsTest.cs | 24 ++--- Tapeti.Tests/Mock/MockLogger.cs | 4 +- Tapeti.Transient/ConfigExtensions.cs | 2 + Tapeti.Transient/TransientGenericBinding.cs | 1 - Tapeti.Transient/TransientPublisher.cs | 1 - Tapeti.Transient/TransientRouter.cs | 4 +- .../Tapeti.UnityContainer.csproj | 35 -------- .../UnityDependencyResolver.cs | 90 ------------------- Tapeti.sln | 9 +- Tapeti.sln.DotSettings | 2 + .../Config/ControllerMessageContextPayload.cs | 1 - Tapeti/Config/IBinding.cs | 12 +-- Tapeti/Config/IControllerBindingMiddleware.cs | 1 - Tapeti/Config/IControllerFilterMiddleware.cs | 1 - Tapeti/Config/IControllerMethodBinding.cs | 1 - Tapeti/Config/ITapetiConfig.cs | 1 - Tapeti/Config/ITapetiExtensionBinding.cs | 1 - Tapeti/Connection/ITapetiClient.cs | 19 +++- Tapeti/Connection/RabbitMQArguments.cs | 36 ++++++++ Tapeti/Connection/TapetiBasicConsumer.cs | 1 - Tapeti/Connection/TapetiClient.cs | 73 ++++++++++----- Tapeti/Connection/TapetiConsumer.cs | 4 +- Tapeti/Connection/TapetiPublisher.cs | 8 +- Tapeti/Connection/TapetiSubscriber.cs | 34 ++++--- Tapeti/Default/CancellationTokenBinding.cs | 1 - Tapeti/Default/ConsoleLogger.cs | 7 +- Tapeti/Default/ControllerBindingContext.cs | 2 - Tapeti/Default/ControllerMethodBinding.cs | 4 +- Tapeti/Default/DependencyResolverBinding.cs | 1 - Tapeti/Default/DevNullLogger.cs | 1 - Tapeti/Default/FallbackStringEnumConverter.cs | 1 - Tapeti/Default/JsonMessageSerializer.cs | 3 +- Tapeti/Default/MessageBinding.cs | 1 - Tapeti/Default/MessageProperties.cs | 1 - Tapeti/Default/NackExceptionStrategy.cs | 1 - .../Default/NamespaceMatchExchangeStrategy.cs | 1 - Tapeti/Default/PublishResultBinding.cs | 1 - Tapeti/Default/RequeueExceptionStrategy.cs | 1 - Tapeti/Default/TypeNameRoutingKeyStrategy.cs | 1 - Tapeti/Exceptions/NackException.cs | 1 - Tapeti/Exceptions/NoRouteException.cs | 1 - Tapeti/Helpers/DictionaryHelper.cs | 2 +- Tapeti/IDependencyResolver.cs | 1 - Tapeti/ILogger.cs | 6 +- Tapeti/IPublisher.cs | 1 - Tapeti/Tapeti.csproj | 4 +- Tapeti/TapetiAppSettingsConnectionParams.cs | 89 ------------------ Tapeti/TapetiConfig.cs | 2 +- Tapeti/TapetiConfigControllers.cs | 27 +++--- Tapeti/TapetiConnection.cs | 1 - Tapeti/Tasks/SingleThreadTaskQueue.cs | 1 - 88 files changed, 257 insertions(+), 440 deletions(-) delete mode 100644 Tapeti.UnityContainer/Tapeti.UnityContainer.csproj delete mode 100644 Tapeti.UnityContainer/UnityDependencyResolver.cs create mode 100644 Tapeti/Connection/RabbitMQArguments.cs delete mode 100644 Tapeti/TapetiAppSettingsConnectionParams.cs diff --git a/Examples/01-PublishSubscribe/01-PublishSubscribe.csproj b/Examples/01-PublishSubscribe/01-PublishSubscribe.csproj index 9d3fabd..bb77e7f 100644 --- a/Examples/01-PublishSubscribe/01-PublishSubscribe.csproj +++ b/Examples/01-PublishSubscribe/01-PublishSubscribe.csproj @@ -11,7 +11,6 @@ - @@ -20,7 +19,6 @@ - diff --git a/Examples/01-PublishSubscribe/Program.cs b/Examples/01-PublishSubscribe/Program.cs index aaf32ad..89c1f9b 100644 --- a/Examples/01-PublishSubscribe/Program.cs +++ b/Examples/01-PublishSubscribe/Program.cs @@ -13,8 +13,6 @@ using Tapeti.DataAnnotations; using Tapeti.Default; using Tapeti.Ninject; using Tapeti.SimpleInjector; -using Tapeti.UnityContainer; -using Unity; using Container = SimpleInjector.Container; // ReSharper disable UnusedMember.Global @@ -30,7 +28,6 @@ namespace _01_PublishSubscribe // or use your IoC container of choice: //var dependencyResolver = GetAutofacDependencyResolver(); //var dependencyResolver = GetCastleWindsorDependencyResolver(); - //var dependencyResolver = GetUnityDependencyResolver(); //var dependencyResolver = GetNinjectDependencyResolver(); // This helper is used because this example is not run as a service. You do not @@ -131,17 +128,6 @@ namespace _01_PublishSubscribe } - internal static IDependencyContainer GetUnityDependencyResolver() - { - var container = new UnityContainer(); - - container.RegisterType(); - container.RegisterType(); - - return new UnityDependencyResolver(container); - } - - internal static IDependencyContainer GetNinjectDependencyResolver() { var kernel = new StandardKernel(); diff --git a/Examples/ExampleLib/ExampleConsoleApp.cs b/Examples/ExampleLib/ExampleConsoleApp.cs index feabdec..94aa865 100644 --- a/Examples/ExampleLib/ExampleConsoleApp.cs +++ b/Examples/ExampleLib/ExampleConsoleApp.cs @@ -79,7 +79,7 @@ namespace ExampleLib { while (true) { - if (!(e is AggregateException aggregateException)) + if (e is not AggregateException aggregateException) return e; if (aggregateException.InnerExceptions.Count != 1) diff --git a/Examples/Messaging.TapetiExample/Messaging.TapetiExample.csproj b/Examples/Messaging.TapetiExample/Messaging.TapetiExample.csproj index 13e2b2e..43a8b0c 100644 --- a/Examples/Messaging.TapetiExample/Messaging.TapetiExample.csproj +++ b/Examples/Messaging.TapetiExample/Messaging.TapetiExample.csproj @@ -6,7 +6,7 @@ - + diff --git a/Tapeti.Autofac/AutofacDependencyResolver.cs b/Tapeti.Autofac/AutofacDependencyResolver.cs index 74ac975..9346674 100644 --- a/Tapeti.Autofac/AutofacDependencyResolver.cs +++ b/Tapeti.Autofac/AutofacDependencyResolver.cs @@ -2,9 +2,10 @@ using Autofac; using Autofac.Builder; +// ReSharper disable UnusedMember.Global + namespace Tapeti.Autofac { - /// /// /// Dependency resolver and container implementation for Autofac. /// Since this class needs access to both the ContainerBuilder and the built IContainer, @@ -83,7 +84,7 @@ namespace Tapeti.Autofac { CheckContainerBuilder(); containerBuilder - .Register(context => factory()) + .Register(_ => factory()) .As() .PreserveExistingDefaults(); } @@ -116,7 +117,7 @@ namespace Tapeti.Autofac { CheckContainerBuilder(); containerBuilder - .Register(context => factory()) + .Register(_ => factory()) .As() .SingleInstance() .PreserveExistingDefaults(); diff --git a/Tapeti.Autofac/Tapeti.Autofac.csproj b/Tapeti.Autofac/Tapeti.Autofac.csproj index e0263c8..57996d9 100644 --- a/Tapeti.Autofac/Tapeti.Autofac.csproj +++ b/Tapeti.Autofac/Tapeti.Autofac.csproj @@ -15,7 +15,7 @@ - + diff --git a/Tapeti.CastleWindsor/Tapeti.CastleWindsor.csproj b/Tapeti.CastleWindsor/Tapeti.CastleWindsor.csproj index 9df8c90..21b5dfb 100644 --- a/Tapeti.CastleWindsor/Tapeti.CastleWindsor.csproj +++ b/Tapeti.CastleWindsor/Tapeti.CastleWindsor.csproj @@ -15,7 +15,7 @@ - + diff --git a/Tapeti.CastleWindsor/WindsorDependencyResolver.cs b/Tapeti.CastleWindsor/WindsorDependencyResolver.cs index e398157..95d5f5e 100644 --- a/Tapeti.CastleWindsor/WindsorDependencyResolver.cs +++ b/Tapeti.CastleWindsor/WindsorDependencyResolver.cs @@ -4,7 +4,6 @@ using Castle.Windsor; namespace Tapeti.CastleWindsor { - /// /// /// Dependency resolver and container implementation for Castle Windsor. /// diff --git a/Tapeti.DataAnnotations/DataAnnotationsExtension.cs b/Tapeti.DataAnnotations/DataAnnotationsExtension.cs index abdbc5c..11ac2b3 100644 --- a/Tapeti.DataAnnotations/DataAnnotationsExtension.cs +++ b/Tapeti.DataAnnotations/DataAnnotationsExtension.cs @@ -3,7 +3,6 @@ using Tapeti.Config; namespace Tapeti.DataAnnotations { - /// /// /// Provides the DataAnnotations validation middleware. /// diff --git a/Tapeti.DataAnnotations/DataAnnotationsMessageMiddleware.cs b/Tapeti.DataAnnotations/DataAnnotationsMessageMiddleware.cs index d2df50a..6b3a723 100644 --- a/Tapeti.DataAnnotations/DataAnnotationsMessageMiddleware.cs +++ b/Tapeti.DataAnnotations/DataAnnotationsMessageMiddleware.cs @@ -5,7 +5,6 @@ using Tapeti.Config; namespace Tapeti.DataAnnotations { - /// /// /// Validates consumed messages using System.ComponentModel.DataAnnotations /// diff --git a/Tapeti.DataAnnotations/DataAnnotationsPublishMiddleware.cs b/Tapeti.DataAnnotations/DataAnnotationsPublishMiddleware.cs index 7908cda..025d056 100644 --- a/Tapeti.DataAnnotations/DataAnnotationsPublishMiddleware.cs +++ b/Tapeti.DataAnnotations/DataAnnotationsPublishMiddleware.cs @@ -5,7 +5,6 @@ using Tapeti.Config; namespace Tapeti.DataAnnotations { - /// /// /// Validates published messages using System.ComponentModel.DataAnnotations /// diff --git a/Tapeti.Flow.SQL/SqlConnectionFlowRepository.cs b/Tapeti.Flow.SQL/SqlConnectionFlowRepository.cs index bdf3979..afe5611 100644 --- a/Tapeti.Flow.SQL/SqlConnectionFlowRepository.cs +++ b/Tapeti.Flow.SQL/SqlConnectionFlowRepository.cs @@ -11,7 +11,6 @@ using Newtonsoft.Json; namespace Tapeti.Flow.SQL { - /// /// /// IFlowRepository implementation for SQL server. /// diff --git a/Tapeti.Flow.SQL/SqlExceptionHelper.cs b/Tapeti.Flow.SQL/SqlExceptionHelper.cs index e4d507e..5237a6e 100644 --- a/Tapeti.Flow.SQL/SqlExceptionHelper.cs +++ b/Tapeti.Flow.SQL/SqlExceptionHelper.cs @@ -13,7 +13,7 @@ namespace Tapeti.Flow.SQL // 2627: Violation of %ls constraint '%.*ls'. Cannot insert duplicate key in object '%.*ls'. The duplicate key value is %ls. public static bool IsDuplicateKey(SqlException e) { - return e != null && (e.Number == 2601 || e.Number == 2627); + return e is { Number: 2601 or 2627 }; } @@ -21,12 +21,12 @@ namespace Tapeti.Flow.SQL { switch (e) { - case TimeoutException _: + case TimeoutException: return true; - case Exception exception: + case not null: { - var sqlExceptions = ExtractSqlExceptions(exception); + var sqlExceptions = ExtractSqlExceptions(e); return sqlExceptions.Select(UnwrapSqlErrors).Any(IsRecoverableSQLError); } diff --git a/Tapeti.Flow/Annotations/ContinuationAttribute.cs b/Tapeti.Flow/Annotations/ContinuationAttribute.cs index 2612a30..e1fa2c3 100644 --- a/Tapeti.Flow/Annotations/ContinuationAttribute.cs +++ b/Tapeti.Flow/Annotations/ContinuationAttribute.cs @@ -2,7 +2,6 @@ namespace Tapeti.Flow.Annotations { - /// /// /// Marks a message handler as a response message handler which continues a Tapeti Flow. /// The method only receives direct messages, and does not create a routing key based binding to the queue. diff --git a/Tapeti.Flow/Annotations/StartAttribute.cs b/Tapeti.Flow/Annotations/StartAttribute.cs index 8c1fd2e..d43c274 100644 --- a/Tapeti.Flow/Annotations/StartAttribute.cs +++ b/Tapeti.Flow/Annotations/StartAttribute.cs @@ -3,7 +3,6 @@ using JetBrains.Annotations; namespace Tapeti.Flow.Annotations { - /// /// /// Marks this method as the start of a Tapeti Flow. Use IFlowStarter.Start to begin a new flow and /// call this method. Must return an IYieldPoint. diff --git a/Tapeti.Flow/ConfigExtensions.cs b/Tapeti.Flow/ConfigExtensions.cs index 474da6a..5bc973d 100644 --- a/Tapeti.Flow/ConfigExtensions.cs +++ b/Tapeti.Flow/ConfigExtensions.cs @@ -1,5 +1,7 @@ using Tapeti.Config; +// ReSharper disable UnusedMember.Global + namespace Tapeti.Flow { /// diff --git a/Tapeti.Flow/Default/FlowBindingMiddleware.cs b/Tapeti.Flow/Default/FlowBindingMiddleware.cs index 877f890..1cb9d6d 100644 --- a/Tapeti.Flow/Default/FlowBindingMiddleware.cs +++ b/Tapeti.Flow/Default/FlowBindingMiddleware.cs @@ -60,7 +60,7 @@ namespace Tapeti.Flow.Default } else if (context.Result.Info.ParameterType == typeof(void)) { - context.Result.SetHandler((messageContext, value) => HandleParallelResponse(messageContext)); + context.Result.SetHandler((messageContext, _) => HandleParallelResponse(messageContext)); } else throw new ArgumentException($"Result type must be IYieldPoint, Task or void in controller {context.Method.DeclaringType?.FullName}, method {context.Method.Name}"); diff --git a/Tapeti.Flow/Default/FlowHandlerContext.cs b/Tapeti.Flow/Default/FlowHandlerContext.cs index c1e8c38..c533c4b 100644 --- a/Tapeti.Flow/Default/FlowHandlerContext.cs +++ b/Tapeti.Flow/Default/FlowHandlerContext.cs @@ -3,7 +3,6 @@ using Tapeti.Config; namespace Tapeti.Flow.Default { - /// /// /// Default implementation for IFlowHandlerContext /// diff --git a/Tapeti.Flow/Default/FlowProvider.cs b/Tapeti.Flow/Default/FlowProvider.cs index 3c8c515..eb99d18 100644 --- a/Tapeti.Flow/Default/FlowProvider.cs +++ b/Tapeti.Flow/Default/FlowProvider.cs @@ -206,7 +206,7 @@ namespace Tapeti.Flow.Default /// public async ValueTask Execute(IFlowHandlerContext context, IYieldPoint yieldPoint) { - if (!(yieldPoint is DelegateYieldPoint executableYieldPoint)) + if (yieldPoint is not DelegateYieldPoint executableYieldPoint) throw new YieldPointException($"Yield point is required in controller {context.Controller.GetType().Name} for method {context.Method.Name}"); FlowContext flowContext = null; @@ -297,8 +297,8 @@ namespace Tapeti.Flow.Default { private class RequestInfo { - public object Message { get; set; } - public ResponseHandlerInfo ResponseHandlerInfo { get; set; } + public object Message { get; init; } + public ResponseHandlerInfo ResponseHandlerInfo { get; init; } } @@ -372,21 +372,13 @@ namespace Tapeti.Flow.Default { if (requests.Count == 0) { - switch (noRequestsBehaviour) + return noRequestsBehaviour switch { - case FlowNoRequestsBehaviour.Exception: - throw new YieldPointException("At least one request must be added before yielding a parallel request"); - - case FlowNoRequestsBehaviour.Converge: - return new DelegateYieldPoint(context => - flowProvider.Converge(context, convergeMethod.Method.Name, convergeMethodSync)); - - case FlowNoRequestsBehaviour.EndFlow: - return new DelegateYieldPoint(EndFlow); - - default: - throw new ArgumentOutOfRangeException(nameof(noRequestsBehaviour), noRequestsBehaviour, null); - } + FlowNoRequestsBehaviour.Exception => throw new YieldPointException("At least one request must be added before yielding a parallel request"), + FlowNoRequestsBehaviour.Converge => new DelegateYieldPoint(context => flowProvider.Converge(context, convergeMethod.Method.Name, convergeMethodSync)), + FlowNoRequestsBehaviour.EndFlow => new DelegateYieldPoint(EndFlow), + _ => throw new ArgumentOutOfRangeException(nameof(noRequestsBehaviour), noRequestsBehaviour, null) + }; } if (convergeMethod?.Method == null) diff --git a/Tapeti.Flow/Default/FlowStarter.cs b/Tapeti.Flow/Default/FlowStarter.cs index f391fe8..238947e 100644 --- a/Tapeti.Flow/Default/FlowStarter.cs +++ b/Tapeti.Flow/Default/FlowStarter.cs @@ -6,7 +6,6 @@ using Tapeti.Config; namespace Tapeti.Flow.Default { - /// /// /// Default implementation for IFlowStarter. /// diff --git a/Tapeti.Flow/Default/FlowState.cs b/Tapeti.Flow/Default/FlowState.cs index eb7961d..a6ce743 100644 --- a/Tapeti.Flow/Default/FlowState.cs +++ b/Tapeti.Flow/Default/FlowState.cs @@ -18,7 +18,7 @@ namespace Tapeti.Flow.Default /// public FlowMetadata Metadata { - get => metadata ?? (metadata = new FlowMetadata()); + get => metadata ??= new FlowMetadata(); set => metadata = value; } @@ -34,7 +34,7 @@ namespace Tapeti.Flow.Default /// public Dictionary Continuations { - get => continuations ?? (continuations = new Dictionary()); + get => continuations ??= new Dictionary(); set => continuations = value; } diff --git a/Tapeti.Flow/Default/FlowStore.cs b/Tapeti.Flow/Default/FlowStore.cs index 99fe5f0..324f121 100644 --- a/Tapeti.Flow/Default/FlowStore.cs +++ b/Tapeti.Flow/Default/FlowStore.cs @@ -9,7 +9,6 @@ using Tapeti.Flow.FlowHelpers; namespace Tapeti.Flow.Default { - /// /// /// Default implementation of IFlowStore. /// @@ -119,7 +118,7 @@ namespace Tapeti.Flow.Default if (!loaded) throw new InvalidOperationException("Flow store is not yet loaded."); - return new ValueTask(continuationLookup.TryGetValue(continuationID, out var result) ? result : (Guid?)null); + return new ValueTask(continuationLookup.TryGetValue(continuationID, out var result) ? result : null); } diff --git a/Tapeti.Flow/Default/NonPersistentFlowRepository.cs b/Tapeti.Flow/Default/NonPersistentFlowRepository.cs index bbaa40d..0ac6ecc 100644 --- a/Tapeti.Flow/Default/NonPersistentFlowRepository.cs +++ b/Tapeti.Flow/Default/NonPersistentFlowRepository.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; namespace Tapeti.Flow.Default { - /// /// /// Default implementation for IFlowRepository. Does not persist any state, relying on the FlowStore's cache instead. /// diff --git a/Tapeti.Flow/FlowExtension.cs b/Tapeti.Flow/FlowExtension.cs index 6c979e9..cc28e1e 100644 --- a/Tapeti.Flow/FlowExtension.cs +++ b/Tapeti.Flow/FlowExtension.cs @@ -4,7 +4,6 @@ using Tapeti.Flow.Default; namespace Tapeti.Flow { - /// /// /// Provides the Flow middleware. /// diff --git a/Tapeti.Flow/FlowHelpers/MethodSerializer.cs b/Tapeti.Flow/FlowHelpers/MethodSerializer.cs index 229ad65..c11c6eb 100644 --- a/Tapeti.Flow/FlowHelpers/MethodSerializer.cs +++ b/Tapeti.Flow/FlowHelpers/MethodSerializer.cs @@ -35,8 +35,6 @@ namespace Tapeti.Flow.FlowHelpers try { assembly = Assembly.Load(match.Groups["assembly"].Value); - if (assembly == null) - return null; } catch { diff --git a/Tapeti.Flow/IFlowHandlerContext.cs b/Tapeti.Flow/IFlowHandlerContext.cs index 921dd4e..0b99556 100644 --- a/Tapeti.Flow/IFlowHandlerContext.cs +++ b/Tapeti.Flow/IFlowHandlerContext.cs @@ -4,7 +4,6 @@ using Tapeti.Config; namespace Tapeti.Flow { - /// /// /// Provides information about the handler for the flow. /// diff --git a/Tapeti.Flow/IFlowStore.cs b/Tapeti.Flow/IFlowStore.cs index 9766e5b..38dc328 100644 --- a/Tapeti.Flow/IFlowStore.cs +++ b/Tapeti.Flow/IFlowStore.cs @@ -42,7 +42,6 @@ namespace Tapeti.Flow } - /// /// /// Represents a lock on the flow state, to provide thread safety. /// diff --git a/Tapeti.Flow/ResponseExpectedException.cs b/Tapeti.Flow/ResponseExpectedException.cs index 07d66b1..8c0d225 100644 --- a/Tapeti.Flow/ResponseExpectedException.cs +++ b/Tapeti.Flow/ResponseExpectedException.cs @@ -2,7 +2,6 @@ namespace Tapeti.Flow { - /// /// /// Raised when a response is expected to end a flow, but none was provided. /// diff --git a/Tapeti.Flow/Tapeti.Flow.csproj b/Tapeti.Flow/Tapeti.Flow.csproj index d991a6c..6fc172c 100644 --- a/Tapeti.Flow/Tapeti.Flow.csproj +++ b/Tapeti.Flow/Tapeti.Flow.csproj @@ -36,6 +36,6 @@ - + diff --git a/Tapeti.Flow/YieldPointException.cs b/Tapeti.Flow/YieldPointException.cs index 2d135d0..a0eed51 100644 --- a/Tapeti.Flow/YieldPointException.cs +++ b/Tapeti.Flow/YieldPointException.cs @@ -2,7 +2,6 @@ namespace Tapeti.Flow { - /// /// /// Raised when an invalid yield point is returned. /// diff --git a/Tapeti.Ninject/NinjectDependencyResolver.cs b/Tapeti.Ninject/NinjectDependencyResolver.cs index 0e2fdcf..4f2b791 100644 --- a/Tapeti.Ninject/NinjectDependencyResolver.cs +++ b/Tapeti.Ninject/NinjectDependencyResolver.cs @@ -4,7 +4,6 @@ using Ninject; namespace Tapeti.Ninject { - /// /// /// Dependency resolver and container implementation for Ninject. /// @@ -49,7 +48,7 @@ namespace Tapeti.Ninject if (kernel.GetBindings(typeof(TService)).Any()) return; - kernel.Bind().ToMethod(context => factory()); + kernel.Bind().ToMethod(_ => factory()); } @@ -77,7 +76,7 @@ namespace Tapeti.Ninject if (kernel.GetBindings(typeof(TService)).Any()) return; - kernel.Bind().ToMethod(context => factory()).InSingletonScope(); + kernel.Bind().ToMethod(_ => factory()).InSingletonScope(); } diff --git a/Tapeti.Ninject/Tapeti.Ninject.csproj b/Tapeti.Ninject/Tapeti.Ninject.csproj index 2010385..84d56eb 100644 --- a/Tapeti.Ninject/Tapeti.Ninject.csproj +++ b/Tapeti.Ninject/Tapeti.Ninject.csproj @@ -15,7 +15,7 @@ - + diff --git a/Tapeti.Serilog/Tapeti.Serilog.csproj b/Tapeti.Serilog/Tapeti.Serilog.csproj index 830f755..2b520d3 100644 --- a/Tapeti.Serilog/Tapeti.Serilog.csproj +++ b/Tapeti.Serilog/Tapeti.Serilog.csproj @@ -19,7 +19,7 @@ - + diff --git a/Tapeti.Serilog/TapetiSeriLogger.cs b/Tapeti.Serilog/TapetiSeriLogger.cs index f13afbd..af3de98 100644 --- a/Tapeti.Serilog/TapetiSeriLogger.cs +++ b/Tapeti.Serilog/TapetiSeriLogger.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text; using Tapeti.Config; +using Tapeti.Connection; using ISerilogLogger = Serilog.ILogger; // ReSharper disable UnusedMember.Global @@ -129,7 +130,7 @@ namespace Tapeti.Serilog } /// - public void QueueExistsWarning(string queueName, IReadOnlyDictionary existingArguments, IReadOnlyDictionary arguments) + public void QueueExistsWarning(string queueName, IRabbitMQArguments existingArguments, IRabbitMQArguments arguments) { seriLogger.Warning("Tapeti: durable queue {queueName} exists with incompatible x-arguments ({existingArguments} vs. {arguments}) and will not be redeclared, queue will be consumed as-is", queueName, diff --git a/Tapeti.SimpleInjector/SimpleInjectorDependencyResolver.cs b/Tapeti.SimpleInjector/SimpleInjectorDependencyResolver.cs index c8a189f..0c96418 100644 --- a/Tapeti.SimpleInjector/SimpleInjectorDependencyResolver.cs +++ b/Tapeti.SimpleInjector/SimpleInjectorDependencyResolver.cs @@ -4,7 +4,6 @@ using SimpleInjector; namespace Tapeti.SimpleInjector { - /// /// /// Dependency resolver and container implementation for SimpleInjector. /// diff --git a/Tapeti.SimpleInjector/Tapeti.SimpleInjector.csproj b/Tapeti.SimpleInjector/Tapeti.SimpleInjector.csproj index d1e9d4f..c60b753 100644 --- a/Tapeti.SimpleInjector/Tapeti.SimpleInjector.csproj +++ b/Tapeti.SimpleInjector/Tapeti.SimpleInjector.csproj @@ -19,7 +19,7 @@ - + diff --git a/Tapeti.Tests/Client/TapetiClientTests.cs b/Tapeti.Tests/Client/TapetiClientTests.cs index 0bebbd7..c9157ba 100644 --- a/Tapeti.Tests/Client/TapetiClientTests.cs +++ b/Tapeti.Tests/Client/TapetiClientTests.cs @@ -1,9 +1,12 @@ // Do not include in the Release build for AppVeyor due to the Docker requirement #if DEBUG +using System.Text; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Tapeti.Connection; +using Tapeti.Default; +using Tapeti.Exceptions; using Tapeti.Tests.Mock; using Xunit; using Xunit.Abstractions; @@ -11,11 +14,14 @@ using Xunit.Abstractions; namespace Tapeti.Tests.Client { [Collection(RabbitMQCollection.Name)] - public class TapetiClientTests + public class TapetiClientTests : IAsyncLifetime { private readonly RabbitMQFixture fixture; private readonly MockDependencyResolver dependencyResolver = new(); + private TapetiClient client; + + public TapetiClientTests(RabbitMQFixture fixture, ITestOutputHelper testOutputHelper) { this.fixture = fixture; @@ -24,6 +30,22 @@ namespace Tapeti.Tests.Client } + public Task InitializeAsync() + { + client = CreateClient(); + + return Task.CompletedTask; + } + + + public async Task DisposeAsync() + { + await client.Close(); + client = null; + } + + + [Fact] public void Fixture() { @@ -35,31 +57,50 @@ namespace Tapeti.Tests.Client [Fact] public async Task DynamicQueueDeclareNoPrefix() { - var client = CreateCilent(); - var queueName = await client.DynamicQueueDeclare(null, null, CancellationToken.None); queueName.Should().NotBeNullOrEmpty(); - - await client.Close(); } [Fact] public async Task DynamicQueueDeclarePrefix() { - var client = CreateCilent(); - var queueName = await client.DynamicQueueDeclare("dynamicprefix", null, CancellationToken.None); queueName.Should().StartWith("dynamicprefix"); + } - await client.Close(); + + [Fact] + public async Task PublishHandleOverflow() + { + var queue1 = await client.DynamicQueueDeclare(null, new RabbitMQArguments + { + { "x-max-length", 5 }, + { "x-overflow", "reject-publish" } + }, CancellationToken.None); + + var queue2 = await client.DynamicQueueDeclare(null, null, CancellationToken.None); + + var body = Encoding.UTF8.GetBytes("Hello world!"); + var properties = new MessageProperties(); + + + for (var i = 0; i < 5; i++) + await client.Publish(body, properties, null, queue1, true); + + + var publishOverMaxLength = () => client.Publish(body, properties, null, queue1, true); + await publishOverMaxLength.Should().ThrowAsync(); + + // The channel should recover and allow further publishing + await client.Publish(body, properties, null, queue2, true); } // TODO test the other methods - private TapetiClient CreateCilent() + private TapetiClient CreateClient() { return new TapetiClient( new TapetiConfig.Config(dependencyResolver), diff --git a/Tapeti.Tests/Config/QueueArgumentsTest.cs b/Tapeti.Tests/Config/QueueArgumentsTest.cs index a43e5bb..ebb8cbc 100644 --- a/Tapeti.Tests/Config/QueueArgumentsTest.cs +++ b/Tapeti.Tests/Config/QueueArgumentsTest.cs @@ -18,7 +18,7 @@ namespace Tapeti.Tests.Config private static readonly MockRepository MoqRepository = new(MockBehavior.Strict); private readonly Mock client; - private readonly Dictionary> declaredQueues = new(); + private readonly Dictionary declaredQueues = new(); public QueueArgumentsTest() @@ -45,8 +45,8 @@ namespace Tapeti.Tests.Config var queue = 0; client - .Setup(c => c.DynamicQueueDeclare(null, It.IsAny>(), It.IsAny())) - .Callback((string _, IReadOnlyDictionary arguments, CancellationToken _) => + .Setup(c => c.DynamicQueueDeclare(null, It.IsAny(), It.IsAny())) + .Callback((string _, IRabbitMQArguments arguments, CancellationToken _) => { queue++; declaredQueues.Add($"queue-{queue}", arguments); @@ -54,8 +54,8 @@ namespace Tapeti.Tests.Config .ReturnsAsync(() => $"queue-{queue}"); client - .Setup(c => c.DurableQueueDeclare(It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny())) - .Callback((string queueName, IEnumerable _, IReadOnlyDictionary arguments, CancellationToken _) => + .Setup(c => c.DurableQueueDeclare(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) + .Callback((string queueName, IEnumerable _, IRabbitMQArguments arguments, CancellationToken _) => { declaredQueues.Add(queueName, arguments); }) @@ -89,10 +89,10 @@ namespace Tapeti.Tests.Config var arguments = declaredQueues["queue-1"]; arguments.Should().ContainKey("x-custom").WhoseValue.Should().Be("custom value"); - arguments.Should().ContainKey("x-another").WhoseValue.Should().Be("another value"); - arguments.Should().ContainKey("x-max-length").WhoseValue.Should().Be("100"); - arguments.Should().ContainKey("x-max-length-bytes").WhoseValue.Should().Be("100000"); - arguments.Should().ContainKey("x-message-ttl").WhoseValue.Should().Be("4269"); + arguments.Should().ContainKey("x-another").WhoseValue.Should().Be(true); + arguments.Should().ContainKey("x-max-length").WhoseValue.Should().Be(100); + arguments.Should().ContainKey("x-max-length-bytes").WhoseValue.Should().Be(100000); + arguments.Should().ContainKey("x-message-ttl").WhoseValue.Should().Be(4269); arguments.Should().ContainKey("x-overflow").WhoseValue.Should().Be("reject-publish"); } @@ -108,10 +108,10 @@ namespace Tapeti.Tests.Config declaredQueues.Should().HaveCount(2); var arguments1 = declaredQueues["queue-1"]; - arguments1.Should().ContainKey("x-max-length").WhoseValue.Should().Be("100"); + arguments1.Should().ContainKey("x-max-length").WhoseValue.Should().Be(100); var arguments2 = declaredQueues["queue-2"]; - arguments2.Should().ContainKey("x-max-length-bytes").WhoseValue.Should().Be("100000"); + arguments2.Should().ContainKey("x-max-length-bytes").WhoseValue.Should().Be(100000); } @@ -148,7 +148,7 @@ namespace Tapeti.Tests.Config [DynamicQueue] - [QueueArguments("x-custom", "custom value", "x-another", "another value", MaxLength = 100, MaxLengthBytes = 100000, MessageTTL = 4269, Overflow = RabbitMQOverflow.RejectPublish)] + [QueueArguments("x-custom", "custom value", "x-another", true, MaxLength = 100, MaxLengthBytes = 100000, MessageTTL = 4269, Overflow = RabbitMQOverflow.RejectPublish)] private class TestController { public void HandleMessage1(TestMessage1 message) diff --git a/Tapeti.Tests/Mock/MockLogger.cs b/Tapeti.Tests/Mock/MockLogger.cs index a2d2387..19c41fb 100644 --- a/Tapeti.Tests/Mock/MockLogger.cs +++ b/Tapeti.Tests/Mock/MockLogger.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; using System.Text; using Tapeti.Config; +using Tapeti.Connection; using Xunit.Abstractions; namespace Tapeti.Tests.Mock @@ -49,7 +49,7 @@ namespace Tapeti.Tests.Mock : $"Declaring {(durable ? "durable" : "dynamic")} queue {queueName}"); } - public void QueueExistsWarning(string queueName, IReadOnlyDictionary existingArguments, IReadOnlyDictionary arguments) + public void QueueExistsWarning(string queueName, IRabbitMQArguments existingArguments, IRabbitMQArguments arguments) { var argumentsText = new StringBuilder(); foreach (var pair in arguments) diff --git a/Tapeti.Transient/ConfigExtensions.cs b/Tapeti.Transient/ConfigExtensions.cs index aba7641..43a069c 100644 --- a/Tapeti.Transient/ConfigExtensions.cs +++ b/Tapeti.Transient/ConfigExtensions.cs @@ -1,6 +1,8 @@ using System; using Tapeti.Config; +// ReSharper disable UnusedMember.Global + namespace Tapeti.Transient { /// diff --git a/Tapeti.Transient/TransientGenericBinding.cs b/Tapeti.Transient/TransientGenericBinding.cs index a514466..0617c62 100644 --- a/Tapeti.Transient/TransientGenericBinding.cs +++ b/Tapeti.Transient/TransientGenericBinding.cs @@ -4,7 +4,6 @@ using Tapeti.Config; namespace Tapeti.Transient { - /// /// /// Implements a binding for transient request response messages. /// Register this binding using the WithTransient config extension method. diff --git a/Tapeti.Transient/TransientPublisher.cs b/Tapeti.Transient/TransientPublisher.cs index 3092c86..fb1f713 100644 --- a/Tapeti.Transient/TransientPublisher.cs +++ b/Tapeti.Transient/TransientPublisher.cs @@ -2,7 +2,6 @@ namespace Tapeti.Transient { - /// /// /// Default implementation of ITransientPublisher /// diff --git a/Tapeti.Transient/TransientRouter.cs b/Tapeti.Transient/TransientRouter.cs index 7c0e94d..96c27af 100644 --- a/Tapeti.Transient/TransientRouter.cs +++ b/Tapeti.Transient/TransientRouter.cs @@ -55,7 +55,7 @@ namespace Tapeti.Transient public async Task RequestResponse(IPublisher publisher, object request) { var correlation = Guid.NewGuid(); - var tcs = map.GetOrAdd(correlation, c => new TaskCompletionSource()); + var tcs = map.GetOrAdd(correlation, _ => new TaskCompletionSource()); try { @@ -77,7 +77,7 @@ namespace Tapeti.Transient throw; } - using (new Timer(TimeoutResponse, tcs, defaultTimeoutMs, -1)) + await using (new Timer(TimeoutResponse, tcs, defaultTimeoutMs, -1)) { return await tcs.Task; } diff --git a/Tapeti.UnityContainer/Tapeti.UnityContainer.csproj b/Tapeti.UnityContainer/Tapeti.UnityContainer.csproj deleted file mode 100644 index 41ceb50..0000000 --- a/Tapeti.UnityContainer/Tapeti.UnityContainer.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - net6.0;net7.0 - true - Mark van Renswoude - - Unity container integration package for Tapeti - rabbitmq tapeti unity - Unlicense - https://github.com/MvRens/Tapeti - Tapeti.SimpleInjector.png - 2.0.0 - 9 - - - - - - - - - - - - - True - - - - - - - - diff --git a/Tapeti.UnityContainer/UnityDependencyResolver.cs b/Tapeti.UnityContainer/UnityDependencyResolver.cs deleted file mode 100644 index a3ab84e..0000000 --- a/Tapeti.UnityContainer/UnityDependencyResolver.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using Unity; -using Unity.Lifetime; - -namespace Tapeti.UnityContainer -{ - /// - /// - /// Dependency resolver and container implementation for SimpleInjector. - /// - public class UnityDependencyResolver : IDependencyContainer - { - private readonly IUnityContainer container; - - - /// - /// - public UnityDependencyResolver(IUnityContainer container) - { - this.container = container; - } - - - /// - public T Resolve() where T : class - { - return container.Resolve(); - } - - /// - public object Resolve(Type type) - { - return container.Resolve(type); - } - - - /// - public void RegisterDefault() where TService : class where TImplementation : class, TService - { - if (container.IsRegistered(typeof(TService))) - return; - - container.RegisterType(); - } - - /// - public void RegisterDefault(Func factory) where TService : class - { - if (container.IsRegistered(typeof(TService))) - return; - - container.RegisterFactory(c => factory()); - } - - - /// - public void RegisterDefaultSingleton() where TService : class where TImplementation : class, TService - { - if (container.IsRegistered(typeof(TService))) - return; - - container.RegisterSingleton(); - } - - /// - public void RegisterDefaultSingleton(TService instance) where TService : class - { - if (container.IsRegistered(typeof(TService))) - return; - - container.RegisterInstance(instance); - } - - /// - public void RegisterDefaultSingleton(Func factory) where TService : class - { - if (container.IsRegistered(typeof(TService))) - return; - - container.RegisterFactory(c => factory(), new SingletonLifetimeManager()); - } - - - /// - public void RegisterController(Type type) - { - container.RegisterType(type); - } - } -} diff --git a/Tapeti.sln b/Tapeti.sln index d740886..d16ea52 100644 --- a/Tapeti.sln +++ b/Tapeti.sln @@ -45,8 +45,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tapeti.CastleWindsor", "Tap EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tapeti.Autofac", "Tapeti.Autofac\Tapeti.Autofac.csproj", "{B3802005-C941-41B6-A9A5-20573A7C24AE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tapeti.UnityContainer", "Tapeti.UnityContainer\Tapeti.UnityContainer.csproj", "{BA8CA9A2-BAFF-42BB-8439-3DD9D1F6C32E}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tapeti.Ninject", "Tapeti.Ninject\Tapeti.Ninject.csproj", "{29478B10-FC53-4E93-ADEF-A775D9408131}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "06-StatelessRequestResponse", "Examples\06-StatelessRequestResponse\06-StatelessRequestResponse.csproj", "{152227AA-3165-4550-8997-6EA80C84516E}" @@ -55,7 +53,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "07-ParallelizationTest", "E EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "08-MessageHandlerLogging", "Examples\08-MessageHandlerLogging\08-MessageHandlerLogging.csproj", "{906605A6-2CAB-4B29-B0DD-B735BF265E39}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tapeti.Benchmarks", "Tapeti.Benchmarks\Tapeti.Benchmarks.csproj", "{DBE56131-9207-4CEA-BA3E-031351677C48}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tapeti.Benchmarks", "Tapeti.Benchmarks\Tapeti.Benchmarks.csproj", "{DBE56131-9207-4CEA-BA3E-031351677C48}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -131,10 +129,6 @@ Global {B3802005-C941-41B6-A9A5-20573A7C24AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {B3802005-C941-41B6-A9A5-20573A7C24AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {B3802005-C941-41B6-A9A5-20573A7C24AE}.Release|Any CPU.Build.0 = Release|Any CPU - {BA8CA9A2-BAFF-42BB-8439-3DD9D1F6C32E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BA8CA9A2-BAFF-42BB-8439-3DD9D1F6C32E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BA8CA9A2-BAFF-42BB-8439-3DD9D1F6C32E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BA8CA9A2-BAFF-42BB-8439-3DD9D1F6C32E}.Release|Any CPU.Build.0 = Release|Any CPU {29478B10-FC53-4E93-ADEF-A775D9408131}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29478B10-FC53-4E93-ADEF-A775D9408131}.Debug|Any CPU.Build.0 = Debug|Any CPU {29478B10-FC53-4E93-ADEF-A775D9408131}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -176,7 +170,6 @@ Global {330D05CE-5321-4C7D-8017-2070B891289E} = {266B9B94-A4D2-41C2-860C-24A7C3B63B56} {374AAE64-598B-4F67-8870-4A05168FF987} = {99380F97-AD1A-459F-8AB3-D404E1E6AD4F} {B3802005-C941-41B6-A9A5-20573A7C24AE} = {99380F97-AD1A-459F-8AB3-D404E1E6AD4F} - {BA8CA9A2-BAFF-42BB-8439-3DD9D1F6C32E} = {99380F97-AD1A-459F-8AB3-D404E1E6AD4F} {29478B10-FC53-4E93-ADEF-A775D9408131} = {99380F97-AD1A-459F-8AB3-D404E1E6AD4F} {152227AA-3165-4550-8997-6EA80C84516E} = {266B9B94-A4D2-41C2-860C-24A7C3B63B56} {E69E6BA5-68E7-4A4D-A38C-B2526AA66E96} = {266B9B94-A4D2-41C2-860C-24A7C3B63B56} diff --git a/Tapeti.sln.DotSettings b/Tapeti.sln.DotSettings index 4a2b131..c44a322 100644 --- a/Tapeti.sln.DotSettings +++ b/Tapeti.sln.DotSettings @@ -4,7 +4,9 @@ ID JSON KV + MQ SQL + UTF <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> True True diff --git a/Tapeti/Config/ControllerMessageContextPayload.cs b/Tapeti/Config/ControllerMessageContextPayload.cs index 7666433..992b053 100644 --- a/Tapeti/Config/ControllerMessageContextPayload.cs +++ b/Tapeti/Config/ControllerMessageContextPayload.cs @@ -1,6 +1,5 @@ namespace Tapeti.Config { - /// /// /// Extends the message context with information about the controller. /// diff --git a/Tapeti/Config/IBinding.cs b/Tapeti/Config/IBinding.cs index 269fb08..d7fa843 100644 --- a/Tapeti/Config/IBinding.cs +++ b/Tapeti/Config/IBinding.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; +using Tapeti.Connection; namespace Tapeti.Config { @@ -82,7 +82,7 @@ namespace Tapeti.Config /// The message class to be bound to the queue /// The name of the durable queue /// Optional arguments - ValueTask BindDurable(Type messageClass, string queueName, IReadOnlyDictionary arguments); + ValueTask BindDurable(Type messageClass, string queueName, IRabbitMQArguments arguments); /// /// Binds the messageClass to a dynamic auto-delete queue. @@ -95,7 +95,7 @@ namespace Tapeti.Config /// An optional prefix for the dynamic queue's name. If not provided, RabbitMQ's default logic will be used to create an amq.gen queue. /// Optional arguments /// The generated name of the dynamic queue - ValueTask BindDynamic(Type messageClass, string queuePrefix, IReadOnlyDictionary arguments); + ValueTask BindDynamic(Type messageClass, string queuePrefix, IRabbitMQArguments arguments); /// /// Declares a durable queue but does not add a binding for a messageClass' routing key. @@ -103,7 +103,7 @@ namespace Tapeti.Config /// /// The name of the durable queue /// Optional arguments - ValueTask BindDurableDirect(string queueName, IReadOnlyDictionary arguments); + ValueTask BindDurableDirect(string queueName, IRabbitMQArguments arguments); /// /// Declares a dynamic queue but does not add a binding for a messageClass' routing key. @@ -113,7 +113,7 @@ namespace Tapeti.Config /// An optional prefix for the dynamic queue's name. If not provided, RabbitMQ's default logic will be used to create an amq.gen queue. /// Optional arguments /// The generated name of the dynamic queue - ValueTask BindDynamicDirect(Type messageClass, string queuePrefix, IReadOnlyDictionary arguments); + ValueTask BindDynamicDirect(Type messageClass, string queuePrefix, IRabbitMQArguments arguments); /// /// Declares a dynamic queue but does not add a binding for a messageClass' routing key. @@ -122,7 +122,7 @@ namespace Tapeti.Config /// An optional prefix for the dynamic queue's name. If not provided, RabbitMQ's default logic will be used to create an amq.gen queue. /// Optional arguments /// The generated name of the dynamic queue - ValueTask BindDynamicDirect(string queuePrefix, IReadOnlyDictionary arguments); + ValueTask BindDynamicDirect(string queuePrefix, IRabbitMQArguments arguments); /// /// Marks the specified durable queue as having an obsolete binding. If after all bindings have subscribed, the queue only contains obsolete diff --git a/Tapeti/Config/IControllerBindingMiddleware.cs b/Tapeti/Config/IControllerBindingMiddleware.cs index d88c951..cf878b2 100644 --- a/Tapeti/Config/IControllerBindingMiddleware.cs +++ b/Tapeti/Config/IControllerBindingMiddleware.cs @@ -2,7 +2,6 @@ namespace Tapeti.Config { - /// /// /// Called when a Controller method is registered. /// diff --git a/Tapeti/Config/IControllerFilterMiddleware.cs b/Tapeti/Config/IControllerFilterMiddleware.cs index a4e3a35..dc7be4e 100644 --- a/Tapeti/Config/IControllerFilterMiddleware.cs +++ b/Tapeti/Config/IControllerFilterMiddleware.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; namespace Tapeti.Config { - /// /// /// Denotes middleware that runs before the controller is instantiated. /// diff --git a/Tapeti/Config/IControllerMethodBinding.cs b/Tapeti/Config/IControllerMethodBinding.cs index 0fb4ce5..2d4c50f 100644 --- a/Tapeti/Config/IControllerMethodBinding.cs +++ b/Tapeti/Config/IControllerMethodBinding.cs @@ -3,7 +3,6 @@ using System.Reflection; namespace Tapeti.Config { - /// /// /// Represents a binding to a method in a controller class to handle incoming messages. /// diff --git a/Tapeti/Config/ITapetiConfig.cs b/Tapeti/Config/ITapetiConfig.cs index 519eeb2..2b14a26 100644 --- a/Tapeti/Config/ITapetiConfig.cs +++ b/Tapeti/Config/ITapetiConfig.cs @@ -77,7 +77,6 @@ namespace Tapeti.Config } - /// /// /// Contains a list of registered bindings, with a few added helpers. /// diff --git a/Tapeti/Config/ITapetiExtensionBinding.cs b/Tapeti/Config/ITapetiExtensionBinding.cs index 33b064e..cfcbc30 100644 --- a/Tapeti/Config/ITapetiExtensionBinding.cs +++ b/Tapeti/Config/ITapetiExtensionBinding.cs @@ -2,7 +2,6 @@ namespace Tapeti.Config { - /// /// /// Provides a way for Tapeti extensions to register custom bindings. /// diff --git a/Tapeti/Connection/ITapetiClient.cs b/Tapeti/Connection/ITapetiClient.cs index af1b560..d9df591 100644 --- a/Tapeti/Connection/ITapetiClient.cs +++ b/Tapeti/Connection/ITapetiClient.cs @@ -6,7 +6,6 @@ using Tapeti.Config; namespace Tapeti.Connection { - /// /// /// Defines a queue binding to an exchange using a routing key /// @@ -52,6 +51,18 @@ namespace Tapeti.Connection return ((Exchange != null ? Exchange.GetHashCode() : 0) * 397) ^ (RoutingKey != null ? RoutingKey.GetHashCode() : 0); } } + + /// + public static bool operator ==(QueueBinding left, QueueBinding right) + { + return left.Equals(right); + } + + /// + public static bool operator !=(QueueBinding left, QueueBinding right) + { + return !left.Equals(right); + } } @@ -93,7 +104,7 @@ namespace Tapeti.Connection /// A list of bindings. Any bindings already on the queue which are not in this list will be removed /// Optional arguments /// Cancelled when the connection is lost - Task DurableQueueDeclare(string queueName, IEnumerable bindings, IReadOnlyDictionary arguments, CancellationToken cancellationToken); + Task DurableQueueDeclare(string queueName, IEnumerable bindings, IRabbitMQArguments arguments, CancellationToken cancellationToken); /// /// Verifies a durable queue exists. Will raise an exception if it does not. @@ -101,7 +112,7 @@ namespace Tapeti.Connection /// The name of the queue to verify /// Optional arguments /// Cancelled when the connection is lost - Task DurableQueueVerify(string queueName, IReadOnlyDictionary arguments, CancellationToken cancellationToken); + Task DurableQueueVerify(string queueName, IRabbitMQArguments arguments, CancellationToken cancellationToken); /// /// Deletes a durable queue. @@ -117,7 +128,7 @@ namespace Tapeti.Connection /// An optional prefix for the dynamic queue's name. If not provided, RabbitMQ's default logic will be used to create an amq.gen queue. /// Optional arguments /// Cancelled when the connection is lost - Task DynamicQueueDeclare(string queuePrefix, IReadOnlyDictionary arguments, CancellationToken cancellationToken); + Task DynamicQueueDeclare(string queuePrefix, IRabbitMQArguments arguments, CancellationToken cancellationToken); /// /// Add a binding to a dynamic queue. diff --git a/Tapeti/Connection/RabbitMQArguments.cs b/Tapeti/Connection/RabbitMQArguments.cs new file mode 100644 index 0000000..5c0d7e9 --- /dev/null +++ b/Tapeti/Connection/RabbitMQArguments.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Text; + +namespace Tapeti.Connection +{ + /// + public interface IRabbitMQArguments : IReadOnlyDictionary + { + } + + + internal class RabbitMQArguments : Dictionary, IRabbitMQArguments + { + public RabbitMQArguments() + { + } + + #if NETSTANDARD2_1_OR_GREATER + public RabbitMQArguments(IReadOnlyDictionary values) : base(values) + { + } + #else + public RabbitMQArguments(IReadOnlyDictionary values) + { + foreach (var pair in values) + Add(pair.Key, pair.Value); + } + #endif + + + public void AddUTF8(string key, string value) + { + Add(key, Encoding.UTF8.GetBytes(value)); + } + } +} diff --git a/Tapeti/Connection/TapetiBasicConsumer.cs b/Tapeti/Connection/TapetiBasicConsumer.cs index b465ee2..5b04ba5 100644 --- a/Tapeti/Connection/TapetiBasicConsumer.cs +++ b/Tapeti/Connection/TapetiBasicConsumer.cs @@ -14,7 +14,6 @@ namespace Tapeti.Connection public delegate Task ResponseFunc(long expectedConnectionReference, ulong deliveryTag, ConsumeResult result); - /// /// /// Implements the bridge between the RabbitMQ Client consumer and a Tapeti Consumer /// diff --git a/Tapeti/Connection/TapetiClient.cs b/Tapeti/Connection/TapetiClient.cs index 4ff3251..aaa8c2a 100644 --- a/Tapeti/Connection/TapetiClient.cs +++ b/Tapeti/Connection/TapetiClient.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using RabbitMQ.Client; using RabbitMQ.Client.Events; using RabbitMQ.Client.Exceptions; @@ -24,7 +25,6 @@ namespace Tapeti.Connection } - /// /// /// Implementation of ITapetiClient for the RabbitMQ Client library /// @@ -185,15 +185,18 @@ namespace Tapeti.Connection var replyCode = publishResultTask.Result; - // There is no RabbitMQ.Client.Framing.Constants value for this "No route" reply code - // at the time of writing... - if (replyCode == 312) - throw new NoRouteException( - $"Mandatory message with exchange '{exchange}' and routing key '{routingKey}' does not have a route"); + switch (replyCode) + { + // There is no RabbitMQ.Client.Framing.Constants value for this "No route" reply code + // at the time of writing... + case 312: + throw new NoRouteException( + $"Mandatory message with exchange '{exchange}' and routing key '{routingKey}' does not have a route"); - if (replyCode > 0) - throw new NoRouteException( - $"Mandatory message with exchange '{exchange}' and routing key '{routingKey}' could not be delivered, reply code: {replyCode}"); + case > 0: + throw new NoRouteException( + $"Mandatory message with exchange '{exchange}' and routing key '{routingKey}' could not be delivered, reply code: {replyCode}"); + } }); } @@ -286,7 +289,7 @@ namespace Tapeti.Connection } - private async Task GetDurableQueueDeclareRequired(string queueName, IReadOnlyDictionary arguments) + private async Task GetDurableQueueDeclareRequired(string queueName, IRabbitMQArguments arguments) { var existingQueue = await GetQueueInfo(queueName); if (existingQueue == null) @@ -298,17 +301,44 @@ namespace Tapeti.Connection if (arguments == null && existingQueue.Arguments.Count == 0) return true; - if (existingQueue.Arguments.NullSafeSameValues(arguments)) + var existingArguments = ConvertJsonArguments(existingQueue.Arguments); + if (existingArguments.NullSafeSameValues(arguments)) return true; - (logger as IBindingLogger)?.QueueExistsWarning(queueName, existingQueue.Arguments, arguments); + (logger as IBindingLogger)?.QueueExistsWarning(queueName, existingArguments, arguments); return false; } + private static RabbitMQArguments ConvertJsonArguments(IReadOnlyDictionary arguments) + { + if (arguments == null) + return null; + + var result = new RabbitMQArguments(); + foreach (var pair in arguments) + { + // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault - by design + object value = pair.Value.Type switch + { + JTokenType.Integer => pair.Value.Value(), + JTokenType.Float => pair.Value.Value(), + JTokenType.String => Encoding.UTF8.GetBytes(pair.Value.Value() ?? string.Empty), + JTokenType.Boolean => pair.Value.Value(), + JTokenType.Null => null, + _ => throw new ArgumentOutOfRangeException(nameof(arguments)) + }; + + result.Add(pair.Key, value); + } + + return result; + } + + /// - public async Task DurableQueueDeclare(string queueName, IEnumerable bindings, IReadOnlyDictionary arguments, CancellationToken cancellationToken) + public async Task DurableQueueDeclare(string queueName, IEnumerable bindings, IRabbitMQArguments arguments, CancellationToken cancellationToken) { var declareRequired = await GetDurableQueueDeclareRequired(queueName, arguments); @@ -343,17 +373,16 @@ namespace Tapeti.Connection } - private static IDictionary GetDeclareArguments(IReadOnlyDictionary arguments) + private static IDictionary GetDeclareArguments(IRabbitMQArguments arguments) { - if (arguments == null || arguments.Count == 0) - return null; - - return arguments.ToDictionary(p => p.Key, p => (object)Encoding.UTF8.GetBytes(p.Value)); + return arguments == null || arguments.Count == 0 + ? null + : arguments.ToDictionary(p => p.Key, p => p.Value); } /// - public async Task DurableQueueVerify(string queueName, IReadOnlyDictionary arguments, CancellationToken cancellationToken) + public async Task DurableQueueVerify(string queueName, IRabbitMQArguments arguments, CancellationToken cancellationToken) { if (!await GetDurableQueueDeclareRequired(queueName, arguments)) return; @@ -455,7 +484,7 @@ namespace Tapeti.Connection /// - public async Task DynamicQueueDeclare(string queuePrefix, IReadOnlyDictionary arguments, CancellationToken cancellationToken) + public async Task DynamicQueueDeclare(string queuePrefix, IRabbitMQArguments arguments, CancellationToken cancellationToken) { string queueName = null; var bindingLogger = logger as IBindingLogger; @@ -564,7 +593,7 @@ namespace Tapeti.Connection public bool Exclusive { get; set; } [JsonProperty("arguments")] - public Dictionary Arguments { get; set; } + public Dictionary Arguments { get; set; } [JsonProperty("messages")] public uint Messages { get; set; } @@ -675,7 +704,7 @@ namespace Tapeti.Connection } catch (WebException e) { - if (!(e.Response is HttpWebResponse response)) + if (e.Response is not HttpWebResponse response) throw; if (!TransientStatusCodes.Contains(response.StatusCode)) diff --git a/Tapeti/Connection/TapetiConsumer.cs b/Tapeti/Connection/TapetiConsumer.cs index a010fcf..ceee5c6 100644 --- a/Tapeti/Connection/TapetiConsumer.cs +++ b/Tapeti/Connection/TapetiConsumer.cs @@ -9,7 +9,6 @@ using Tapeti.Helpers; namespace Tapeti.Connection { - /// /// /// Implements a RabbitMQ consumer to pass messages to the Tapeti middleware. /// @@ -172,8 +171,7 @@ namespace Tapeti.Connection return e switch { AggregateException aggregateException => aggregateException.InnerExceptions.Any(IgnoreExceptionDuringShutdown), - TaskCanceledException _ => true, - OperationCanceledException _ => true, + OperationCanceledException => true, _ => e.InnerException != null && IgnoreExceptionDuringShutdown(e.InnerException) }; } diff --git a/Tapeti/Connection/TapetiPublisher.cs b/Tapeti/Connection/TapetiPublisher.cs index c590528..f383b24 100644 --- a/Tapeti/Connection/TapetiPublisher.cs +++ b/Tapeti/Connection/TapetiPublisher.cs @@ -151,11 +151,11 @@ namespace Tapeti.Connection private class PublishContext : IPublishContext { - public ITapetiConfig Config { get; set; } + public ITapetiConfig Config { get; init; } public string Exchange { get; set; } - public string RoutingKey { get; set; } - public object Message { get; set; } - public IMessageProperties Properties { get; set; } + public string RoutingKey { get; init; } + public object Message { get; init; } + public IMessageProperties Properties { get; init; } } } } diff --git a/Tapeti/Connection/TapetiSubscriber.cs b/Tapeti/Connection/TapetiSubscriber.cs index 3b467f3..1e13cb9 100644 --- a/Tapeti/Connection/TapetiSubscriber.cs +++ b/Tapeti/Connection/TapetiSubscriber.cs @@ -72,14 +72,12 @@ namespace Tapeti.Connection /// public void Reconnect() { - CancellationToken cancellationToken; - initializeCancellationTokenSource?.Cancel(); initializeCancellationTokenSource = new CancellationTokenSource(); consumerTags.Clear(); - cancellationToken = initializeCancellationTokenSource.Token; + var cancellationToken = initializeCancellationTokenSource.Token; Task.Run(async () => { @@ -166,7 +164,7 @@ namespace Tapeti.Connection { public string QueueName; public List MessageClasses; - public IReadOnlyDictionary Arguments; + public IRabbitMQArguments Arguments; } private readonly Dictionary> dynamicQueues = new(); @@ -187,12 +185,12 @@ namespace Tapeti.Connection } - public abstract ValueTask BindDurable(Type messageClass, string queueName, IReadOnlyDictionary arguments); - public abstract ValueTask BindDurableDirect(string queueName, IReadOnlyDictionary arguments); + public abstract ValueTask BindDurable(Type messageClass, string queueName, IRabbitMQArguments arguments); + public abstract ValueTask BindDurableDirect(string queueName, IRabbitMQArguments arguments); public abstract ValueTask BindDurableObsolete(string queueName); - public async ValueTask BindDynamic(Type messageClass, string queuePrefix, IReadOnlyDictionary arguments) + public async ValueTask BindDynamic(Type messageClass, string queuePrefix, IRabbitMQArguments arguments) { var result = await DeclareDynamicQueue(messageClass, queuePrefix, arguments); if (!result.IsNewMessageClass) @@ -207,14 +205,14 @@ namespace Tapeti.Connection } - public async ValueTask BindDynamicDirect(Type messageClass, string queuePrefix, IReadOnlyDictionary arguments) + public async ValueTask BindDynamicDirect(Type messageClass, string queuePrefix, IRabbitMQArguments arguments) { var result = await DeclareDynamicQueue(messageClass, queuePrefix, arguments); return result.QueueName; } - public async ValueTask BindDynamicDirect(string queuePrefix, IReadOnlyDictionary arguments) + public async ValueTask BindDynamicDirect(string queuePrefix, IRabbitMQArguments arguments) { // If we don't know the routing key, always create a new queue to ensure there is no overlap. // Keep it out of the dynamicQueues dictionary, so it can't be re-used later on either. @@ -228,7 +226,7 @@ namespace Tapeti.Connection public bool IsNewMessageClass; } - private async Task DeclareDynamicQueue(Type messageClass, string queuePrefix, IReadOnlyDictionary arguments) + private async Task DeclareDynamicQueue(Type messageClass, string queuePrefix, IRabbitMQArguments arguments) { // Group by prefix var key = queuePrefix ?? ""; @@ -284,7 +282,7 @@ namespace Tapeti.Connection private struct DurableQueueInfo { public List MessageClasses; - public IReadOnlyDictionary Arguments; + public IRabbitMQArguments Arguments; } @@ -297,7 +295,7 @@ namespace Tapeti.Connection } - public override ValueTask BindDurable(Type messageClass, string queueName, IReadOnlyDictionary arguments) + public override ValueTask BindDurable(Type messageClass, string queueName, IRabbitMQArguments arguments) { // Collect the message classes per queue so we can determine afterwards // if any of the bindings currently set on the durable queue are no @@ -326,7 +324,7 @@ namespace Tapeti.Connection } - public override ValueTask BindDurableDirect(string queueName, IReadOnlyDictionary arguments) + public override ValueTask BindDurableDirect(string queueName, IRabbitMQArguments arguments) { if (!durableQueues.TryGetValue(queueName, out var durableQueueInfo)) { @@ -398,12 +396,12 @@ namespace Tapeti.Connection } - public override async ValueTask BindDurable(Type messageClass, string queueName, IReadOnlyDictionary arguments) + public override async ValueTask BindDurable(Type messageClass, string queueName, IRabbitMQArguments arguments) { await VerifyDurableQueue(queueName, arguments); } - public override async ValueTask BindDurableDirect(string queueName, IReadOnlyDictionary arguments) + public override async ValueTask BindDurableDirect(string queueName, IRabbitMQArguments arguments) { await VerifyDurableQueue(queueName, arguments); } @@ -414,7 +412,7 @@ namespace Tapeti.Connection } - private async Task VerifyDurableQueue(string queueName, IReadOnlyDictionary arguments) + private async Task VerifyDurableQueue(string queueName, IRabbitMQArguments arguments) { if (!durableQueues.Add(queueName)) return; @@ -431,12 +429,12 @@ namespace Tapeti.Connection } - public override ValueTask BindDurable(Type messageClass, string queueName, IReadOnlyDictionary arguments) + public override ValueTask BindDurable(Type messageClass, string queueName, IRabbitMQArguments arguments) { return default; } - public override ValueTask BindDurableDirect(string queueName, IReadOnlyDictionary arguments) + public override ValueTask BindDurableDirect(string queueName, IRabbitMQArguments arguments) { return default; } diff --git a/Tapeti/Default/CancellationTokenBinding.cs b/Tapeti/Default/CancellationTokenBinding.cs index 01c8a72..531ffaf 100644 --- a/Tapeti/Default/CancellationTokenBinding.cs +++ b/Tapeti/Default/CancellationTokenBinding.cs @@ -5,7 +5,6 @@ using Tapeti.Config; namespace Tapeti.Default { - /// /// /// Binds a parameter of type CancellationToken to a token which is cancelled when the RabbitMQ connection is closed. /// Similar to and very much inspired by ASP.NET's RequestAborted CancellationToken. diff --git a/Tapeti/Default/ConsoleLogger.cs b/Tapeti/Default/ConsoleLogger.cs index 8b6e955..ed7aa15 100644 --- a/Tapeti/Default/ConsoleLogger.cs +++ b/Tapeti/Default/ConsoleLogger.cs @@ -1,13 +1,12 @@ using System; -using System.Collections.Generic; using System.Text; using Tapeti.Config; +using Tapeti.Connection; // ReSharper disable UnusedMember.Global - public API namespace Tapeti.Default { - /// /// /// Default ILogger implementation for console applications. /// @@ -81,13 +80,13 @@ namespace Tapeti.Default } /// - public void QueueExistsWarning(string queueName, IReadOnlyDictionary existingArguments, IReadOnlyDictionary arguments) + public void QueueExistsWarning(string queueName, IRabbitMQArguments existingArguments, IRabbitMQArguments arguments) { Console.WriteLine($"[Tapeti] Durable queue {queueName} exists with incompatible x-arguments ({GetArgumentsText(existingArguments)} vs. {GetArgumentsText(arguments)}) and will not be redeclared, queue will be consumed as-is"); } - private static string GetArgumentsText(IReadOnlyDictionary arguments) + private static string GetArgumentsText(IRabbitMQArguments arguments) { var argumentsText = new StringBuilder(); foreach (var pair in arguments) diff --git a/Tapeti/Default/ControllerBindingContext.cs b/Tapeti/Default/ControllerBindingContext.cs index 57ee673..327cf6b 100644 --- a/Tapeti/Default/ControllerBindingContext.cs +++ b/Tapeti/Default/ControllerBindingContext.cs @@ -99,7 +99,6 @@ namespace Tapeti.Default } - /// /// /// Default implementation for IBindingParameter /// @@ -139,7 +138,6 @@ namespace Tapeti.Default } - /// /// /// Default implementation for IBindingResult /// diff --git a/Tapeti/Default/ControllerMethodBinding.cs b/Tapeti/Default/ControllerMethodBinding.cs index a2b0614..4d9ba9c 100644 --- a/Tapeti/Default/ControllerMethodBinding.cs +++ b/Tapeti/Default/ControllerMethodBinding.cs @@ -4,11 +4,11 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; using Tapeti.Config; +using Tapeti.Connection; using Tapeti.Helpers; namespace Tapeti.Default { - /// /// /// Binding implementation for controller methods. Do not instantiate this class yourself, /// instead use the ITapetiConfigBuilder RegisterController / RegisterAllControllers extension @@ -319,7 +319,7 @@ namespace Tapeti.Default /// /// Optional arguments (x-arguments) passed when declaring the queue. /// - public IReadOnlyDictionary QueueArguments { get; set; } + public IRabbitMQArguments QueueArguments { get; set; } /// /// Determines if the QueueInfo properties contain a valid combination. diff --git a/Tapeti/Default/DependencyResolverBinding.cs b/Tapeti/Default/DependencyResolverBinding.cs index 8eb3b9a..3f2c81e 100644 --- a/Tapeti/Default/DependencyResolverBinding.cs +++ b/Tapeti/Default/DependencyResolverBinding.cs @@ -4,7 +4,6 @@ using Tapeti.Config; namespace Tapeti.Default { - /// /// /// Attempts to resolve any unhandled parameters to Controller methods using the IoC container. /// This middleware is included by default in the standard TapetiConfig. diff --git a/Tapeti/Default/DevNullLogger.cs b/Tapeti/Default/DevNullLogger.cs index 9e712d7..002aebe 100644 --- a/Tapeti/Default/DevNullLogger.cs +++ b/Tapeti/Default/DevNullLogger.cs @@ -3,7 +3,6 @@ using Tapeti.Config; namespace Tapeti.Default { - /// /// /// Default ILogger implementation which does not log anything. /// diff --git a/Tapeti/Default/FallbackStringEnumConverter.cs b/Tapeti/Default/FallbackStringEnumConverter.cs index 8967c0e..9af984f 100644 --- a/Tapeti/Default/FallbackStringEnumConverter.cs +++ b/Tapeti/Default/FallbackStringEnumConverter.cs @@ -4,7 +4,6 @@ using Newtonsoft.Json; namespace Tapeti.Default { - /// /// /// Converts an to and from its name string value. If an unknown string value is encountered /// it will translate to 0xDEADBEEF (-559038737) so it can be gracefully handled. diff --git a/Tapeti/Default/JsonMessageSerializer.cs b/Tapeti/Default/JsonMessageSerializer.cs index 8f92928..bceb42a 100644 --- a/Tapeti/Default/JsonMessageSerializer.cs +++ b/Tapeti/Default/JsonMessageSerializer.cs @@ -6,7 +6,6 @@ using Tapeti.Config; namespace Tapeti.Default { - /// /// /// IMessageSerializer implementation for JSON encoding and decoding using Newtonsoft.Json. /// @@ -49,7 +48,7 @@ namespace Tapeti.Default /// public object Deserialize(byte[] body, IMessageProperties properties) { - if (!(properties.ContentType is ContentType)) + if (properties.ContentType is not ContentType) throw new ArgumentException($"content_type must be {ContentType}"); var typeName = properties.GetHeader(ClassTypeHeader); diff --git a/Tapeti/Default/MessageBinding.cs b/Tapeti/Default/MessageBinding.cs index 2265a88..4feddfb 100644 --- a/Tapeti/Default/MessageBinding.cs +++ b/Tapeti/Default/MessageBinding.cs @@ -3,7 +3,6 @@ using Tapeti.Config; namespace Tapeti.Default { - /// /// /// Gets the message class from the first parameter of a controller method. /// This middleware is included by default in the standard TapetiConfig. diff --git a/Tapeti/Default/MessageProperties.cs b/Tapeti/Default/MessageProperties.cs index 8227934..6a4086b 100644 --- a/Tapeti/Default/MessageProperties.cs +++ b/Tapeti/Default/MessageProperties.cs @@ -4,7 +4,6 @@ using Tapeti.Config; namespace Tapeti.Default { - /// /// /// IMessagePropertiesReader implementation for providing properties manually /// diff --git a/Tapeti/Default/NackExceptionStrategy.cs b/Tapeti/Default/NackExceptionStrategy.cs index e760e20..8cf1de7 100644 --- a/Tapeti/Default/NackExceptionStrategy.cs +++ b/Tapeti/Default/NackExceptionStrategy.cs @@ -3,7 +3,6 @@ using Tapeti.Config; namespace Tapeti.Default { - /// /// /// Default implementation of an exception strategy which marks the messages as Error. /// diff --git a/Tapeti/Default/NamespaceMatchExchangeStrategy.cs b/Tapeti/Default/NamespaceMatchExchangeStrategy.cs index 54d16d0..123fb44 100644 --- a/Tapeti/Default/NamespaceMatchExchangeStrategy.cs +++ b/Tapeti/Default/NamespaceMatchExchangeStrategy.cs @@ -3,7 +3,6 @@ using System.Text.RegularExpressions; namespace Tapeti.Default { - /// /// /// IExchangeStrategy implementation which uses the first identifier in the namespace in lower case, /// skipping the first identifier if it is 'Messaging'. diff --git a/Tapeti/Default/PublishResultBinding.cs b/Tapeti/Default/PublishResultBinding.cs index 80a2f90..b56dce0 100644 --- a/Tapeti/Default/PublishResultBinding.cs +++ b/Tapeti/Default/PublishResultBinding.cs @@ -8,7 +8,6 @@ using Tapeti.Helpers; namespace Tapeti.Default { - /// /// /// Attempts to publish a return value for Controller methods as a response to the incoming message. /// diff --git a/Tapeti/Default/RequeueExceptionStrategy.cs b/Tapeti/Default/RequeueExceptionStrategy.cs index f91922b..987e954 100644 --- a/Tapeti/Default/RequeueExceptionStrategy.cs +++ b/Tapeti/Default/RequeueExceptionStrategy.cs @@ -5,7 +5,6 @@ using Tapeti.Config; namespace Tapeti.Default { - /// /// /// Example exception strategy which requeues all messages that result in an error. /// diff --git a/Tapeti/Default/TypeNameRoutingKeyStrategy.cs b/Tapeti/Default/TypeNameRoutingKeyStrategy.cs index 35ee8d8..0074c61 100644 --- a/Tapeti/Default/TypeNameRoutingKeyStrategy.cs +++ b/Tapeti/Default/TypeNameRoutingKeyStrategy.cs @@ -7,7 +7,6 @@ using Tapeti.Helpers; namespace Tapeti.Default { - /// /// /// IRoutingKeyStrategy implementation which transforms the class name into a dot-separated routing key based /// on the casing. Accounts for acronyms. If the class name ends with 'Message' it is not included in the routing key. diff --git a/Tapeti/Exceptions/NackException.cs b/Tapeti/Exceptions/NackException.cs index a2fb7fa..6d2e5e9 100644 --- a/Tapeti/Exceptions/NackException.cs +++ b/Tapeti/Exceptions/NackException.cs @@ -2,7 +2,6 @@ namespace Tapeti.Exceptions { - /// /// /// Raised when a message is nacked by the message bus. /// diff --git a/Tapeti/Exceptions/NoRouteException.cs b/Tapeti/Exceptions/NoRouteException.cs index 3f1ac64..96d865e 100644 --- a/Tapeti/Exceptions/NoRouteException.cs +++ b/Tapeti/Exceptions/NoRouteException.cs @@ -2,7 +2,6 @@ namespace Tapeti.Exceptions { - /// /// /// Raised when a mandatory message has no route. /// diff --git a/Tapeti/Helpers/DictionaryHelper.cs b/Tapeti/Helpers/DictionaryHelper.cs index ea41b7a..ed22529 100644 --- a/Tapeti/Helpers/DictionaryHelper.cs +++ b/Tapeti/Helpers/DictionaryHelper.cs @@ -10,7 +10,7 @@ namespace Tapeti.Helpers /// /// Checks if two dictionaries are considered compatible. If either is null they are considered empty. /// - public static bool NullSafeSameValues(this IReadOnlyDictionary arguments1, IReadOnlyDictionary arguments2) + public static bool NullSafeSameValues(this IReadOnlyDictionary arguments1, IReadOnlyDictionary arguments2) { if (arguments1 == null || arguments2 == null) return (arguments1 == null || arguments1.Count == 0) && (arguments2 == null || arguments2.Count == 0); diff --git a/Tapeti/IDependencyResolver.cs b/Tapeti/IDependencyResolver.cs index 12870bf..002bec1 100644 --- a/Tapeti/IDependencyResolver.cs +++ b/Tapeti/IDependencyResolver.cs @@ -23,7 +23,6 @@ namespace Tapeti } - /// /// /// Allows registering controller classes into the IoC container. Also registers default implementations, /// so that the calling application may override these. diff --git a/Tapeti/ILogger.cs b/Tapeti/ILogger.cs index 58e4c48..cb99434 100644 --- a/Tapeti/ILogger.cs +++ b/Tapeti/ILogger.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Generic; using Tapeti.Config; +using Tapeti.Connection; // ReSharper disable UnusedMember.Global // ReSharper disable UnusedMemberInSuper.Global @@ -24,7 +24,6 @@ namespace Tapeti } - /// /// /// Contains information about the failed connection. /// @@ -37,7 +36,6 @@ namespace Tapeti } - /// /// /// Contains information about the established connection. /// @@ -140,7 +138,7 @@ namespace Tapeti /// The name of the queue that is declared /// The x-arguments of the existing queue /// The x-arguments of the queue that would be declared - void QueueExistsWarning(string queueName, IReadOnlyDictionary existingArguments, IReadOnlyDictionary arguments); + void QueueExistsWarning(string queueName, IRabbitMQArguments existingArguments, IRabbitMQArguments arguments); /// /// Called before a binding is added to a queue. diff --git a/Tapeti/IPublisher.cs b/Tapeti/IPublisher.cs index 70cb754..885133f 100644 --- a/Tapeti/IPublisher.cs +++ b/Tapeti/IPublisher.cs @@ -54,7 +54,6 @@ namespace Tapeti } - /// /// /// Low-level publisher for Tapeti internal use. /// diff --git a/Tapeti/Tapeti.csproj b/Tapeti/Tapeti.csproj index 54098ee..9991245 100644 --- a/Tapeti/Tapeti.csproj +++ b/Tapeti/Tapeti.csproj @@ -23,8 +23,8 @@ - - + + diff --git a/Tapeti/TapetiAppSettingsConnectionParams.cs b/Tapeti/TapetiAppSettingsConnectionParams.cs deleted file mode 100644 index 87138b7..0000000 --- a/Tapeti/TapetiAppSettingsConnectionParams.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System.Configuration; -using System.Linq; - -// ReSharper disable UnusedMember.Global - -namespace Tapeti -{ - /// - /// - /// Implementation of TapetiConnectionParams which reads the values from the AppSettings. - /// - /// - /// - /// AppSettings keys - /// - /// rabbitmq:hostname - /// rabbitmq:port - /// rabbitmq:virtualhost - /// rabbitmq:username - /// rabbitmq:password - /// rabbitmq:prefetchcount - /// rabbitmq:managementport - /// rabbitmq:clientproperty:* - /// - public class TapetiAppSettingsConnectionParams : TapetiConnectionParams - { - private const string DefaultPrefix = "rabbitmq:"; - // ReSharper disable InconsistentNaming - private const string KeyHostname = "hostname"; - private const string KeyPort = "port"; - private const string KeyVirtualHost = "virtualhost"; - private const string KeyUsername = "username"; - private const string KeyPassword = "password"; - private const string KeyPrefetchCount = "prefetchcount"; - private const string KeyManagementPort = "managementport"; - private const string KeyClientProperty = "clientproperty:"; - // ReSharper restore InconsistentNaming - - - private readonly struct AppSettingsKey - { - public readonly string Entry; - public readonly string Parameter; - - public AppSettingsKey(string entry, string parameter) - { - Entry = entry; - Parameter = parameter; - } - } - - - /// - /// - /// The prefix to apply to the keys. Defaults to "rabbitmq:" - public TapetiAppSettingsConnectionParams(string prefix = DefaultPrefix) - { - var keys = !string.IsNullOrEmpty(prefix) - ? ConfigurationManager.AppSettings.AllKeys.Where(k => k.StartsWith(prefix)).Select(k => new AppSettingsKey(k, k.Substring(prefix.Length))) - : ConfigurationManager.AppSettings.AllKeys.Select(k => new AppSettingsKey(k, k)); - - - - foreach (var key in keys) - { - var value = ConfigurationManager.AppSettings[key.Entry]; - - if (key.Parameter.StartsWith(KeyClientProperty)) - { - ClientProperties.Add(key.Parameter.Substring(KeyClientProperty.Length), value); - } - else - { - // ReSharper disable once SwitchStatementMissingSomeCases - don't fail if we encounter an unknown value - switch (key.Parameter) - { - case KeyHostname: HostName = value; break; - case KeyPort: Port = int.Parse(value); break; - case KeyVirtualHost: VirtualHost = value; break; - case KeyUsername: Username = value; break; - case KeyPassword: Password = value; break; - case KeyPrefetchCount: PrefetchCount = ushort.Parse(value); break; - case KeyManagementPort: ManagementPort = int.Parse(value); break; - } - } - } - } - } -} diff --git a/Tapeti/TapetiConfig.cs b/Tapeti/TapetiConfig.cs index fed2296..ca451c4 100644 --- a/Tapeti/TapetiConfig.cs +++ b/Tapeti/TapetiConfig.cs @@ -189,7 +189,7 @@ namespace Tapeti /// protected void RegisterDefaults() { - if (!(DependencyResolver is IDependencyContainer container)) + if (DependencyResolver is not IDependencyContainer container) return; if (ConsoleHelper.IsAvailable()) diff --git a/Tapeti/TapetiConfigControllers.cs b/Tapeti/TapetiConfigControllers.cs index b86e1f1..4307171 100644 --- a/Tapeti/TapetiConfigControllers.cs +++ b/Tapeti/TapetiConfigControllers.cs @@ -1,16 +1,15 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; using Tapeti.Annotations; using Tapeti.Config; +using Tapeti.Connection; using Tapeti.Default; // ReSharper disable UnusedMember.Global namespace Tapeti { - /// /// /// Thrown when an issue is detected in a controller configuration. /// @@ -171,40 +170,34 @@ namespace Tapeti } - private static IReadOnlyDictionary GetQueueArguments(QueueArgumentsAttribute queueArgumentsAttribute) + private static IRabbitMQArguments GetQueueArguments(QueueArgumentsAttribute queueArgumentsAttribute) { if (queueArgumentsAttribute == null) return null; - #if NETSTANDARD2_1_OR_GREATER - var arguments = new Dictionary(queueArgumentsAttribute.CustomArguments); - #else - var arguments = new Dictionary(); - foreach (var pair in queueArgumentsAttribute.CustomArguments) - arguments.Add(pair.Key, pair.Value); - #endif - + var arguments = new RabbitMQArguments(queueArgumentsAttribute.CustomArguments); + if (queueArgumentsAttribute.MaxLength > 0) - arguments.Add(@"x-max-length", queueArgumentsAttribute.MaxLength.ToString()); + arguments.Add(@"x-max-length", queueArgumentsAttribute.MaxLength); if (queueArgumentsAttribute.MaxLengthBytes > 0) - arguments.Add(@"x-max-length-bytes", queueArgumentsAttribute.MaxLengthBytes.ToString()); + arguments.Add(@"x-max-length-bytes", queueArgumentsAttribute.MaxLengthBytes); if (queueArgumentsAttribute.MessageTTL > 0) - arguments.Add(@"x-message-ttl", queueArgumentsAttribute.MessageTTL.ToString()); + arguments.Add(@"x-message-ttl", queueArgumentsAttribute.MessageTTL); switch (queueArgumentsAttribute.Overflow) { case RabbitMQOverflow.NotSpecified: break; case RabbitMQOverflow.DropHead: - arguments.Add(@"x-overflow", @"drop-head"); + arguments.AddUTF8(@"x-overflow", @"drop-head"); break; case RabbitMQOverflow.RejectPublish: - arguments.Add(@"x-overflow", @"reject-publish"); + arguments.AddUTF8(@"x-overflow", @"reject-publish"); break; case RabbitMQOverflow.RejectPublishDeadletter: - arguments.Add(@"x-overflow", @"reject-publish-dlx"); + arguments.AddUTF8(@"x-overflow", @"reject-publish-dlx"); break; default: throw new ArgumentOutOfRangeException(nameof(queueArgumentsAttribute.Overflow), queueArgumentsAttribute.Overflow, "Unsupported Overflow value"); diff --git a/Tapeti/TapetiConnection.cs b/Tapeti/TapetiConnection.cs index 6b9ef0a..1bd4bdf 100644 --- a/Tapeti/TapetiConnection.cs +++ b/Tapeti/TapetiConnection.cs @@ -9,7 +9,6 @@ using Tapeti.Connection; namespace Tapeti { - /// /// /// Creates a connection to RabbitMQ based on the provided Tapeti config. /// diff --git a/Tapeti/Tasks/SingleThreadTaskQueue.cs b/Tapeti/Tasks/SingleThreadTaskQueue.cs index c08a1d4..19303ae 100644 --- a/Tapeti/Tasks/SingleThreadTaskQueue.cs +++ b/Tapeti/Tasks/SingleThreadTaskQueue.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; namespace Tapeti.Tasks { - /// /// /// An implementation of a queue which runs tasks on a single thread. ///