From 272f8e613664b176103dd75c17387311648d8f8e Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Fri, 5 Nov 2021 18:46:56 +0100 Subject: [PATCH] Restored backward compat of Release Profile POST api --- src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj | 1 + .../ReleaseProfiles/ReleaseProfilesFixture.cs | 58 +++++++++++++++++++ .../Profiles/Release/ReleaseProfileModule.cs | 2 +- .../Release/ReleaseProfileResource.cs | 46 ++++++++++++--- 4 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 src/NzbDrone.Api.Test/v3/ReleaseProfiles/ReleaseProfilesFixture.cs diff --git a/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj b/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj index 12a3c40f5..88f16cc3a 100644 --- a/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj +++ b/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj @@ -9,6 +9,7 @@ + diff --git a/src/NzbDrone.Api.Test/v3/ReleaseProfiles/ReleaseProfilesFixture.cs b/src/NzbDrone.Api.Test/v3/ReleaseProfiles/ReleaseProfilesFixture.cs new file mode 100644 index 000000000..916f0387e --- /dev/null +++ b/src/NzbDrone.Api.Test/v3/ReleaseProfiles/ReleaseProfilesFixture.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Common.Serializer; +using Sonarr.Api.V3.Profiles.Release; +using Sonarr.Http.REST; + +namespace NzbDrone.Api.Test.v3.ReleaseProfiles +{ + [TestFixture] + public class ReleaseProfilesFixture + { + [Test] + public void should_deserialize_releaseprofile_v3_ignored_null() + { + var resource = Json.Deserialize("{ \"ignored\": null, \"required\": null }"); + + var model = resource.ToModel(); + + model.Ignored.Should().BeEquivalentTo(); + model.Required.Should().BeEquivalentTo(); + } + + [Test] + public void should_deserialize_releaseprofile_v3_ignored_string() + { + var resource = Json.Deserialize("{ \"ignored\": \"testa,testb\", \"required\": \"testc,testd\" }"); + + var model = resource.ToModel(); + + model.Ignored.Should().BeEquivalentTo("testa", "testb"); + model.Required.Should().BeEquivalentTo("testc", "testd"); + } + + [Test] + public void should_deserialize_releaseprofile_v3_ignored_string_array() + { + var resource = Json.Deserialize("{ \"ignored\": [ \"testa\", \"testb\" ], \"required\": [ \"testc\", \"testd\" ] }"); + + var model = resource.ToModel(); + + model.Ignored.Should().BeEquivalentTo("testa", "testb"); + model.Required.Should().BeEquivalentTo("testc", "testd"); + } + + [Test] + public void should_throw_with_bad_releaseprofile_v3_ignored_type() + { + var resource = Json.Deserialize("{ \"ignored\": {} }"); + + Assert.Throws(() => resource.ToModel()); + } + } +} diff --git a/src/Sonarr.Api.V3/Profiles/Release/ReleaseProfileModule.cs b/src/Sonarr.Api.V3/Profiles/Release/ReleaseProfileModule.cs index d3fed8e8a..1248e697f 100644 --- a/src/Sonarr.Api.V3/Profiles/Release/ReleaseProfileModule.cs +++ b/src/Sonarr.Api.V3/Profiles/Release/ReleaseProfileModule.cs @@ -27,7 +27,7 @@ namespace Sonarr.Api.V3.Profiles.Release SharedValidator.RuleFor(d => d).Custom((restriction, context) => { - if (restriction.Ignored.Empty() && restriction.Required.Empty() && restriction.Preferred.Empty()) + if (restriction.MapIgnored().Empty() && restriction.MapRequired().Empty() && restriction.Preferred.Empty()) { context.AddFailure("'Must contain', 'Must not contain' or 'Preferred' is required"); } diff --git a/src/Sonarr.Api.V3/Profiles/Release/ReleaseProfileResource.cs b/src/Sonarr.Api.V3/Profiles/Release/ReleaseProfileResource.cs index 7ac876a53..1029dfbd7 100644 --- a/src/Sonarr.Api.V3/Profiles/Release/ReleaseProfileResource.cs +++ b/src/Sonarr.Api.V3/Profiles/Release/ReleaseProfileResource.cs @@ -1,5 +1,8 @@ +using System; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using NzbDrone.Core.Profiles.Releases; using Sonarr.Http.REST; @@ -9,8 +12,9 @@ namespace Sonarr.Api.V3.Profiles.Release { public string Name { get; set; } public bool Enabled { get; set; } - public List Required { get; set; } - public List Ignored { get; set; } + // Is List, string or JArray, we accept 'string' with POST for backward compatibility + public object Required { get; set; } + public object Ignored { get; set; } public List> Preferred { get; set; } public bool IncludePreferredWhenRenaming { get; set; } public int IndexerId { get; set; } @@ -18,8 +22,6 @@ namespace Sonarr.Api.V3.Profiles.Release public ReleaseProfileResource() { - Required = new List(); - Ignored = new List(); Preferred = new List>(); Tags = new HashSet(); } @@ -36,8 +38,8 @@ namespace Sonarr.Api.V3.Profiles.Release Id = model.Id, Name = model.Name, Enabled = model.Enabled, - Required = model.Required, - Ignored = model.Ignored, + Required = model.Required ?? new List(), + Ignored = model.Ignored ?? new List(), Preferred = model.Preferred, IncludePreferredWhenRenaming = model.IncludePreferredWhenRenaming, IndexerId = model.IndexerId, @@ -54,8 +56,8 @@ namespace Sonarr.Api.V3.Profiles.Release Id = resource.Id, Name = resource.Name, Enabled = resource.Enabled, - Required = resource.Required, - Ignored = resource.Ignored, + Required = resource.MapRequired(), + Ignored = resource.MapIgnored(), Preferred = resource.Preferred, IncludePreferredWhenRenaming = resource.IncludePreferredWhenRenaming, IndexerId = resource.IndexerId, @@ -67,5 +69,33 @@ namespace Sonarr.Api.V3.Profiles.Release { return models.Select(ToResource).ToList(); } + + public static List MapRequired(this ReleaseProfileResource profile) => ParseArray(profile.Required, "required"); + public static List MapIgnored(this ReleaseProfileResource profile) => ParseArray(profile.Ignored, "ignored"); + + private static List ParseArray(object resource, string title) + { + if (resource == null) + { + return new List(); + } + + if (resource is List list) + { + return list; + } + + if (resource is JArray jarray) + { + return jarray.ToObject>(); + } + + if (resource is string str) + { + return str.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + } + + throw new BadRequestException($"Invalid field {title}, should be string or string array"); + } } }