fix: Refactor and re-introduce capability enforcement

Fixes #189
pull/201/head
Robert Dailey 11 months ago
parent f020459023
commit 406e0590a4

@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- When using `sync`, continue processing other instances when there's a failure.
- Regression: Perform Sonarr compatibility checks again (#189).
## [5.0.2] - 2023-06-24

@ -12,16 +12,16 @@ namespace Recyclarr.Cli.Pipelines.ReleaseProfile.Api;
public class SonarrReleaseProfileCompatibilityHandler : ISonarrReleaseProfileCompatibilityHandler
{
private readonly ILogger _log;
private readonly ISonarrCapabilityChecker _capabilityChecker;
private readonly ISonarrCapabilityFetcher _capabilityFetcher;
private readonly IMapper _mapper;
public SonarrReleaseProfileCompatibilityHandler(
ILogger log,
ISonarrCapabilityChecker capabilityChecker,
ISonarrCapabilityFetcher capabilityFetcher,
IMapper mapper)
{
_log = log;
_capabilityChecker = capabilityChecker;
_capabilityFetcher = capabilityFetcher;
_mapper = mapper;
}
@ -29,7 +29,7 @@ public class SonarrReleaseProfileCompatibilityHandler : ISonarrReleaseProfileCom
IServiceConfiguration config,
SonarrReleaseProfile profile)
{
var capabilities = await _capabilityChecker.GetCapabilities(config);
var capabilities = await _capabilityFetcher.GetCapabilities(config);
if (capabilities is null)
{
throw new ServiceIncompatibilityException("Capabilities could not be obtained");

@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using Flurl.Http;
using Recyclarr.Cli.Console.Settings;
using Recyclarr.TrashLib.Compatibility;
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Parsing;
using Recyclarr.TrashLib.Config.Services;
@ -18,19 +19,22 @@ public class SyncProcessor : ISyncProcessor
private readonly IConfigurationFinder _configFinder;
private readonly IConfigurationLoader _configLoader;
private readonly SyncPipelineExecutor _pipelines;
private readonly ServiceAgnosticCapabilityEnforcer _capabilityEnforcer;
public SyncProcessor(
IAnsiConsole console,
ILogger log,
IConfigurationFinder configFinder,
IConfigurationLoader configLoader,
SyncPipelineExecutor pipelines)
SyncPipelineExecutor pipelines,
ServiceAgnosticCapabilityEnforcer capabilityEnforcer)
{
_console = console;
_log = log;
_configFinder = configFinder;
_configLoader = configLoader;
_pipelines = pipelines;
_capabilityEnforcer = capabilityEnforcer;
}
public async Task<ExitStatus> ProcessConfigs(ISyncSettings settings)
@ -74,6 +78,7 @@ public class SyncProcessor : ISyncProcessor
try
{
PrintProcessingHeader(config.ServiceType, config);
await _capabilityEnforcer.Check(config);
await _pipelines.Process(settings, config);
}
catch (Exception e)

@ -10,12 +10,14 @@ public class CompatibilityAutofacModule : Module
{
base.Load(builder);
builder.RegisterType<ServiceAgnosticCapabilityEnforcer>();
// Sonarr
builder.RegisterType<SonarrCapabilityFetcher>().As<ISonarrCapabilityFetcher>();
builder.RegisterType<SonarrCapabilityEnforcer>();
builder.RegisterType<SonarrCapabilityChecker>().As<ISonarrCapabilityChecker>()
.InstancePerLifetimeScope();
// Radarr
builder.RegisterType<RadarrCapabilityChecker>().InstancePerLifetimeScope();
builder.RegisterType<RadarrCapabilityFetcher>().As<IRadarrCapabilityFetcher>();
builder.RegisterType<RadarrCapabilityEnforcer>();
}
}

@ -0,0 +1,8 @@
using Recyclarr.TrashLib.Config.Services;
namespace Recyclarr.TrashLib.Compatibility.Radarr;
public interface IRadarrCapabilityFetcher
{
Task<RadarrCapabilities?> GetCapabilities(IServiceConfiguration config);
}

@ -0,0 +1,25 @@
using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.ExceptionTypes;
namespace Recyclarr.TrashLib.Compatibility.Radarr;
public class RadarrCapabilityEnforcer
{
private readonly IRadarrCapabilityFetcher _capabilityFetcher;
public RadarrCapabilityEnforcer(IRadarrCapabilityFetcher capabilityFetcher)
{
_capabilityFetcher = capabilityFetcher;
}
public async Task Check(RadarrConfiguration config)
{
var capabilities = await _capabilityFetcher.GetCapabilities(config);
if (capabilities is null)
{
throw new ServiceIncompatibilityException("Capabilities could not be obtained");
}
// For the future: Add more capability checks here as needed
}
}

@ -2,9 +2,9 @@ using Recyclarr.TrashLib.ApiServices.System;
namespace Recyclarr.TrashLib.Compatibility.Radarr;
public class RadarrCapabilityChecker : ServiceCapabilityChecker<RadarrCapabilities>
public class RadarrCapabilityFetcher : ServiceCapabilityFetcher<RadarrCapabilities>, IRadarrCapabilityFetcher
{
public RadarrCapabilityChecker(IServiceInformation info)
public RadarrCapabilityFetcher(IServiceInformation info)
: base(info)
{
}

@ -0,0 +1,33 @@
using Recyclarr.TrashLib.Compatibility.Radarr;
using Recyclarr.TrashLib.Compatibility.Sonarr;
using Recyclarr.TrashLib.Config.Services;
namespace Recyclarr.TrashLib.Compatibility;
public class ServiceAgnosticCapabilityEnforcer
{
private readonly SonarrCapabilityEnforcer _sonarrEnforcer;
private readonly RadarrCapabilityEnforcer _radarrEnforcer;
public ServiceAgnosticCapabilityEnforcer(
SonarrCapabilityEnforcer sonarrEnforcer,
RadarrCapabilityEnforcer radarrEnforcer)
{
_sonarrEnforcer = sonarrEnforcer;
_radarrEnforcer = radarrEnforcer;
}
public async Task Check(IServiceConfiguration config)
{
switch (config)
{
case SonarrConfiguration c:
await _sonarrEnforcer.Check(c);
break;
case RadarrConfiguration c:
await _radarrEnforcer.Check(c);
break;
}
}
}

@ -3,11 +3,11 @@ using Recyclarr.TrashLib.Config.Services;
namespace Recyclarr.TrashLib.Compatibility;
public abstract class ServiceCapabilityChecker<T> where T : class
public abstract class ServiceCapabilityFetcher<T> where T : class
{
private readonly IServiceInformation _info;
protected ServiceCapabilityChecker(IServiceInformation info)
protected ServiceCapabilityFetcher(IServiceInformation info)
{
_info = info;
}

@ -2,7 +2,7 @@ using Recyclarr.TrashLib.Config.Services;
namespace Recyclarr.TrashLib.Compatibility.Sonarr;
public interface ISonarrCapabilityChecker
public interface ISonarrCapabilityFetcher
{
Task<SonarrCapabilities?> GetCapabilities(IServiceConfiguration config);
}

@ -1,8 +1,10 @@
namespace Recyclarr.TrashLib.Compatibility.Sonarr;
public record SonarrCapabilities(Version Version)
public record SonarrCapabilities
{
public static Version MinimumVersion => new("3.0.4.1098");
public static Version MinimumVersion { get; } = new("3.0.4.1098");
public Version Version { get; init; } = new();
public bool SupportsNamedReleaseProfiles { get; init; }

@ -6,16 +6,16 @@ namespace Recyclarr.TrashLib.Compatibility.Sonarr;
public class SonarrCapabilityEnforcer
{
private readonly ISonarrCapabilityChecker _capabilityChecker;
private readonly ISonarrCapabilityFetcher _capabilityFetcher;
public SonarrCapabilityEnforcer(ISonarrCapabilityChecker capabilityChecker)
public SonarrCapabilityEnforcer(ISonarrCapabilityFetcher capabilityFetcher)
{
_capabilityChecker = capabilityChecker;
_capabilityFetcher = capabilityFetcher;
}
public async Task Check(SonarrConfiguration config)
{
var capabilities = await _capabilityChecker.GetCapabilities(config);
var capabilities = await _capabilityFetcher.GetCapabilities(config);
if (capabilities is null)
{
throw new ServiceIncompatibilityException("Capabilities could not be obtained");

@ -2,17 +2,19 @@ using Recyclarr.TrashLib.ApiServices.System;
namespace Recyclarr.TrashLib.Compatibility.Sonarr;
public class SonarrCapabilityChecker : ServiceCapabilityChecker<SonarrCapabilities>, ISonarrCapabilityChecker
public class SonarrCapabilityFetcher : ServiceCapabilityFetcher<SonarrCapabilities>, ISonarrCapabilityFetcher
{
public SonarrCapabilityChecker(IServiceInformation info)
public SonarrCapabilityFetcher(IServiceInformation info)
: base(info)
{
}
protected override SonarrCapabilities BuildCapabilitiesObject(Version version)
{
return new SonarrCapabilities(version)
return new SonarrCapabilities
{
Version = version,
SupportsNamedReleaseProfiles =
version >= SonarrCapabilities.MinimumVersion,

@ -28,7 +28,7 @@ public class SonarrReleaseProfileCompatibilityHandlerTest : CliIntegrationFixtur
protected override void RegisterTypes(ContainerBuilder builder)
{
base.RegisterTypes(builder);
builder.RegisterMockFor<ISonarrCapabilityChecker>();
builder.RegisterMockFor<ISonarrCapabilityFetcher>();
}
[Test]
@ -59,8 +59,8 @@ public class SonarrReleaseProfileCompatibilityHandlerTest : CliIntegrationFixtur
[Test]
public async Task Send_v2_to_v1()
{
var capabilityChecker = Resolve<ISonarrCapabilityChecker>();
capabilityChecker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities(new Version())
var capabilityChecker = Resolve<ISonarrCapabilityFetcher>();
capabilityChecker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities
{
ArraysNeededForReleaseProfileRequiredAndIgnored = false
});
@ -76,8 +76,8 @@ public class SonarrReleaseProfileCompatibilityHandlerTest : CliIntegrationFixtur
[Test]
public async Task Send_v2_to_v2()
{
var capabilityChecker = Resolve<ISonarrCapabilityChecker>();
capabilityChecker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities(new Version())
var capabilityChecker = Resolve<ISonarrCapabilityFetcher>();
capabilityChecker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities
{
ArraysNeededForReleaseProfileRequiredAndIgnored = true
});

@ -11,12 +11,12 @@ public class SonarrCapabilityEnforcerTest
{
[Test, AutoMockData]
public void Fail_when_capabilities_not_obtained(
[Frozen] ISonarrCapabilityChecker checker,
[Frozen] ISonarrCapabilityFetcher fetcher,
SonarrCapabilityEnforcer sut)
{
var config = NewConfig.Sonarr();
checker.GetCapabilities(default!).ReturnsForAnyArgs((SonarrCapabilities?) null);
fetcher.GetCapabilities(default!).ReturnsForAnyArgs((SonarrCapabilities?) null);
var act = () => sut.Check(config);
@ -25,12 +25,12 @@ public class SonarrCapabilityEnforcerTest
[Test, AutoMockData]
public void Minimum_version_not_met(
[Frozen] ISonarrCapabilityChecker checker,
[Frozen] ISonarrCapabilityFetcher fetcher,
SonarrCapabilityEnforcer sut)
{
var config = NewConfig.Sonarr();
checker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities(new Version())
fetcher.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities
{
SupportsNamedReleaseProfiles = false
});
@ -42,7 +42,7 @@ public class SonarrCapabilityEnforcerTest
[Test, AutoMockData]
public void Release_profiles_not_allowed_in_v4(
[Frozen] ISonarrCapabilityChecker checker,
[Frozen] ISonarrCapabilityFetcher fetcher,
SonarrCapabilityEnforcer sut)
{
var config = NewConfig.Sonarr() with
@ -53,7 +53,7 @@ public class SonarrCapabilityEnforcerTest
}
};
checker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities(new Version())
fetcher.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities
{
SupportsNamedReleaseProfiles = true,
SupportsCustomFormats = true
@ -66,7 +66,7 @@ public class SonarrCapabilityEnforcerTest
[Test, AutoMockData]
public void Custom_formats_not_allowed_in_v3(
[Frozen] ISonarrCapabilityChecker checker,
[Frozen] ISonarrCapabilityFetcher fetcher,
SonarrCapabilityEnforcer sut)
{
var config = NewConfig.Sonarr() with
@ -77,7 +77,7 @@ public class SonarrCapabilityEnforcerTest
}
};
checker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities(new Version())
fetcher.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities
{
SupportsNamedReleaseProfiles = true,
SupportsCustomFormats = false

Loading…
Cancel
Save