1
0
mirror of synced 2024-07-01 08:17:39 +00:00
Tapeti/Tapeti/TapetiConfig.cs
Mark van Renswoude bcdb376256 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
2022-11-22 13:20:47 +01:00

341 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Tapeti.Config;
using Tapeti.Default;
using Tapeti.Helpers;
// ReSharper disable UnusedMember.Global
namespace Tapeti
{
/// <inheritdoc cref="ITapetiConfigBuilder" />
/// <summary>
/// Default implementation of the Tapeti config builder.
/// Automatically registers the default middleware for injecting the message parameter and handling the return value.
/// </summary>
public class TapetiConfig : ITapetiConfigBuilderAccess
{
private Config config;
private readonly List<IControllerBindingMiddleware> bindingMiddleware = new();
/// <inheritdoc />
public IDependencyResolver DependencyResolver => GetConfig().DependencyResolver;
/// <summary>
/// Instantiates a new Tapeti config builder.
/// </summary>
/// <param name="dependencyResolver">A wrapper implementation for an IoC container to allow dependency injection</param>
public TapetiConfig(IDependencyResolver dependencyResolver)
{
config = new Config(dependencyResolver);
Use(new DependencyResolverBinding());
Use(new PublishResultBinding());
Use(new CancellationTokenBinding());
// Registered last so it runs first and the MessageClass is known to other middleware
Use(new MessageBinding());
}
/// <inheritdoc />
public ITapetiConfig Build()
{
if (config == null)
throw new InvalidOperationException("TapetiConfig.Build must only be called once");
RegisterDefaults();
(config.DependencyResolver as IDependencyContainer)?.RegisterDefaultSingleton<ITapetiConfig>(config);
var outputConfig = config;
config = null;
outputConfig.Lock();
return outputConfig;
}
/// <inheritdoc />
public ITapetiConfigBuilder Use(IControllerBindingMiddleware handler)
{
bindingMiddleware.Add(handler);
return this;
}
/// <inheritdoc />
public ITapetiConfigBuilder Use(IMessageMiddleware handler)
{
GetConfig().Use(handler);
return this;
}
/// <inheritdoc />
public ITapetiConfigBuilder Use(IPublishMiddleware handler)
{
GetConfig().Use(handler);
return this;
}
/// <inheritdoc />
public ITapetiConfigBuilder Use(ITapetiExtension extension)
{
if (DependencyResolver is IDependencyContainer container)
extension.RegisterDefaults(container);
var configInstance = GetConfig();
var middlewareBundle = extension.GetMiddleware(DependencyResolver);
if (middlewareBundle != null)
{
foreach (var middleware in middlewareBundle)
{
switch (middleware)
{
case IControllerBindingMiddleware bindingExtension:
Use(bindingExtension);
break;
case IMessageMiddleware messageExtension:
configInstance.Use(messageExtension);
break;
case IPublishMiddleware publishExtension:
configInstance.Use(publishExtension);
break;
default:
throw new ArgumentException(
$"Unsupported middleware implementation: {middleware?.GetType().Name ?? "null"}");
}
}
}
var bindingBundle = (extension as ITapetiExtensionBinding)?.GetBindings(DependencyResolver);
if (bindingBundle == null)
return this;
foreach (var binding in bindingBundle)
config.RegisterBinding(binding);
return this;
}
/// <inheritdoc />
public void RegisterBinding(IBinding binding)
{
GetConfig().RegisterBinding(binding);
}
/// <inheritdoc />
public ITapetiConfigBuilder DisablePublisherConfirms()
{
GetConfig().SetPublisherConfirms(false);
return this;
}
/// <inheritdoc />
public ITapetiConfigBuilder SetPublisherConfirms(bool enabled)
{
GetConfig().SetPublisherConfirms(enabled);
return this;
}
/// <inheritdoc />
public ITapetiConfigBuilder EnableDeclareDurableQueues()
{
GetConfig().SetDeclareDurableQueues(true);
return this;
}
/// <inheritdoc />
public ITapetiConfigBuilder SetDeclareDurableQueues(bool enabled)
{
GetConfig().SetDeclareDurableQueues(enabled);
return this;
}
/// <inheritdoc />
public ITapetiConfigBuilder DisableVerifyDurableQueues()
{
GetConfig().SetVerifyDurableQueues(false);
return this;
}
/// <inheritdoc />
public ITapetiConfigBuilder SetVerifyDurableQueues(bool enabled)
{
GetConfig().SetVerifyDurableQueues(enabled);
return this;
}
/// <summary>
/// Registers the default implementation of various Tapeti interfaces into the IoC container.
/// </summary>
protected void RegisterDefaults()
{
if (DependencyResolver is not IDependencyContainer container)
return;
if (ConsoleHelper.IsAvailable())
container.RegisterDefault<ILogger, ConsoleLogger>();
else
container.RegisterDefault<ILogger, DevNullLogger>();
container.RegisterDefault<IMessageSerializer, JsonMessageSerializer>();
container.RegisterDefault<IExchangeStrategy, NamespaceMatchExchangeStrategy>();
container.RegisterDefault<IRoutingKeyStrategy, TypeNameRoutingKeyStrategy>();
container.RegisterDefault<IExceptionStrategy, NackExceptionStrategy>();
}
/// <inheritdoc />
public void ApplyBindingMiddleware(IControllerBindingContext context, Action lastHandler)
{
MiddlewareHelper.Go(bindingMiddleware,
(handler, next) => handler.Handle(context, next),
lastHandler);
}
private Config GetConfig()
{
if (config == null)
throw new InvalidOperationException("TapetiConfig can not be updated after Build");
return config;
}
/// <inheritdoc />
internal class Config : ITapetiConfig
{
private readonly ConfigFeatures features = new();
private readonly ConfigMiddleware middleware = new();
private readonly ConfigBindings bindings = new();
public IDependencyResolver DependencyResolver { get; }
public ITapetiConfigFeatues Features => features;
public ITapetiConfigMiddleware Middleware => middleware;
public ITapetiConfigBindings Bindings => bindings;
public Config(IDependencyResolver dependencyResolver)
{
DependencyResolver = dependencyResolver;
}
public void Lock()
{
bindings.Lock();
}
public void Use(IMessageMiddleware handler)
{
middleware.Use(handler);
}
public void Use(IPublishMiddleware handler)
{
middleware.Use(handler);
}
public void RegisterBinding(IBinding binding)
{
bindings.Add(binding);
}
public void SetPublisherConfirms(bool enabled)
{
features.PublisherConfirms = enabled;
}
public void SetDeclareDurableQueues(bool enabled)
{
features.DeclareDurableQueues = enabled;
}
public void SetVerifyDurableQueues(bool enabled)
{
features.VerifyDurableQueues = enabled;
}
}
internal class ConfigFeatures : ITapetiConfigFeatues
{
public bool PublisherConfirms { get; internal set; } = true;
public bool DeclareDurableQueues { get; internal set; }
public bool VerifyDurableQueues { get; internal set; } = true;
}
internal class ConfigMiddleware : ITapetiConfigMiddleware
{
private readonly List<IMessageMiddleware> messageMiddleware = new();
private readonly List<IPublishMiddleware> publishMiddleware = new();
public IReadOnlyList<IMessageMiddleware> Message => messageMiddleware;
public IReadOnlyList<IPublishMiddleware> Publish => publishMiddleware;
public void Use(IMessageMiddleware handler)
{
messageMiddleware.Add(handler);
}
public void Use(IPublishMiddleware handler)
{
publishMiddleware.Add(handler);
}
}
internal class ConfigBindings : List<IBinding>, ITapetiConfigBindings
{
private Dictionary<MethodInfo, IControllerMethodBinding> methodLookup;
public IControllerMethodBinding ForMethod(Delegate method)
{
return methodLookup.TryGetValue(method.Method, out var binding) ? binding : null;
}
public IControllerMethodBinding ForMethod(MethodInfo method)
{
return methodLookup.TryGetValue(method, out var binding) ? binding : null;
}
public void Lock()
{
methodLookup = this
.Where(binding => binding is IControllerMethodBinding)
.Cast<IControllerMethodBinding>()
.ToDictionary(binding => binding.Method, binding => binding);
}
}
}
}