Fixed: Commas in Must (Not) Contain regex

Closes #4672
pull/4677/head
Mark McDowall 3 years ago
parent f6fbd3cfee
commit ada01a1116

@ -46,13 +46,13 @@ class TextTagInputConnector extends Component {
// to oddities with restrictions (as an example). // to oddities with restrictions (as an example).
const newValue = [...valueArray]; const newValue = [...valueArray];
const newTags = split(tag.name); const newTags = tag.name.startsWith('/') ? [tag.name] : split(tag.name);
newTags.forEach((newTag) => { newTags.forEach((newTag) => {
newValue.push(newTag.trim()); newValue.push(newTag.trim());
}); });
onChange({ name, value: newValue.join(',') }); onChange({ name, value: newValue });
} }
onTagDelete = ({ index }) => { onTagDelete = ({ index }) => {

@ -13,7 +13,7 @@ import FormLabel from 'Components/Form/FormLabel';
import FormInputGroup from 'Components/Form/FormInputGroup'; import FormInputGroup from 'Components/Form/FormInputGroup';
import styles from './EditReleaseProfileModalContent.css'; import styles from './EditReleaseProfileModalContent.css';
const tagInputDelimiters = ['Tab', 'Enter', ',']; const tagInputDelimiters = ['Tab', 'Enter'];
function EditReleaseProfileModalContent(props) { function EditReleaseProfileModalContent(props) {
const { const {

@ -90,7 +90,7 @@ class ReleaseProfile extends Component {
<div> <div>
{ {
split(required).map((item) => { required.map((item) => {
if (!item) { if (!item) {
return null; return null;
} }
@ -126,7 +126,7 @@ class ReleaseProfile extends Component {
<div> <div>
{ {
split(ignored).map((item) => { ignored.map((item) => {
if (!item) { if (!item) {
return null; return null;
} }
@ -195,8 +195,8 @@ ReleaseProfile.propTypes = {
id: PropTypes.number.isRequired, id: PropTypes.number.isRequired,
name: PropTypes.string, name: PropTypes.string,
enabled: PropTypes.bool.isRequired, enabled: PropTypes.bool.isRequired,
required: PropTypes.string.isRequired, required: PropTypes.arrayOf(PropTypes.string).isRequired,
ignored: PropTypes.string.isRequired, ignored: PropTypes.arrayOf(PropTypes.string).isRequired,
preferred: PropTypes.arrayOf(PropTypes.object).isRequired, preferred: PropTypes.arrayOf(PropTypes.object).isRequired,
tags: PropTypes.arrayOf(PropTypes.number).isRequired, tags: PropTypes.arrayOf(PropTypes.number).isRequired,
indexerId: PropTypes.number.isRequired, indexerId: PropTypes.number.isRequired,

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Profiles.Releases; using NzbDrone.Core.Profiles.Releases;
@ -27,8 +28,8 @@ namespace NzbDrone.Api.Restrictions
{ {
Id = model.Id, Id = model.Id,
Required = model.Required, Required = string.Join(",", model.Required),
Ignored = model.Ignored, Ignored = string.Join(",", model.Ignored),
Tags = new HashSet<int>(model.Tags) Tags = new HashSet<int>(model.Tags)
}; };
} }
@ -41,8 +42,8 @@ namespace NzbDrone.Api.Restrictions
{ {
Id = resource.Id, Id = resource.Id,
Required = resource.Required, Required = resource.Required.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
Ignored = resource.Ignored, Ignored = resource.Ignored.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
Tags = new HashSet<int>(resource.Tags) Tags = new HashSet<int>(resource.Tags)
}; };
} }

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -33,7 +34,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.SetConstant<ITermMatcherService>(Mocker.Resolve<TermMatcherService>()); Mocker.SetConstant<ITermMatcherService>(Mocker.Resolve<TermMatcherService>());
} }
private void GivenRestictions(string required, string ignored) private void GivenRestictions(List<string> required, List<string> ignored)
{ {
Mocker.GetMock<IReleaseProfileService>() Mocker.GetMock<IReleaseProfileService>()
.Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>())) .Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>()))
@ -60,7 +61,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_be_true_when_title_contains_one_required_term() public void should_be_true_when_title_contains_one_required_term()
{ {
GivenRestictions("WEBRip", null); GivenRestictions(new List<string> { "WEBRip" }, null);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
} }
@ -68,7 +69,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_be_false_when_title_does_not_contain_any_required_terms() public void should_be_false_when_title_does_not_contain_any_required_terms()
{ {
GivenRestictions("doesnt,exist", null); GivenRestictions(new List<string> { "doesnt", "exist" }, null);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
} }
@ -76,7 +77,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_be_true_when_title_does_not_contain_any_ignored_terms() public void should_be_true_when_title_does_not_contain_any_ignored_terms()
{ {
GivenRestictions(null, "ignored"); GivenRestictions(null, new List<string> { "ignored" });
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
} }
@ -84,7 +85,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_be_false_when_title_contains_one_anded_ignored_terms() public void should_be_false_when_title_contains_one_anded_ignored_terms()
{ {
GivenRestictions(null, "edited"); GivenRestictions(null, new List<string> { "edited" });
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
} }
@ -95,7 +96,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[TestCase("X264,NOTTHERE")] [TestCase("X264,NOTTHERE")]
public void should_ignore_case_when_matching_required(string required) public void should_ignore_case_when_matching_required(string required)
{ {
GivenRestictions(required, null); GivenRestictions(required.Split(',').ToList(), null);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
} }
@ -106,7 +107,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[TestCase("X264,NOTTHERE")] [TestCase("X264,NOTTHERE")]
public void should_ignore_case_when_matching_ignored(string ignored) public void should_ignore_case_when_matching_ignored(string ignored)
{ {
GivenRestictions(null, ignored); GivenRestictions(null, ignored.Split(',').ToList());
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
} }
@ -120,7 +121,11 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>())) .Setup(s => s.EnabledForTags(It.IsAny<HashSet<int>>(), It.IsAny<int>()))
.Returns(new List<ReleaseProfile> .Returns(new List<ReleaseProfile>
{ {
new ReleaseProfile { Required = "x264", Ignored = "www.Speed.cd" } new ReleaseProfile
{
Required = new List<string> { "x264" },
Ignored = new List<string> { "www.Speed.cd" }
}
}); });
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
@ -132,7 +137,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[TestCase(@"/\.WEB/", true)] [TestCase(@"/\.WEB/", true)]
public void should_match_perl_regex(string pattern, bool expected) public void should_match_perl_regex(string pattern, bool expected)
{ {
GivenRestictions(pattern, null); GivenRestictions(pattern.Split(',').ToList(), null);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().Be(expected); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().Be(expected);
} }

@ -0,0 +1,48 @@
using System;
using System.Data;
using System.Linq;
using FluentMigrator;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(162)]
public class release_profile_to_array : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(ChangeRequiredIgnoredTypes);
}
private void ChangeRequiredIgnoredTypes(IDbConnection conn, IDbTransaction tran)
{
using (var getEmailCmd = conn.CreateCommand())
{
getEmailCmd.Transaction = tran;
getEmailCmd.CommandText = "SELECT Id, Required, Ignored FROM ReleaseProfiles";
using (var reader = getEmailCmd.ExecuteReader())
{
while (reader.Read())
{
var id = reader.GetInt32(0);
var required = reader.GetString(1).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var ignored = reader.GetString(2).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
using (var updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE ReleaseProfiles SET Required = ?, Ignored = ? WHERE Id = ?";
updateCmd.AddParameter(required.ToJson());
updateCmd.AddParameter(ignored.ToJson());
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}

@ -32,12 +32,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
var title = subject.Release.Title; var title = subject.Release.Title;
var releaseProfiles = _releaseProfileService.EnabledForTags(subject.Series.Tags, subject.Release.IndexerId); var releaseProfiles = _releaseProfileService.EnabledForTags(subject.Series.Tags, subject.Release.IndexerId);
var required = releaseProfiles.Where(r => r.Required.IsNotNullOrWhiteSpace()); var required = releaseProfiles.Where(r => r.Required.Any());
var ignored = releaseProfiles.Where(r => r.Ignored.IsNotNullOrWhiteSpace()); var ignored = releaseProfiles.Where(r => r.Ignored.Any());
foreach (var r in required) foreach (var r in required)
{ {
var requiredTerms = r.Required.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList(); var requiredTerms = r.Required;
var foundTerms = ContainsAny(requiredTerms, title); var foundTerms = ContainsAny(requiredTerms, title);
if (foundTerms.Empty()) if (foundTerms.Empty())
@ -50,7 +50,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
foreach (var r in ignored) foreach (var r in ignored)
{ {
var ignoredTerms = r.Ignored.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); var ignoredTerms = r.Ignored;
var foundTerms = ContainsAny(ignoredTerms, title); var foundTerms = ContainsAny(ignoredTerms, title);
if (foundTerms.Any()) if (foundTerms.Any())

@ -7,8 +7,8 @@ namespace NzbDrone.Core.Profiles.Releases
{ {
public string Name { get; set; } public string Name { get; set; }
public bool Enabled { get; set; } public bool Enabled { get; set; }
public string Required { get; set; } public List<string> Required { get; set; }
public string Ignored { get; set; } public List<string> Ignored { get; set; }
public List<KeyValuePair<string, int>> Preferred { get; set; } public List<KeyValuePair<string, int>> Preferred { get; set; }
public bool IncludePreferredWhenRenaming { get; set; } public bool IncludePreferredWhenRenaming { get; set; }
public int IndexerId { get; set; } public int IndexerId { get; set; }
@ -17,6 +17,8 @@ namespace NzbDrone.Core.Profiles.Releases
public ReleaseProfile() public ReleaseProfile()
{ {
Enabled = true; Enabled = true;
Required = new List<string>();
Ignored = new List<string>();
Preferred = new List<KeyValuePair<string, int>>(); Preferred = new List<KeyValuePair<string, int>>();
IncludePreferredWhenRenaming = true; IncludePreferredWhenRenaming = true;
Tags = new HashSet<int>(); Tags = new HashSet<int>();

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation; using FluentValidation;
using FluentValidation.Results;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Profiles.Releases; using NzbDrone.Core.Profiles.Releases;
@ -28,7 +27,7 @@ namespace Sonarr.Api.V3.Profiles.Release
SharedValidator.RuleFor(d => d).Custom((restriction, context) => SharedValidator.RuleFor(d => d).Custom((restriction, context) =>
{ {
if (restriction.Ignored.IsNullOrWhiteSpace() && restriction.Required.IsNullOrWhiteSpace() && restriction.Preferred.Empty()) if (restriction.Ignored.Empty() && restriction.Required.Empty() && restriction.Preferred.Empty())
{ {
context.AddFailure("'Must contain', 'Must not contain' or 'Preferred' is required"); context.AddFailure("'Must contain', 'Must not contain' or 'Preferred' is required");
} }

@ -9,8 +9,8 @@ namespace Sonarr.Api.V3.Profiles.Release
{ {
public string Name { get; set; } public string Name { get; set; }
public bool Enabled { get; set; } public bool Enabled { get; set; }
public string Required { get; set; } public List<string> Required { get; set; }
public string Ignored { get; set; } public List<string> Ignored { get; set; }
public List<KeyValuePair<string, int>> Preferred { get; set; } public List<KeyValuePair<string, int>> Preferred { get; set; }
public bool IncludePreferredWhenRenaming { get; set; } public bool IncludePreferredWhenRenaming { get; set; }
public int IndexerId { get; set; } public int IndexerId { get; set; }

Loading…
Cancel
Save