diff --git a/src/Recyclarr/Command/RadarrCommand.cs b/src/Recyclarr/Command/RadarrCommand.cs index 906c76ce..1a1a007d 100644 --- a/src/Recyclarr/Command/RadarrCommand.cs +++ b/src/Recyclarr/Command/RadarrCommand.cs @@ -1,3 +1,4 @@ +using System.Reactive.Linq; using Autofac; using CliFx.Attributes; using JetBrains.Annotations; @@ -61,6 +62,12 @@ internal class RadarrCommand : ServiceCommand log.Information("Processing server {Url}", FlurlLogging.SanitizeUrl(config.BaseUrl)); + // There's no actual compatibility checks to perform yet. We directly access the RadarrCompatibility class, + // as opposed to a IRadarrVersionEnforcement object (like Sonarr does), simply to force the API invocation + // in Radarr to acquire and log version information. + var compatibility = scope.Resolve(); + await compatibility.Capabilities.LastAsync(); + // ReSharper disable InvertIf if (config.QualityDefinition != null) diff --git a/src/Recyclarr/CompositionRoot.cs b/src/Recyclarr/CompositionRoot.cs index 6a9d3329..70de5324 100644 --- a/src/Recyclarr/CompositionRoot.cs +++ b/src/Recyclarr/CompositionRoot.cs @@ -18,6 +18,7 @@ using TrashLib.Services.Common; using TrashLib.Services.CustomFormat; using TrashLib.Services.Radarr; using TrashLib.Services.Sonarr; +using TrashLib.Services.System; using TrashLib.Startup; using VersionControl; using YamlDotNet.Serialization; @@ -44,6 +45,7 @@ public static class CompositionRoot builder.RegisterModule(); builder.RegisterModule(); builder.RegisterModule(); + builder.RegisterModule(); // Needed for Autofac.Extras.Ordering builder.RegisterSource(); diff --git a/src/TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs b/src/TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs index d8411486..b375125c 100644 --- a/src/TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs +++ b/src/TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs @@ -14,6 +14,8 @@ using TrashLib.Services.Sonarr; using TrashLib.Services.Sonarr.Api; using TrashLib.Services.Sonarr.Api.Objects; using TrashLib.Services.Sonarr.Config; +using TrashLib.Services.System; +using TrashLib.Services.System.Dto; using TrashLib.Startup; namespace TrashLib.Tests.Sonarr; @@ -119,11 +121,11 @@ public class SonarrCompatibilityTest [Test, AutoMockData] public async Task Failure_when_release_profiles_used_with_sonarr_v4( - [Frozen] ISonarrApi api, + [Frozen] ISystemApiService api, [Frozen(Matching.ImplementedInterfaces)] SonarrCompatibility compatibility, SonarrVersionEnforcement enforcement) { - api.GetVersion().Returns(new Version(4, 0)); + api.GetStatus().Returns(new SystemStatus("Sonarr", "4.0")); var config = new SonarrConfiguration { @@ -137,11 +139,11 @@ public class SonarrCompatibilityTest [Test, AutoMockData] public async Task No_failure_when_release_profiles_used_with_sonarr_v3( - [Frozen] ISonarrApi api, + [Frozen] ISystemApiService api, [Frozen(Matching.ImplementedInterfaces)] SonarrCompatibility compatibility, SonarrVersionEnforcement enforcement) { - api.GetVersion().Returns(new Version(3, 9)); + api.GetStatus().Returns(new SystemStatus("Sonarr", "3.9")); var config = new SonarrConfiguration { @@ -155,11 +157,11 @@ public class SonarrCompatibilityTest [Test, AutoMockData] public async Task Failure_when_custom_formats_used_with_sonarr_v3( - [Frozen] ISonarrApi api, + [Frozen] ISystemApiService api, [Frozen(Matching.ImplementedInterfaces)] SonarrCompatibility compatibility, SonarrVersionEnforcement enforcement) { - api.GetVersion().Returns(new Version(3, 9)); + api.GetStatus().Returns(new SystemStatus("Sonarr", "3.9")); var config = new SonarrConfiguration { @@ -173,11 +175,11 @@ public class SonarrCompatibilityTest [Test, AutoMockData] public async Task No_failure_when_custom_formats_used_with_sonarr_v4( - [Frozen] ISonarrApi api, + [Frozen] ISystemApiService api, [Frozen(Matching.ImplementedInterfaces)] SonarrCompatibility compatibility, SonarrVersionEnforcement enforcement) { - api.GetVersion().Returns(new Version(4, 0)); + api.GetStatus().Returns(new SystemStatus("Sonarr", "4.0")); var config = new SonarrConfiguration { diff --git a/src/TrashLib/Services/Radarr/RadarrAutofacModule.cs b/src/TrashLib/Services/Radarr/RadarrAutofacModule.cs index 50554127..af6dcce9 100644 --- a/src/TrashLib/Services/Radarr/RadarrAutofacModule.cs +++ b/src/TrashLib/Services/Radarr/RadarrAutofacModule.cs @@ -15,5 +15,6 @@ public class RadarrAutofacModule : Module builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType(); } } diff --git a/src/TrashLib/Services/Radarr/RadarrCapabilities.cs b/src/TrashLib/Services/Radarr/RadarrCapabilities.cs new file mode 100644 index 00000000..8ec32a51 --- /dev/null +++ b/src/TrashLib/Services/Radarr/RadarrCapabilities.cs @@ -0,0 +1,8 @@ +namespace TrashLib.Services.Radarr; + +public record RadarrCapabilities(Version Version) +{ + public RadarrCapabilities() : this(new Version()) + { + } +} diff --git a/src/TrashLib/Services/Radarr/RadarrCompatibility.cs b/src/TrashLib/Services/Radarr/RadarrCompatibility.cs new file mode 100644 index 00000000..6467b2a9 --- /dev/null +++ b/src/TrashLib/Services/Radarr/RadarrCompatibility.cs @@ -0,0 +1,17 @@ +using Serilog; +using TrashLib.Services.System; + +namespace TrashLib.Services.Radarr; + +public class RadarrCompatibility : ServiceCompatibility +{ + public RadarrCompatibility(ISystemApiService api, ILogger log) + : base(api, log) + { + } + + protected override RadarrCapabilities BuildCapabilitiesObject(Version version) + { + return new RadarrCapabilities(version); + } +} diff --git a/src/TrashLib/Services/Sonarr/Api/ISonarrApi.cs b/src/TrashLib/Services/Sonarr/Api/ISonarrApi.cs index e1284e35..02708039 100644 --- a/src/TrashLib/Services/Sonarr/Api/ISonarrApi.cs +++ b/src/TrashLib/Services/Sonarr/Api/ISonarrApi.cs @@ -10,6 +10,4 @@ public interface ISonarrApi Task> UpdateQualityDefinition( IReadOnlyCollection newQuality); - - Task GetVersion(); } diff --git a/src/TrashLib/Services/Sonarr/Api/SonarrApi.cs b/src/TrashLib/Services/Sonarr/Api/SonarrApi.cs index cea30f5a..df1e270a 100644 --- a/src/TrashLib/Services/Sonarr/Api/SonarrApi.cs +++ b/src/TrashLib/Services/Sonarr/Api/SonarrApi.cs @@ -14,15 +14,6 @@ public class SonarrApi : ISonarrApi _serverInfo = serverInfo; } - public async Task GetVersion() - { - var response = await BaseUrl() - .AppendPathSegment("system/status") - .GetJsonAsync(); - - return new Version(response.version); - } - public async Task> GetTags() { return await BaseUrl() diff --git a/src/TrashLib/Services/Sonarr/SonarrCompatibility.cs b/src/TrashLib/Services/Sonarr/SonarrCompatibility.cs index 31316290..9e615154 100644 --- a/src/TrashLib/Services/Sonarr/SonarrCompatibility.cs +++ b/src/TrashLib/Services/Sonarr/SonarrCompatibility.cs @@ -1,30 +1,19 @@ -using System.Reactive.Concurrency; -using System.Reactive.Linq; using Serilog; -using TrashLib.Services.Sonarr.Api; +using TrashLib.Services.System; namespace TrashLib.Services.Sonarr; -public class SonarrCompatibility : ISonarrCompatibility +public class SonarrCompatibility : ServiceCompatibility, ISonarrCompatibility { - private readonly ILogger _log; - - public SonarrCompatibility(ISonarrApi api, ILogger log) + public SonarrCompatibility(ISystemApiService api, ILogger log) + : base(api, log) { - _log = log; - Capabilities = Observable.FromAsync(async () => await api.GetVersion(), NewThreadScheduler.Default) - .Timeout(TimeSpan.FromSeconds(15)) - .Select(BuildCapabilitiesObject) - .Replay(1) - .AutoConnect(); } - public IObservable Capabilities { get; } public Version MinimumVersion => new("3.0.4.1098"); - private SonarrCapabilities BuildCapabilitiesObject(Version version) + protected override SonarrCapabilities BuildCapabilitiesObject(Version version) { - _log.Debug("Sonarr Version: {Version}", version); return new SonarrCapabilities(version) { SupportsNamedReleaseProfiles = diff --git a/src/TrashLib/Services/System/Dto/SystemStatus.cs b/src/TrashLib/Services/System/Dto/SystemStatus.cs new file mode 100644 index 00000000..8abeff3d --- /dev/null +++ b/src/TrashLib/Services/System/Dto/SystemStatus.cs @@ -0,0 +1,6 @@ +namespace TrashLib.Services.System.Dto; + +public record SystemStatus( + string AppName, + string Version +); diff --git a/src/TrashLib/Services/System/ISystemApiService.cs b/src/TrashLib/Services/System/ISystemApiService.cs new file mode 100644 index 00000000..2e3e7546 --- /dev/null +++ b/src/TrashLib/Services/System/ISystemApiService.cs @@ -0,0 +1,8 @@ +using TrashLib.Services.System.Dto; + +namespace TrashLib.Services.System; + +public interface ISystemApiService +{ + Task GetStatus(); +} diff --git a/src/TrashLib/Services/System/ServiceCompatibility.cs b/src/TrashLib/Services/System/ServiceCompatibility.cs new file mode 100644 index 00000000..38ac711a --- /dev/null +++ b/src/TrashLib/Services/System/ServiceCompatibility.cs @@ -0,0 +1,32 @@ +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using Serilog; +using TrashLib.Services.System.Dto; + +namespace TrashLib.Services.System; + +public abstract class ServiceCompatibility where T : new() +{ + private readonly ILogger _log; + + protected ServiceCompatibility(ISystemApiService api, ILogger log) + { + _log = log; + Capabilities = Observable.FromAsync(async () => await api.GetStatus(), NewThreadScheduler.Default) + .Timeout(TimeSpan.FromSeconds(15)) + .Do(LogServiceInfo) + .Select(x => new Version(x.Version)) + .Select(BuildCapabilitiesObject) + .Replay(1) + .AutoConnect(); + } + + public IObservable Capabilities { get; } + + private void LogServiceInfo(SystemStatus status) + { + _log.Debug("{Service} Version: {Version}", status.AppName, status.Version); + } + + protected abstract T BuildCapabilitiesObject(Version version); +} diff --git a/src/TrashLib/Services/System/SystemApiService.cs b/src/TrashLib/Services/System/SystemApiService.cs new file mode 100644 index 00000000..78ae3449 --- /dev/null +++ b/src/TrashLib/Services/System/SystemApiService.cs @@ -0,0 +1,25 @@ +using Flurl; +using Flurl.Http; +using TrashLib.Config.Services; +using TrashLib.Services.System.Dto; + +namespace TrashLib.Services.System; + +public class SystemApiService : ISystemApiService +{ + private readonly IServerInfo _serverInfo; + + public SystemApiService(IServerInfo serverInfo) + { + _serverInfo = serverInfo; + } + + public async Task GetStatus() + { + return await BaseUrl() + .AppendPathSegment("system/status") + .GetJsonAsync(); + } + + private Url BaseUrl() => _serverInfo.BuildRequest(); +} diff --git a/src/TrashLib/Services/System/SystemServiceAutofacModule.cs b/src/TrashLib/Services/System/SystemServiceAutofacModule.cs new file mode 100644 index 00000000..7b1e9d39 --- /dev/null +++ b/src/TrashLib/Services/System/SystemServiceAutofacModule.cs @@ -0,0 +1,12 @@ +using Autofac; + +namespace TrashLib.Services.System; + +public class SystemServiceAutofacModule : Module +{ + protected override void Load(ContainerBuilder builder) + { + base.Load(builder); + builder.RegisterType().As(); + } +}