From 6184105d3c4747b7714aed820fc67283bffaa97c Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 22 Jun 2014 11:01:22 -0700 Subject: [PATCH] Fixed: Prevent adding a series if the path is the ancestor of another series --- src/NzbDrone.Api/Series/SeriesModule.cs | 26 ++++++++++++------- .../PathExtensionFixture.cs | 12 +++++++-- src/NzbDrone.Common/PathExtensions.cs | 2 +- src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + .../Paths/SeriesAncestorValidator.cs | 25 ++++++++++++++++++ 5 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 src/NzbDrone.Core/Validation/Paths/SeriesAncestorValidator.cs diff --git a/src/NzbDrone.Api/Series/SeriesModule.cs b/src/NzbDrone.Api/Series/SeriesModule.cs index c69bcd5fc..0439fdb27 100644 --- a/src/NzbDrone.Api/Series/SeriesModule.cs +++ b/src/NzbDrone.Api/Series/SeriesModule.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using FluentValidation; +using NzbDrone.Common; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.MediaCover; using NzbDrone.Core.MediaFiles.Events; @@ -40,7 +41,8 @@ namespace NzbDrone.Api.Series PathExistsValidator pathExistsValidator, SeriesPathValidator seriesPathValidator, SeriesExistsValidator seriesExistsValidator, - DroneFactoryValidator droneFactoryValidator + DroneFactoryValidator droneFactoryValidator, + SeriesAncestorValidator seriesAncestorValidator ) : base(commandExecutor) { @@ -59,17 +61,21 @@ namespace NzbDrone.Api.Series SharedValidator.RuleFor(s => s.QualityProfileId).ValidId(); - PutValidator.RuleFor(s => s.Path) - .Cascade(CascadeMode.StopOnFirstFailure) - .IsValidPath() - .SetValidator(rootFolderValidator) - .SetValidator(seriesPathValidator) - .SetValidator(droneFactoryValidator); - - PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => String.IsNullOrEmpty(s.RootFolderPath)); - PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => String.IsNullOrEmpty(s.Path)); + SharedValidator.RuleFor(s => s.Path) + .Cascade(CascadeMode.StopOnFirstFailure) + .IsValidPath() + .SetValidator(rootFolderValidator) + .SetValidator(seriesPathValidator) + .SetValidator(droneFactoryValidator) + .SetValidator(seriesAncestorValidator) + .When(s => !s.Path.IsNullOrWhiteSpace()); + + PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace()); + PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace()); PostValidator.RuleFor(s => s.Title).NotEmpty(); PostValidator.RuleFor(s => s.TvdbId).GreaterThan(0).SetValidator(seriesExistsValidator); + + PutValidator.RuleFor(s => s.Path).IsValidPath(); } private void PopulateAlternativeTitles(List resources) diff --git a/src/NzbDrone.Common.Test/PathExtensionFixture.cs b/src/NzbDrone.Common.Test/PathExtensionFixture.cs index 2564d676a..4b8c355dd 100644 --- a/src/NzbDrone.Common.Test/PathExtensionFixture.cs +++ b/src/NzbDrone.Common.Test/PathExtensionFixture.cs @@ -12,6 +12,7 @@ namespace NzbDrone.Common.Test [TestFixture] public class PathExtensionFixture : TestBase { + private string _parent = @"C:\Test".AsOsAgnostic(); private IAppFolderInfo GetIAppDirectoryInfo() { @@ -86,8 +87,6 @@ namespace NzbDrone.Common.Test first.AsOsAgnostic().PathEquals(second.AsOsAgnostic()).Should().BeFalse(); } - private string _parent = @"C:\Test".AsOsAgnostic(); - [Test] public void should_return_false_when_not_a_child() { @@ -137,6 +136,15 @@ namespace NzbDrone.Common.Test parentPath.IsParentPath(childPath).Should().Be(expectedResult); } + [Test] + [Ignore] + public void should_not_be_parent_when_it_is_grandparent() + { + var path = Path.Combine(_parent, "parent", "child"); + + _parent.IsParentPath(path).Should().BeFalse(); + } + [Test] public void normalize_path_exception_empty() { diff --git a/src/NzbDrone.Common/PathExtensions.cs b/src/NzbDrone.Common/PathExtensions.cs index 053a22edb..7598f84f8 100644 --- a/src/NzbDrone.Common/PathExtensions.cs +++ b/src/NzbDrone.Common/PathExtensions.cs @@ -47,7 +47,7 @@ namespace NzbDrone.Common { if (!parentPath.IsParentPath(childPath)) { - throw new NzbDrone.Common.Exceptions.NotParentException("{0} is not a child of {1}", childPath, parentPath); + throw new Exceptions.NotParentException("{0} is not a child of {1}", childPath, parentPath); } return childPath.Substring(parentPath.Length).Trim(Path.DirectorySeparatorChar); diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 63c970219..8b70b8ca6 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -709,6 +709,7 @@ + diff --git a/src/NzbDrone.Core/Validation/Paths/SeriesAncestorValidator.cs b/src/NzbDrone.Core/Validation/Paths/SeriesAncestorValidator.cs new file mode 100644 index 000000000..c050b9e82 --- /dev/null +++ b/src/NzbDrone.Core/Validation/Paths/SeriesAncestorValidator.cs @@ -0,0 +1,25 @@ +using System.Linq; +using FluentValidation.Validators; +using NzbDrone.Common; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Validation.Paths +{ + public class SeriesAncestorValidator : PropertyValidator + { + private readonly ISeriesService _seriesService; + + public SeriesAncestorValidator(ISeriesService seriesService) + : base("Path is an ancestor of an existing path") + { + _seriesService = seriesService; + } + + protected override bool IsValid(PropertyValidatorContext context) + { + if (context.PropertyValue == null) return true; + + return !_seriesService.GetAllSeries().Any(s => context.PropertyValue.ToString().IsParentPath(s.Path)); + } + } +} \ No newline at end of file