diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a93ddc8..280e9fa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +This release contains **BREAKING CHANGES**. See the [v6.0 Upgrade Guide][breaking6] for required +changes you may need to make. + +[breaking6]: https://recyclarr.dev/wiki/upgrade-guide/v6.0/ + +### Changed + +- **BREAKING**: Minimum required Sonarr version increased to `3.0.9.1549` (Previous minimum version + was `3.0.4.1098`). + ## [5.4.3] - 2023-09-16 ### Changed diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 447d4dfe..9716e234 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -17,7 +17,6 @@ - @@ -64,4 +63,4 @@ - \ No newline at end of file + diff --git a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs b/src/Recyclarr.Cli/Pipelines/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs deleted file mode 100644 index efa9052b..00000000 --- a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Newtonsoft.Json.Linq; -using Recyclarr.Cli.Pipelines.ReleaseProfile.Api.Objects; -using Recyclarr.TrashLib.Config; - -namespace Recyclarr.Cli.Pipelines.ReleaseProfile.Api; - -public interface ISonarrReleaseProfileCompatibilityHandler -{ - Task CompatibleReleaseProfileForSending( - IServiceConfiguration config, - SonarrReleaseProfile profile); - - SonarrReleaseProfile CompatibleReleaseProfileForReceiving(JObject profile); -} diff --git a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/Api/ReleaseProfileApiService.cs b/src/Recyclarr.Cli/Pipelines/ReleaseProfile/Api/ReleaseProfileApiService.cs index 53c02305..55a1ff03 100644 --- a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/Api/ReleaseProfileApiService.cs +++ b/src/Recyclarr.Cli/Pipelines/ReleaseProfile/Api/ReleaseProfileApiService.cs @@ -1,5 +1,4 @@ using Flurl.Http; -using Newtonsoft.Json.Linq; using Recyclarr.Cli.Pipelines.ReleaseProfile.Api.Objects; using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Http; @@ -8,45 +7,32 @@ namespace Recyclarr.Cli.Pipelines.ReleaseProfile.Api; public class ReleaseProfileApiService : IReleaseProfileApiService { - private readonly ISonarrReleaseProfileCompatibilityHandler _profileHandler; private readonly IServiceRequestBuilder _service; - public ReleaseProfileApiService( - ISonarrReleaseProfileCompatibilityHandler profileHandler, - IServiceRequestBuilder service) + public ReleaseProfileApiService(IServiceRequestBuilder service) { - _profileHandler = profileHandler; _service = service; } public async Task UpdateReleaseProfile(IServiceConfiguration config, SonarrReleaseProfile profile) { - var profileToSend = await _profileHandler.CompatibleReleaseProfileForSending(config, profile); await _service.Request(config, "releaseprofile", profile.Id) - .PutJsonAsync(profileToSend); + .PutJsonAsync(profile); } public async Task CreateReleaseProfile( IServiceConfiguration config, SonarrReleaseProfile profile) { - var profileToSend = await _profileHandler.CompatibleReleaseProfileForSending(config, profile); - - var response = await _service.Request(config, "releaseprofile") - .PostJsonAsync(profileToSend) - .ReceiveJson(); - - return _profileHandler.CompatibleReleaseProfileForReceiving(response); + return await _service.Request(config, "releaseprofile") + .PostJsonAsync(profile) + .ReceiveJson(); } public async Task> GetReleaseProfiles(IServiceConfiguration config) { - var response = await _service.Request(config, "releaseprofile") - .GetJsonAsync>(); - - return response - .Select(_profileHandler.CompatibleReleaseProfileForReceiving) - .ToList(); + return await _service.Request(config, "releaseprofile") + .GetJsonAsync>(); } public async Task DeleteReleaseProfile(IServiceConfiguration config, int releaseProfileId) diff --git a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs b/src/Recyclarr.Cli/Pipelines/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs deleted file mode 100644 index 3eb8cd71..00000000 --- a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs +++ /dev/null @@ -1,59 +0,0 @@ -using AutoMapper; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Schema; -using Recyclarr.Cli.Pipelines.ReleaseProfile.Api.Objects; -using Recyclarr.Cli.Pipelines.ReleaseProfile.Api.Schemas; -using Recyclarr.TrashLib.Compatibility.Sonarr; -using Recyclarr.TrashLib.Config; - -namespace Recyclarr.Cli.Pipelines.ReleaseProfile.Api; - -public class SonarrReleaseProfileCompatibilityHandler : ISonarrReleaseProfileCompatibilityHandler -{ - private readonly ILogger _log; - private readonly ISonarrCapabilityFetcher _capabilityFetcher; - private readonly IMapper _mapper; - - public SonarrReleaseProfileCompatibilityHandler( - ILogger log, - ISonarrCapabilityFetcher capabilityFetcher, - IMapper mapper) - { - _log = log; - _capabilityFetcher = capabilityFetcher; - _mapper = mapper; - } - - public async Task CompatibleReleaseProfileForSending( - IServiceConfiguration config, - SonarrReleaseProfile profile) - { - var capabilities = await _capabilityFetcher.GetCapabilities(config); - - return capabilities.ArraysNeededForReleaseProfileRequiredAndIgnored - ? profile - : _mapper.Map(profile); - } - - public SonarrReleaseProfile CompatibleReleaseProfileForReceiving(JObject profile) - { - var schema = JSchema.Parse(SonarrReleaseProfileSchema.V2); - if (profile.IsValid(schema, out IList? errorMessages)) - { - return profile.ToObject() - ?? throw new InvalidDataException("SonarrReleaseProfile V2 parsing failed"); - } - - _log.Debug("SonarrReleaseProfile is not a match for V2, proceeding to V1: {Reasons}", errorMessages); - - schema = JSchema.Parse(SonarrReleaseProfileSchema.V1); - if (profile.IsValid(schema, out errorMessages)) - { - // This will throw if there's an issue during mapping. - return _mapper.Map(profile.ToObject()); - } - - throw new InvalidDataException( - $"SonarrReleaseProfile expected, but no supported schema detected: {errorMessages}"); - } -} diff --git a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/ReleaseProfileAutofacModule.cs b/src/Recyclarr.Cli/Pipelines/ReleaseProfile/ReleaseProfileAutofacModule.cs index 1f9828f7..f23e6c1b 100644 --- a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/ReleaseProfileAutofacModule.cs +++ b/src/Recyclarr.Cli/Pipelines/ReleaseProfile/ReleaseProfileAutofacModule.cs @@ -17,9 +17,6 @@ public class ReleaseProfileAutofacModule : Module builder.RegisterType().As(); builder.RegisterType(); - builder.RegisterType() - .As(); - builder.RegisterAggregateService(); builder.RegisterType(); builder.RegisterType(); diff --git a/src/Recyclarr.TrashLib.Config/Parsing/BackwardCompatibility/ResetUnmatchedScoresYamlTypeConverter.cs b/src/Recyclarr.TrashLib.Config/Parsing/BackwardCompatibility/ResetUnmatchedScoresYamlTypeConverter.cs deleted file mode 100644 index a6842768..00000000 --- a/src/Recyclarr.TrashLib.Config/Parsing/BackwardCompatibility/ResetUnmatchedScoresYamlTypeConverter.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.ComponentModel; -using System.Globalization; - -namespace Recyclarr.TrashLib.Config.Parsing.BackwardCompatibility; - -public class ResetUnmatchedScoresYamlTypeConverter : TypeConverter -{ - public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) - { - return sourceType == typeof(bool) || sourceType == typeof(string); - } - - public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) - { - var enabledFlag = Convert.ToBoolean(value); - return new ResetUnmatchedScoresConfigYaml - { - FromBool = true, - Enabled = enabledFlag - }; - } -} diff --git a/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilities.cs b/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilities.cs index e8602c9a..6d79ac10 100644 --- a/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilities.cs +++ b/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilities.cs @@ -2,17 +2,9 @@ namespace Recyclarr.TrashLib.Compatibility.Sonarr; public record SonarrCapabilities { - public static Version MinimumVersion { get; } = new("3.0.4.1098"); + public static Version MinimumVersion { get; } = new("3.0.9.1549"); public Version Version { get; init; } = new(); - public bool SupportsNamedReleaseProfiles { get; init; } - - // Background: Issue #16 filed which points to a backward-breaking API - // change made in Sonarr at commit [deed85d2f]. - // - // [deed85d2f]: https://github.com/Sonarr/Sonarr/commit/deed85d2f9147e6180014507ef4f5af3695b0c61 - public bool ArraysNeededForReleaseProfileRequiredAndIgnored { get; init; } - public bool SupportsCustomFormats { get; init; } } diff --git a/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilityEnforcer.cs b/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilityEnforcer.cs index 60924cdb..dafc3d76 100644 --- a/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilityEnforcer.cs +++ b/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilityEnforcer.cs @@ -17,7 +17,7 @@ public class SonarrCapabilityEnforcer { var capabilities = await _capabilityFetcher.GetCapabilities(config); - if (!capabilities.SupportsNamedReleaseProfiles) + if (capabilities.Version < SonarrCapabilities.MinimumVersion) { throw new ServiceIncompatibilityException( $"Your Sonarr version {capabilities.Version} does not meet the minimum " + diff --git a/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilityFetcher.cs b/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilityFetcher.cs index fd481c3a..a8b80f83 100644 --- a/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilityFetcher.cs +++ b/src/Recyclarr.TrashLib/Compatibility/Sonarr/SonarrCapabilityFetcher.cs @@ -15,12 +15,6 @@ public class SonarrCapabilityFetcher : ServiceCapabilityFetcher= SonarrCapabilities.MinimumVersion, - - ArraysNeededForReleaseProfileRequiredAndIgnored = - version >= new Version("3.0.6.1355"), - SupportsCustomFormats = version >= new Version(4, 0) }; diff --git a/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj b/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj index ed3d5f90..c08b7b26 100644 --- a/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj +++ b/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj @@ -11,7 +11,6 @@ - diff --git a/src/tests/Recyclarr.Cli.Tests/Pipelines/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandlerTest.cs b/src/tests/Recyclarr.Cli.Tests/Pipelines/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandlerTest.cs deleted file mode 100644 index 22efe868..00000000 --- a/src/tests/Recyclarr.Cli.Tests/Pipelines/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandlerTest.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Autofac; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Serialization; -using Recyclarr.Cli.Pipelines.ReleaseProfile.Api; -using Recyclarr.Cli.Pipelines.ReleaseProfile.Api.Objects; -using Recyclarr.Cli.TestLibrary; -using Recyclarr.TestLibrary.Autofac; -using Recyclarr.TrashLib.Compatibility.Sonarr; -using Recyclarr.TrashLib.Config; - -namespace Recyclarr.Cli.Tests.Pipelines.ReleaseProfile.Api; - -[TestFixture] -[Parallelizable(ParallelScope.All)] -public class SonarrReleaseProfileCompatibilityHandlerTest : CliIntegrationFixture -{ - private static JObject SerializeJson(T obj) - { - JsonSerializerSettings jsonSettings = new() - { - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - return JObject.Parse(JsonConvert.SerializeObject(obj, jsonSettings)); - } - - protected override void RegisterTypes(ContainerBuilder builder) - { - base.RegisterTypes(builder); - builder.RegisterMockFor(); - } - - [Test] - public void Receive_v1_to_v2() - { - var sut = Resolve(); - var dataV1 = new SonarrReleaseProfileV1 {Ignored = "one,two,three"}; - - var result = sut.CompatibleReleaseProfileForReceiving(SerializeJson(dataV1)); - - result.Should().BeEquivalentTo(new SonarrReleaseProfile - { - Ignored = new List {"one", "two", "three"} - }); - } - - [Test] - public void Receive_v2_to_v2() - { - var sut = Resolve(); - var dataV2 = new SonarrReleaseProfile {Ignored = new List {"one", "two", "three"}}; - - var result = sut.CompatibleReleaseProfileForReceiving(SerializeJson(dataV2)); - - result.Should().BeEquivalentTo(dataV2); - } - - [Test] - public async Task Send_v2_to_v1() - { - var capabilityChecker = Resolve(); - capabilityChecker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities - { - ArraysNeededForReleaseProfileRequiredAndIgnored = false - }); - - var sut = Resolve(); - var data = new SonarrReleaseProfile {Ignored = new List {"one", "two", "three"}}; - - var result = await sut.CompatibleReleaseProfileForSending(Substitute.For(), data); - - result.Should().BeEquivalentTo(new SonarrReleaseProfileV1 {Ignored = "one,two,three"}); - } - - [Test] - public async Task Send_v2_to_v2() - { - var capabilityChecker = Resolve(); - capabilityChecker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities - { - ArraysNeededForReleaseProfileRequiredAndIgnored = true - }); - - var sut = Resolve(); - var data = new SonarrReleaseProfile {Ignored = new List {"one", "two", "three"}}; - - var result = await sut.CompatibleReleaseProfileForSending(Substitute.For(), data); - - result.Should().BeEquivalentTo(data); - } -} diff --git a/src/tests/Recyclarr.TrashLib.Tests/Compatibility/Sonarr/SonarrCapabilityEnforcerTest.cs b/src/tests/Recyclarr.TrashLib.Tests/Compatibility/Sonarr/SonarrCapabilityEnforcerTest.cs index b60dd1dd..2df6ed6b 100644 --- a/src/tests/Recyclarr.TrashLib.Tests/Compatibility/Sonarr/SonarrCapabilityEnforcerTest.cs +++ b/src/tests/Recyclarr.TrashLib.Tests/Compatibility/Sonarr/SonarrCapabilityEnforcerTest.cs @@ -15,10 +15,11 @@ public class SonarrCapabilityEnforcerTest SonarrCapabilityEnforcer sut) { var config = NewConfig.Sonarr(); + var min = SonarrCapabilities.MinimumVersion; fetcher.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities { - SupportsNamedReleaseProfiles = false + Version = new Version(min.Major, min.Minor, min.Build, min.Revision - 1) }); var act = () => sut.Check(config); @@ -41,7 +42,6 @@ public class SonarrCapabilityEnforcerTest fetcher.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities { - SupportsNamedReleaseProfiles = true, SupportsCustomFormats = true }); @@ -65,7 +65,6 @@ public class SonarrCapabilityEnforcerTest fetcher.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities { - SupportsNamedReleaseProfiles = true, SupportsCustomFormats = false }); @@ -95,7 +94,6 @@ public class SonarrCapabilityEnforcerTest fetcher.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities { - SupportsNamedReleaseProfiles = true, SupportsCustomFormats = false });