refactor: Implement Yaml Behavior system for extending parsing features

YamlSerializerFactory continues to grow as we add parsing features to
YAML files. A new behavior system now provides granular sets of features
to the factory. To extend the functionality of the  YAML parser,
implement the `IYamlBehavior` interface. It will automatically be
registered to Autofac and injected into `YamlSerializerFactory`.
pull/153/head
Robert Dailey 2 years ago
parent f2bd1fd1be
commit eb9898fdd7

@ -10,6 +10,7 @@ using Recyclarr.Cli.Config;
using Recyclarr.Cli.Logging; using Recyclarr.Cli.Logging;
using Recyclarr.Cli.Migration; using Recyclarr.Cli.Migration;
using Recyclarr.Common; using Recyclarr.Common;
using Recyclarr.Common.Extensions;
using Recyclarr.TrashLib.Cache; using Recyclarr.TrashLib.Cache;
using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Config.Services;
@ -36,6 +37,10 @@ public static class CompositionRoot
private static ILifetimeScope Setup(ContainerBuilder builder, Action<ContainerBuilder>? extraRegistrations = null) private static ILifetimeScope Setup(ContainerBuilder builder, Action<ContainerBuilder>? extraRegistrations = null)
{ {
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => x.FullName?.StartsWithIgnoreCase("Recyclarr") ?? false)
.ToArray();
RegisterAppPaths(builder); RegisterAppPaths(builder);
RegisterLogger(builder); RegisterLogger(builder);
@ -56,7 +61,7 @@ public static class CompositionRoot
builder.RegisterType<ServiceRequestBuilder>().As<IServiceRequestBuilder>(); builder.RegisterType<ServiceRequestBuilder>().As<IServiceRequestBuilder>();
builder.RegisterType<ProgressBar>(); builder.RegisterType<ProgressBar>();
ConfigurationRegistrations(builder); ConfigurationRegistrations(builder, assemblies);
CommandRegistrations(builder); CommandRegistrations(builder);
builder.Register(_ => AutoMapperConfig.Setup()).SingleInstance(); builder.Register(_ => AutoMapperConfig.Setup()).SingleInstance();
@ -81,9 +86,9 @@ public static class CompositionRoot
builder.RegisterType<DefaultAppDataSetup>(); builder.RegisterType<DefaultAppDataSetup>();
} }
private static void ConfigurationRegistrations(ContainerBuilder builder) private static void ConfigurationRegistrations(ContainerBuilder builder, Assembly[] assemblies)
{ {
builder.RegisterModule<ConfigAutofacModule>(); builder.RegisterModule(new ConfigAutofacModule(assemblies));
builder.RegisterType<DefaultObjectFactory>().As<IObjectFactory>(); builder.RegisterType<DefaultObjectFactory>().As<IObjectFactory>();
builder.RegisterType<ConfigurationFinder>().As<IConfigurationFinder>(); builder.RegisterType<ConfigurationFinder>().As<IConfigurationFinder>();

@ -1,8 +1,8 @@
using System.IO.Abstractions; using System.IO.Abstractions;
using FluentValidation; using FluentValidation;
using Recyclarr.Cli.Logging; using Recyclarr.Cli.Logging;
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.Config.Yaml;
using Recyclarr.TrashLib.Http; using Recyclarr.TrashLib.Http;
using Serilog; using Serilog;
using Serilog.Context; using Serilog.Context;

@ -3,8 +3,8 @@ using AutoFixture.NUnit3;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using Recyclarr.TestLibrary.AutoFixture; using Recyclarr.TestLibrary.AutoFixture;
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Settings; using Recyclarr.TrashLib.Config.Settings;
using Recyclarr.TrashLib.Config.Yaml;
using Recyclarr.TrashLib.Startup; using Recyclarr.TrashLib.Startup;
namespace Recyclarr.TrashLib.Tests.Config.Settings; namespace Recyclarr.TrashLib.Tests.Config.Settings;

@ -4,18 +4,30 @@ using FluentValidation;
using Recyclarr.TrashLib.Config.Secrets; using Recyclarr.TrashLib.Config.Secrets;
using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.Config.Settings; using Recyclarr.TrashLib.Config.Settings;
using Recyclarr.TrashLib.Config.Yaml;
using Module = Autofac.Module; using Module = Autofac.Module;
namespace Recyclarr.TrashLib.Config; namespace Recyclarr.TrashLib.Config;
public class ConfigAutofacModule : Module public class ConfigAutofacModule : Module
{ {
private readonly Assembly[] _assemblies;
public ConfigAutofacModule(Assembly[] assemblies)
{
_assemblies = assemblies;
}
protected override void Load(ContainerBuilder builder) protected override void Load(ContainerBuilder builder)
{ {
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) builder.RegisterAssemblyTypes(_assemblies)
.AsClosedTypesOf(typeof(IValidator<>)) .AsClosedTypesOf(typeof(IValidator<>))
.AsImplementedInterfaces(); .AsImplementedInterfaces();
builder.RegisterAssemblyTypes(_assemblies)
.AssignableTo<IYamlBehavior>()
.As<IYamlBehavior>();
builder.RegisterType<SettingsProvider>().As<ISettingsProvider>().SingleInstance(); builder.RegisterType<SettingsProvider>().As<ISettingsProvider>().SingleInstance();
builder.RegisterType<SecretsProvider>().As<ISecretsProvider>().SingleInstance(); builder.RegisterType<SecretsProvider>().As<ISecretsProvider>().SingleInstance();
builder.RegisterType<YamlSerializerFactory>().As<IYamlSerializerFactory>(); builder.RegisterType<YamlSerializerFactory>().As<IYamlSerializerFactory>();

@ -1,9 +0,0 @@
using YamlDotNet.Serialization;
namespace Recyclarr.TrashLib.Config;
public interface IYamlSerializerFactory
{
IDeserializer CreateDeserializer(Action<DeserializerBuilder>? extraBuilder = null);
ISerializer CreateSerializer();
}

@ -1,4 +1,5 @@
using Recyclarr.Common.Extensions; using Recyclarr.Common.Extensions;
using Recyclarr.TrashLib.Config.Yaml;
using Recyclarr.TrashLib.Startup; using Recyclarr.TrashLib.Startup;
namespace Recyclarr.TrashLib.Config.Settings; namespace Recyclarr.TrashLib.Config.Settings;

@ -0,0 +1,8 @@
using YamlDotNet.Serialization;
namespace Recyclarr.TrashLib.Config.Yaml;
public interface IYamlBehavior
{
void Setup(DeserializerBuilder builder);
}

@ -0,0 +1,8 @@
using YamlDotNet.Serialization;
namespace Recyclarr.TrashLib.Config.Yaml;
public interface IYamlSerializerFactory
{
IDeserializer CreateDeserializer();
}

@ -0,0 +1,23 @@
using JetBrains.Annotations;
using Recyclarr.TrashLib.Config.Secrets;
using YamlDotNet.Serialization;
namespace Recyclarr.TrashLib.Config.Yaml;
[UsedImplicitly]
public class SecretsYamlBehavior : IYamlBehavior
{
private readonly ISecretsProvider _secretsProvider;
public SecretsYamlBehavior(ISecretsProvider secretsProvider)
{
_secretsProvider = secretsProvider;
}
public void Setup(DeserializerBuilder builder)
{
builder
.WithNodeDeserializer(new SecretsDeserializer(_secretsProvider))
.WithTagMapping("!secret", typeof(SecretTag));
}
}

@ -1,42 +1,34 @@
using Recyclarr.Common.YamlDotNet; using Recyclarr.Common.YamlDotNet;
using Recyclarr.TrashLib.Config.Secrets;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NamingConventions;
namespace Recyclarr.TrashLib.Config; namespace Recyclarr.TrashLib.Config.Yaml;
public class YamlSerializerFactory : IYamlSerializerFactory public class YamlSerializerFactory : IYamlSerializerFactory
{ {
private readonly IObjectFactory _objectFactory; private readonly IObjectFactory _objectFactory;
private readonly ISecretsProvider _secretsProvider; private readonly IEnumerable<IYamlBehavior> _behaviors;
public YamlSerializerFactory(IObjectFactory objectFactory, ISecretsProvider secretsProvider) public YamlSerializerFactory(IObjectFactory objectFactory, IEnumerable<IYamlBehavior> behaviors)
{ {
_objectFactory = objectFactory; _objectFactory = objectFactory;
_secretsProvider = secretsProvider; _behaviors = behaviors;
} }
public IDeserializer CreateDeserializer(Action<DeserializerBuilder>? extraBuilder = null) public IDeserializer CreateDeserializer()
{ {
var builder = new DeserializerBuilder() var builder = new DeserializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance) .WithNamingConvention(UnderscoredNamingConvention.Instance)
.WithTypeConverter(new YamlNullableEnumTypeConverter()) .WithTypeConverter(new YamlNullableEnumTypeConverter())
.WithNodeDeserializer(new SecretsDeserializer(_secretsProvider))
.WithTagMapping("!secret", typeof(SecretTag))
.WithNodeTypeResolver(new ReadOnlyCollectionNodeTypeResolver()) .WithNodeTypeResolver(new ReadOnlyCollectionNodeTypeResolver())
.WithNodeDeserializer(new ForceEmptySequences(_objectFactory)) .WithNodeDeserializer(new ForceEmptySequences(_objectFactory))
.WithObjectFactory(_objectFactory); .WithObjectFactory(_objectFactory);
extraBuilder?.Invoke(builder); foreach (var behavior in _behaviors)
{
behavior.Setup(builder);
}
return builder.Build(); return builder.Build();
} }
public ISerializer CreateSerializer()
{
return new SerializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.WithTypeConverter(new YamlNullableEnumTypeConverter())
.Build();
}
} }
Loading…
Cancel
Save