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.Migration;
using Recyclarr.Common;
using Recyclarr.Common.Extensions;
using Recyclarr.TrashLib.Cache;
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Services;
@ -36,6 +37,10 @@ public static class CompositionRoot
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);
RegisterLogger(builder);
@ -56,7 +61,7 @@ public static class CompositionRoot
builder.RegisterType<ServiceRequestBuilder>().As<IServiceRequestBuilder>();
builder.RegisterType<ProgressBar>();
ConfigurationRegistrations(builder);
ConfigurationRegistrations(builder, assemblies);
CommandRegistrations(builder);
builder.Register(_ => AutoMapperConfig.Setup()).SingleInstance();
@ -81,9 +86,9 @@ public static class CompositionRoot
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<ConfigurationFinder>().As<IConfigurationFinder>();

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

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

@ -4,18 +4,30 @@ using FluentValidation;
using Recyclarr.TrashLib.Config.Secrets;
using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.Config.Settings;
using Recyclarr.TrashLib.Config.Yaml;
using Module = Autofac.Module;
namespace Recyclarr.TrashLib.Config;
public class ConfigAutofacModule : Module
{
private readonly Assembly[] _assemblies;
public ConfigAutofacModule(Assembly[] assemblies)
{
_assemblies = assemblies;
}
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
builder.RegisterAssemblyTypes(_assemblies)
.AsClosedTypesOf(typeof(IValidator<>))
.AsImplementedInterfaces();
builder.RegisterAssemblyTypes(_assemblies)
.AssignableTo<IYamlBehavior>()
.As<IYamlBehavior>();
builder.RegisterType<SettingsProvider>().As<ISettingsProvider>().SingleInstance();
builder.RegisterType<SecretsProvider>().As<ISecretsProvider>().SingleInstance();
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.TrashLib.Config.Yaml;
using Recyclarr.TrashLib.Startup;
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.TrashLib.Config.Secrets;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace Recyclarr.TrashLib.Config;
namespace Recyclarr.TrashLib.Config.Yaml;
public class YamlSerializerFactory : IYamlSerializerFactory
{
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;
_secretsProvider = secretsProvider;
_behaviors = behaviors;
}
public IDeserializer CreateDeserializer(Action<DeserializerBuilder>? extraBuilder = null)
public IDeserializer CreateDeserializer()
{
var builder = new DeserializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.WithTypeConverter(new YamlNullableEnumTypeConverter())
.WithNodeDeserializer(new SecretsDeserializer(_secretsProvider))
.WithTagMapping("!secret", typeof(SecretTag))
.WithNodeTypeResolver(new ReadOnlyCollectionNodeTypeResolver())
.WithNodeDeserializer(new ForceEmptySequences(_objectFactory))
.WithObjectFactory(_objectFactory);
extraBuilder?.Invoke(builder);
foreach (var behavior in _behaviors)
{
behavior.Setup(builder);
}
return builder.Build();
}
public ISerializer CreateSerializer()
{
return new SerializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.WithTypeConverter(new YamlNullableEnumTypeConverter())
.Build();
}
}
Loading…
Cancel
Save