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");
+ }
}
}