From a2536deef0a3cfe064066b2aa2a0b3b15f95772a Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Fri, 25 Mar 2016 01:56:29 +0100 Subject: [PATCH] Fixed: Significantly improved api performance. --- .../MappingTests/ResourceMappingFixture.cs | 168 ------------------ .../NzbDrone.Api.Test.csproj | 4 - src/NzbDrone.Api.Test/packages.config | 1 - src/NzbDrone.Api/Blacklist/BlacklistModule.cs | 5 +- .../Blacklist/BlacklistResource.cs | 24 +++ src/NzbDrone.Api/Calendar/CalendarModule.cs | 2 +- .../ClientSchema/SchemaBuilder.cs | 12 +- src/NzbDrone.Api/Commands/CommandModule.cs | 13 +- src/NzbDrone.Api/Commands/CommandResource.cs | 41 ++++- .../Config/DownloadClientConfigModule.cs | 5 + .../Config/DownloadClientConfigResource.cs | 20 +++ src/NzbDrone.Api/Config/HostConfigModule.cs | 5 +- src/NzbDrone.Api/Config/HostConfigResource.cs | 38 +++- .../Config/IndexerConfigModule.cs | 8 +- .../Config/IndexerConfigResource.cs | 14 ++ .../Config/MediaManagementConfigModule.cs | 5 + .../Config/MediaManagementConfigResource.cs | 26 +++ src/NzbDrone.Api/Config/NamingConfigModule.cs | 16 +- .../Config/NamingConfigResource.cs | 54 ++++++ .../Config/NzbDroneConfigModule.cs | 6 +- src/NzbDrone.Api/Config/UiConfigModule.cs | 36 +--- src/NzbDrone.Api/Config/UiConfigResource.cs | 20 +++ src/NzbDrone.Api/DiskSpace/DiskSpaceModule.cs | 8 +- .../DiskSpace/DiskSpaceResource.cs | 16 ++ .../DownloadClient/DownloadClientModule.cs | 19 +- .../EpisodeFiles/EpisodeFileModule.cs | 20 +-- .../EpisodeFiles/EpisodeFileResource.cs | 44 +++++ src/NzbDrone.Api/Episodes/EpisodeModule.cs | 7 +- .../Episodes/EpisodeModuleWithSignalR.cs | 60 ++++--- src/NzbDrone.Api/Episodes/EpisodeResource.cs | 45 ++++- .../Episodes/RenameEpisodeModule.cs | 4 +- .../Episodes/RenameEpisodeResource.cs | 24 +++ src/NzbDrone.Api/Extensions/LazyExtensions.cs | 55 ------ src/NzbDrone.Api/Health/HealthModule.cs | 2 +- src/NzbDrone.Api/Health/HealthResource.cs | 24 +++ src/NzbDrone.Api/History/HistoryModule.cs | 15 +- src/NzbDrone.Api/History/HistoryResource.cs | 29 ++- src/NzbDrone.Api/Indexers/IndexerModule.cs | 22 ++- .../Indexers/ReleaseModuleBase.cs | 31 +--- .../Indexers/ReleasePushModule.cs | 5 +- src/NzbDrone.Api/Indexers/ReleaseResource.cs | 101 ++++++++++- src/NzbDrone.Api/Logs/LogModule.cs | 5 +- src/NzbDrone.Api/Logs/LogResource.cs | 21 ++- .../ManualImport/ManualImportModule.cs | 2 +- .../ManualImport/ManualImportResource.cs | 32 +++- src/NzbDrone.Api/Mapping/CloneInjection.cs | 125 ------------- src/NzbDrone.Api/Mapping/MappingValidation.cs | 54 ------ .../Mapping/ResourceMappingException.cs | 15 -- .../Mapping/ValueInjectorExtensions.cs | 42 ----- src/NzbDrone.Api/Metadata/MetadataModule.cs | 17 +- .../Notifications/NotificationModule.cs | 33 +++- .../Notifications/NotificationResource.cs | 2 - src/NzbDrone.Api/NzbDrone.Api.csproj | 9 - src/NzbDrone.Api/NzbDroneRestModule.cs | 42 +---- .../NzbDroneRestModuleWithSignalR.cs | 2 +- src/NzbDrone.Api/PagingResource.cs | 18 +- src/NzbDrone.Api/Parse/ParseModule.cs | 37 ++-- .../Profiles/Delay/DelayProfileModule.cs | 22 ++- .../Profiles/Delay/DelayProfileResource.cs | 46 +++++ .../Profiles/Delay/DelayProfileValidator.cs | 27 --- .../Profiles/Languages/LanguageResource.cs | 2 +- src/NzbDrone.Api/Profiles/ProfileModule.cs | 20 +-- src/NzbDrone.Api/Profiles/ProfileResource.cs | 62 +++++++ .../Profiles/ProfileSchemaModule.cs | 3 +- src/NzbDrone.Api/ProviderModuleBase.cs | 64 ++++--- .../Qualities/QualityDefinitionModule.cs | 7 +- .../Qualities/QualityDefinitionResource.cs | 48 +++++ src/NzbDrone.Api/Queue/QueueModule.cs | 2 +- src/NzbDrone.Api/Queue/QueueResource.cs | 33 ++++ .../RemotePathMappingModule.cs | 16 +- .../RemotePathMappingResource.cs | 39 ++++ .../Restrictions/RestrictionModule.cs | 29 ++- .../Restrictions/RestrictionResource.cs | 40 +++++ .../RootFolders/RootFolderModule.cs | 9 +- .../RootFolders/RootFolderResource.cs | 37 ++++ .../SeasonPass/SeasonPassModule.cs | 1 - .../Series/AlternateTitleResource.cs | 4 +- src/NzbDrone.Api/Series/SeasonResource.cs | 40 ++++- .../Series/SeasonStatisticsResource.cs | 18 ++ src/NzbDrone.Api/Series/SeriesEditorModule.cs | 9 +- src/NzbDrone.Api/Series/SeriesLookupModule.cs | 3 +- src/NzbDrone.Api/Series/SeriesModule.cs | 31 ++-- src/NzbDrone.Api/Series/SeriesResource.cs | 136 +++++++++++++- src/NzbDrone.Api/Tags/TagModule.cs | 33 ++-- src/NzbDrone.Api/Tags/TagResource.cs | 35 ++++ src/NzbDrone.Api/Update/UpdateModule.cs | 3 +- src/NzbDrone.Api/Update/UpdateResource.cs | 30 ++++ src/NzbDrone.Api/Wanted/CutoffModule.cs | 2 +- src/NzbDrone.Api/Wanted/MissingModule.cs | 2 +- src/NzbDrone.Api/packages.config | 1 - .../QualityUpgradableSpecification.cs | 24 ++- .../Clients/Blackhole/ScanWatchFolder.cs | 4 +- .../Download/DownloadClientFactory.cs | 6 +- .../Download/DownloadClientProvider.cs | 3 +- src/NzbDrone.Core/HealthCheck/HealthCheck.cs | 4 + src/NzbDrone.Core/Indexers/IndexerFactory.cs | 6 +- .../Notifications/NotificationFactory.cs | 6 +- src/NzbDrone.Core/NzbDrone.Core.csproj | 3 - .../Delay/DelayProfileTagInUseValidator.cs | 7 +- .../ThingiProvider/IProviderFactory.cs | 3 +- .../ThingiProvider/ProviderFactory.cs | 14 +- .../Validation/Paths/SeriesPathValidator.cs | 9 +- src/NzbDrone.Core/packages.config | 1 - .../ApiTests/CalendarFixture.cs | 14 +- .../ApiTests/DiskSpaceFixture.cs | 36 ++++ .../ApiTests/DownloadClientFixture.cs | 98 ++++++++-- .../ApiTests/EpisodeFileFixture.cs | 20 +++ .../ApiTests/FileSystemFixture.cs | 128 +++++++++++++ .../ApiTests/HistoryFixture.cs | 1 - .../ApiTests/NamingConfigFixture.cs | 14 +- .../ApiTests/RootFolderFixture.cs | 2 +- .../ApiTests/SeriesFixture.cs | 57 +++--- .../ApiTests/WantedFixture.cs | 111 ++++++++++++ .../Client/ClientBase.cs | 132 ++++++++------ .../Client/CommandClient.cs | 42 +++++ .../IntegrationTestBase.cs | 97 +++++++++- .../NzbDrone.Integration.Test.csproj | 4 + src/NzbDrone.sln | 4 +- 118 files changed, 2195 insertions(+), 1019 deletions(-) delete mode 100644 src/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs delete mode 100644 src/NzbDrone.Api/Extensions/LazyExtensions.cs delete mode 100644 src/NzbDrone.Api/Mapping/CloneInjection.cs delete mode 100644 src/NzbDrone.Api/Mapping/MappingValidation.cs delete mode 100644 src/NzbDrone.Api/Mapping/ResourceMappingException.cs delete mode 100644 src/NzbDrone.Api/Mapping/ValueInjectorExtensions.cs delete mode 100644 src/NzbDrone.Api/Profiles/Delay/DelayProfileValidator.cs create mode 100644 src/NzbDrone.Integration.Test/ApiTests/DiskSpaceFixture.cs create mode 100644 src/NzbDrone.Integration.Test/ApiTests/EpisodeFileFixture.cs create mode 100644 src/NzbDrone.Integration.Test/ApiTests/FileSystemFixture.cs create mode 100644 src/NzbDrone.Integration.Test/ApiTests/WantedFixture.cs diff --git a/src/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs b/src/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs deleted file mode 100644 index cf8432cc8..000000000 --- a/src/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs +++ /dev/null @@ -1,168 +0,0 @@ -using System; -using System.Collections.Generic; -using FizzWare.NBuilder; -using FluentAssertions; -using Marr.Data; -using NUnit.Framework; -using NzbDrone.Api.Commands; -using NzbDrone.Api.Config; -using NzbDrone.Api.Episodes; -using NzbDrone.Api.History; -using NzbDrone.Api.Indexers; -using NzbDrone.Api.Logs; -using NzbDrone.Api.Mapping; -using NzbDrone.Api.Profiles; -using NzbDrone.Api.RootFolders; -using NzbDrone.Api.Series; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Instrumentation; -using NzbDrone.Core.Messaging.Commands; -using NzbDrone.Core.Organizer; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Profiles; -using NzbDrone.Core.Qualities; -using NzbDrone.Core.RootFolders; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Update.Commands; -using NzbDrone.Test.Common; -using System.Linq; - -namespace NzbDrone.Api.Test.MappingTests -{ - [TestFixture] - public class ResourceMappingFixture : TestBase - { - [TestCase(typeof(Core.Tv.Series), typeof(SeriesResource))] - [TestCase(typeof(Episode), typeof(EpisodeResource))] - [TestCase(typeof(RootFolder), typeof(RootFolderResource))] - [TestCase(typeof(NamingConfig), typeof(NamingConfigResource))] -// [TestCase(typeof(IndexerDefinition), typeof(IndexerResource))] //TODO: Ignoring because we don't have a good way to ignore properties with value injector - [TestCase(typeof(ReleaseInfo), typeof(ReleaseResource))] - [TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))] - [TestCase(typeof(DownloadDecision), typeof(ReleaseResource))] - [TestCase(typeof(Core.History.History), typeof(HistoryResource))] - [TestCase(typeof(Profile), typeof(ProfileResource))] - [TestCase(typeof(ProfileQualityItem), typeof(ProfileQualityItemResource))] - [TestCase(typeof(Log), typeof(LogResource))] - [TestCase(typeof(Command), typeof(CommandResource))] - public void matching_fields(Type modelType, Type resourceType) - { - MappingValidation.ValidateMapping(modelType, resourceType); - } - - [Test] - public void should_map_lazy_loaded_values_should_not_be_inject_if_not_loaded() - { - var modelWithLazy = new ModelWithLazy() - { - Guid = new TestLazyLoaded() - }; - - modelWithLazy.InjectTo().Guid.Should().BeEmpty(); - - modelWithLazy.Guid.IsLoaded.Should().BeFalse(); - } - - [Test] - public void should_map_lay_loaded_values_should_be_inject_if_loaded() - { - - var guid = Guid.NewGuid(); - - var modelWithLazy = new ModelWithLazy() - { - Guid = new LazyLoaded(guid) - }; - - modelWithLazy.InjectTo().Guid.Should().Be(guid); - - modelWithLazy.Guid.IsLoaded.Should().BeTrue(); - } - - [Test] - public void should_be_able_to_map_lists() - { - var modelList = Builder.CreateListOfSize(10).Build(); - - var resourceList = modelList.InjectTo>(); - - resourceList.Should().HaveSameCount(modelList); - } - - [Test] - public void should_map_wrapped_models() - { - var modelList = Builder.CreateListOfSize(10).Build().ToList(); - - var wrapper = new TestModelWrapper - { - TestlList = modelList - }; - - wrapper.InjectTo(); - } - - - [Test] - public void should_map_profile() - { - var profileResource = new ProfileResource - { - Cutoff = Quality.WEBDL1080p, - Items = new List { new ProfileQualityItemResource { Quality = Quality.WEBDL1080p, Allowed = true } } - }; - - - profileResource.InjectTo(); - - } - - [Test] - public void should_map_tracked_command() - { - var commandResource = new CommandModel { Body = new ApplicationUpdateCommand() }; - commandResource.InjectTo(); - } - } - - public class ModelWithLazy - { - public LazyLoaded Guid { get; set; } - } - - public class ModelWithNoLazy - { - public Guid Guid { get; set; } - } - - public class TestLazyLoaded : LazyLoaded - { - public override void Prepare(Func dataMapperFactory, object parent) - { - throw new InvalidOperationException(); - } - } - - - public class TestModelWrapper - { - public List TestlList { get; set; } - } - - public class TestResourceWrapper - { - public List TestList { get; set; } - } - - public class TestModel - { - public string Field1 { get; set; } - public string Field2 { get; set; } - } - - public class TestResource - { - public string Field1 { get; set; } - public string Field2 { get; set; } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj b/src/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj index 165975e05..d2562cfb1 100644 --- a/src/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj +++ b/src/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj @@ -63,13 +63,9 @@ ..\packages\Moq.4.0.10827\lib\NET40\Moq.dll - - ..\packages\ValueInjecter.2.3.3\lib\net35\Omu.ValueInjecter.dll - - diff --git a/src/NzbDrone.Api.Test/packages.config b/src/NzbDrone.Api.Test/packages.config index 1c01db85a..c3b7176a0 100644 --- a/src/NzbDrone.Api.Test/packages.config +++ b/src/NzbDrone.Api.Test/packages.config @@ -4,5 +4,4 @@ - \ No newline at end of file diff --git a/src/NzbDrone.Api/Blacklist/BlacklistModule.cs b/src/NzbDrone.Api/Blacklist/BlacklistModule.cs index 7cfdab79d..7799612a7 100644 --- a/src/NzbDrone.Api/Blacklist/BlacklistModule.cs +++ b/src/NzbDrone.Api/Blacklist/BlacklistModule.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Blacklisting; +using System; +using NzbDrone.Core.Blacklisting; using NzbDrone.Core.Datastore; namespace NzbDrone.Api.Blacklist @@ -24,7 +25,7 @@ namespace NzbDrone.Api.Blacklist SortDirection = pagingResource.SortDirection }; - return ApplyToPage(_blacklistService.Paged, pagingSpec); + return ApplyToPage(_blacklistService.Paged, pagingSpec, BlacklistResourceMapper.MapToResource); } private void DeleteBlacklist(int id) diff --git a/src/NzbDrone.Api/Blacklist/BlacklistResource.cs b/src/NzbDrone.Api/Blacklist/BlacklistResource.cs index e213518e4..c3f1c6b1b 100644 --- a/src/NzbDrone.Api/Blacklist/BlacklistResource.cs +++ b/src/NzbDrone.Api/Blacklist/BlacklistResource.cs @@ -20,4 +20,28 @@ namespace NzbDrone.Api.Blacklist public SeriesResource Series { get; set; } } + + public static class BlacklistResourceMapper + { + public static BlacklistResource MapToResource(this Core.Blacklisting.Blacklist model) + { + if (model == null) return null; + + return new BlacklistResource + { + Id = model.Id, + + SeriesId = model.SeriesId, + EpisodeIds = model.EpisodeIds, + SourceTitle = model.SourceTitle, + Quality = model.Quality, + Date = model.Date, + Protocol = model.Protocol, + Indexer = model.Indexer, + Message = model.Message, + + Series = model.Series.ToResource() + }; + } + } } diff --git a/src/NzbDrone.Api/Calendar/CalendarModule.cs b/src/NzbDrone.Api/Calendar/CalendarModule.cs index c89d5f52d..f403b79c7 100644 --- a/src/NzbDrone.Api/Calendar/CalendarModule.cs +++ b/src/NzbDrone.Api/Calendar/CalendarModule.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Api.Calendar if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value); if (queryIncludeUnmonitored.HasValue) includeUnmonitored = Convert.ToBoolean(queryIncludeUnmonitored.Value); - var resources = ToListResource(() => _episodeService.EpisodesBetweenDates(start, end, includeUnmonitored)); + var resources = MapToResource(_episodeService.EpisodesBetweenDates(start, end, includeUnmonitored), true, true); return resources.OrderBy(e => e.AirDateUtc).ToList(); } diff --git a/src/NzbDrone.Api/ClientSchema/SchemaBuilder.cs b/src/NzbDrone.Api/ClientSchema/SchemaBuilder.cs index 4a0dbc82c..0687a1413 100644 --- a/src/NzbDrone.Api/ClientSchema/SchemaBuilder.cs +++ b/src/NzbDrone.Api/ClientSchema/SchemaBuilder.cs @@ -6,7 +6,6 @@ using NzbDrone.Common.EnsureThat; using NzbDrone.Common.Extensions; using NzbDrone.Common.Reflection; using NzbDrone.Core.Annotations; -using Omu.ValueInjecter; namespace NzbDrone.Api.ClientSchema { @@ -56,7 +55,7 @@ namespace NzbDrone.Api.ClientSchema return result.OrderBy(r => r.Order).ToList(); } - public static object ReadFormSchema(List fields, Type targetType, object defaults = null) + public static object ReadFromSchema(List fields, Type targetType) { Ensure.That(targetType, () => targetType).IsNotNull(); @@ -64,11 +63,6 @@ namespace NzbDrone.Api.ClientSchema var target = Activator.CreateInstance(targetType); - if (defaults != null) - { - target.InjectFrom(defaults); - } - foreach (var propertyInfo in properties) { var fieldAttribute = propertyInfo.GetAttribute(false); @@ -146,9 +140,9 @@ namespace NzbDrone.Api.ClientSchema } - public static T ReadFormSchema(List fields) + public static T ReadFromSchema(List fields) { - return (T)ReadFormSchema(fields, typeof(T)); + return (T)ReadFromSchema(fields, typeof(T)); } private static List GetSelectOptions(Type selectOptions) diff --git a/src/NzbDrone.Api/Commands/CommandModule.cs b/src/NzbDrone.Api/Commands/CommandModule.cs index fd9414d53..fcaeef9c4 100644 --- a/src/NzbDrone.Api/Commands/CommandModule.cs +++ b/src/NzbDrone.Api/Commands/CommandModule.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using NzbDrone.Api.Extensions; -using NzbDrone.Api.Mapping; using NzbDrone.Api.Validation; using NzbDrone.Common; using NzbDrone.Core.Datastore.Events; @@ -36,15 +35,13 @@ namespace NzbDrone.Api.Commands private CommandResource GetCommand(int id) { - return _commandQueueManager.Get(id).InjectTo(); + return _commandQueueManager.Get(id).ToResource(); } private int StartCommand(CommandResource commandResource) { - var commandType = - _serviceFactory.GetImplementations(typeof (Command)) - .Single(c => c.Name.Replace("Command", "") - .Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase)); + var commandType = _serviceFactory.GetImplementations(typeof(Command)) + .Single(c => c.Name.Replace("Command", "").Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase)); dynamic command = Request.Body.FromJson(commandType); command.Trigger = CommandTrigger.Manual; @@ -55,14 +52,14 @@ namespace NzbDrone.Api.Commands private List GetStartedCommands() { - return ToListResource(_commandQueueManager.GetStarted()); + return _commandQueueManager.GetStarted().ToResource(); } public void Handle(CommandUpdatedEvent message) { if (message.Command.Body.SendUpdatesToClient) { - BroadcastResourceChange(ModelAction.Updated, message.Command.InjectTo()); + BroadcastResourceChange(ModelAction.Updated, message.Command.ToResource()); } } } diff --git a/src/NzbDrone.Api/Commands/CommandResource.cs b/src/NzbDrone.Api/Commands/CommandResource.cs index 6b33a0b9e..cf09f12ac 100644 --- a/src/NzbDrone.Api/Commands/CommandResource.cs +++ b/src/NzbDrone.Api/Commands/CommandResource.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using NzbDrone.Api.REST; using NzbDrone.Core.Messaging.Commands; @@ -9,7 +11,7 @@ namespace NzbDrone.Api.Commands { public string Name { get; set; } public string Message { get; set; } - public Command Body { get; set; } + public object Body { get; set; } public CommandPriority Priority { get; set; } public CommandStatus Status { get; set; } public DateTime Queued { get; set; } @@ -70,7 +72,7 @@ namespace NzbDrone.Api.Commands { get { - if (Body != null) return Body.SendUpdatesToClient; + if (Body != null) return (Body as Command).SendUpdatesToClient; return false; } @@ -82,7 +84,7 @@ namespace NzbDrone.Api.Commands { get { - if (Body != null) return Body.UpdateScheduledTask; + if (Body != null) return (Body as Command).UpdateScheduledTask; return false; } @@ -92,4 +94,37 @@ namespace NzbDrone.Api.Commands public DateTime? LastExecutionTime { get; set; } } + + public static class CommandResourceMapper + { + public static CommandResource ToResource(this CommandModel model) + { + if (model == null) return null; + + return new CommandResource + { + Id = model.Id, + + Name = model.Name, + Message = model.Message, + Body = model.Body, + Priority = model.Priority, + Status = model.Status, + Queued = model.QueuedAt, + Started = model.StartedAt, + Ended = model.EndedAt, + Duration = model.Duration, + Exception = model.Exception, + Trigger = model.Trigger, + + CompletionMessage = model.Body.CompletionMessage, + LastExecutionTime = model.Body.LastExecutionTime + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/Config/DownloadClientConfigModule.cs b/src/NzbDrone.Api/Config/DownloadClientConfigModule.cs index 0cd0ad86a..183797de6 100644 --- a/src/NzbDrone.Api/Config/DownloadClientConfigModule.cs +++ b/src/NzbDrone.Api/Config/DownloadClientConfigModule.cs @@ -21,5 +21,10 @@ namespace NzbDrone.Api.Config .SetValidator(pathExistsValidator) .When(c => !string.IsNullOrWhiteSpace(c.DownloadedEpisodesFolder)); } + + protected override DownloadClientConfigResource ToResource(IConfigService model) + { + return DownloadClientConfigResourceMapper.ToResource(model); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Config/DownloadClientConfigResource.cs b/src/NzbDrone.Api/Config/DownloadClientConfigResource.cs index 9326f5d62..c4783b65c 100644 --- a/src/NzbDrone.Api/Config/DownloadClientConfigResource.cs +++ b/src/NzbDrone.Api/Config/DownloadClientConfigResource.cs @@ -1,5 +1,6 @@ using System; using NzbDrone.Api.REST; +using NzbDrone.Core.Configuration; namespace NzbDrone.Api.Config { @@ -15,4 +16,23 @@ namespace NzbDrone.Api.Config public bool AutoRedownloadFailed { get; set; } public bool RemoveFailedDownloads { get; set; } } + + public static class DownloadClientConfigResourceMapper + { + public static DownloadClientConfigResource ToResource(IConfigService model) + { + return new DownloadClientConfigResource + { + DownloadedEpisodesFolder = model.DownloadedEpisodesFolder, + DownloadClientWorkingFolders = model.DownloadClientWorkingFolders, + DownloadedEpisodesScanInterval = model.DownloadedEpisodesScanInterval, + + EnableCompletedDownloadHandling = model.EnableCompletedDownloadHandling, + RemoveCompletedDownloads = model.RemoveCompletedDownloads, + + AutoRedownloadFailed = model.AutoRedownloadFailed, + RemoveFailedDownloads = model.RemoveFailedDownloads + }; + } + } } diff --git a/src/NzbDrone.Api/Config/HostConfigModule.cs b/src/NzbDrone.Api/Config/HostConfigModule.cs index 6be688c69..367bf770d 100644 --- a/src/NzbDrone.Api/Config/HostConfigModule.cs +++ b/src/NzbDrone.Api/Config/HostConfigModule.cs @@ -8,7 +8,6 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.Update; using NzbDrone.Core.Validation; using NzbDrone.Core.Validation.Paths; -using Omu.ValueInjecter; namespace NzbDrone.Api.Config { @@ -50,12 +49,10 @@ namespace NzbDrone.Api.Config private HostConfigResource GetHostConfig() { - var resource = new HostConfigResource(); - resource.InjectFrom(_configFileProvider, _configService); + var resource = _configFileProvider.ToResource(_configService); resource.Id = 1; var user = _userService.FindUser(); - if (user != null) { resource.Username = user.Username; diff --git a/src/NzbDrone.Api/Config/HostConfigResource.cs b/src/NzbDrone.Api/Config/HostConfigResource.cs index a100556ff..cecec0f7f 100644 --- a/src/NzbDrone.Api/Config/HostConfigResource.cs +++ b/src/NzbDrone.Api/Config/HostConfigResource.cs @@ -1,6 +1,7 @@ using System; using NzbDrone.Api.REST; using NzbDrone.Core.Authentication; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Update; using NzbDrone.Common.Http.Proxy; @@ -20,7 +21,6 @@ namespace NzbDrone.Api.Config public string LogLevel { get; set; } public string Branch { get; set; } public string ApiKey { get; set; } - public bool Torrent { get; set; } public string SslCertHash { get; set; } public string UrlBase { get; set; } public bool UpdateAutomatically { get; set; } @@ -35,4 +35,40 @@ namespace NzbDrone.Api.Config public string ProxyBypassFilter { get; set; } public bool ProxyBypassLocalAddresses { get; set; } } + + public static class HostConfigResourceMapper + { + public static HostConfigResource ToResource(this IConfigFileProvider model, IConfigService configService) + { + // TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead? + return new HostConfigResource + { + BindAddress = model.BindAddress, + Port = model.Port, + SslPort = model.SslPort, + EnableSsl = model.EnableSsl, + LaunchBrowser = model.LaunchBrowser, + AuthenticationMethod = model.AuthenticationMethod, + AnalyticsEnabled = model.AnalyticsEnabled, + //Username + //Password + LogLevel = model.LogLevel, + Branch = model.Branch, + ApiKey = model.ApiKey, + SslCertHash = model.SslCertHash, + UrlBase = model.UrlBase, + UpdateAutomatically = model.UpdateAutomatically, + UpdateMechanism = model.UpdateMechanism, + UpdateScriptPath = model.UpdateScriptPath, + ProxyEnabled = configService.ProxyEnabled, + ProxyType = configService.ProxyType, + ProxyHostname = configService.ProxyHostname, + ProxyPort = configService.ProxyPort, + ProxyUsername = configService.ProxyUsername, + ProxyPassword = configService.ProxyPassword, + ProxyBypassFilter = configService.ProxyBypassFilter, + ProxyBypassLocalAddresses = configService.ProxyBypassLocalAddresses + }; + } + } } diff --git a/src/NzbDrone.Api/Config/IndexerConfigModule.cs b/src/NzbDrone.Api/Config/IndexerConfigModule.cs index 02f03f958..cfa36815b 100644 --- a/src/NzbDrone.Api/Config/IndexerConfigModule.cs +++ b/src/NzbDrone.Api/Config/IndexerConfigModule.cs @@ -1,4 +1,5 @@ -using FluentValidation; +using System; +using FluentValidation; using NzbDrone.Api.Validation; using NzbDrone.Core.Configuration; @@ -19,5 +20,10 @@ namespace NzbDrone.Api.Config SharedValidator.RuleFor(c => c.RssSyncInterval) .IsValidRssSyncInterval(); } + + protected override IndexerConfigResource ToResource(IConfigService model) + { + return IndexerConfigResourceMapper.ToResource(model); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Config/IndexerConfigResource.cs b/src/NzbDrone.Api/Config/IndexerConfigResource.cs index e1615df30..ed5848c4d 100644 --- a/src/NzbDrone.Api/Config/IndexerConfigResource.cs +++ b/src/NzbDrone.Api/Config/IndexerConfigResource.cs @@ -1,5 +1,6 @@ using System; using NzbDrone.Api.REST; +using NzbDrone.Core.Configuration; namespace NzbDrone.Api.Config { @@ -9,4 +10,17 @@ namespace NzbDrone.Api.Config public int Retention { get; set; } public int RssSyncInterval { get; set; } } + + public static class IndexerConfigResourceMapper + { + public static IndexerConfigResource ToResource(IConfigService model) + { + return new IndexerConfigResource + { + MinimumAge = model.MinimumAge, + Retention = model.Retention, + RssSyncInterval = model.RssSyncInterval, + }; + } + } } diff --git a/src/NzbDrone.Api/Config/MediaManagementConfigModule.cs b/src/NzbDrone.Api/Config/MediaManagementConfigModule.cs index ecdf28ccb..e8d3b9eb4 100644 --- a/src/NzbDrone.Api/Config/MediaManagementConfigModule.cs +++ b/src/NzbDrone.Api/Config/MediaManagementConfigModule.cs @@ -14,5 +14,10 @@ namespace NzbDrone.Api.Config SharedValidator.RuleFor(c => c.FolderChmod).NotEmpty(); SharedValidator.RuleFor(c => c.RecycleBin).IsValidPath().SetValidator(pathExistsValidator).When(c => !string.IsNullOrWhiteSpace(c.RecycleBin)); } + + protected override MediaManagementConfigResource ToResource(IConfigService model) + { + return MediaManagementConfigResourceMapper.ToResource(model); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs b/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs index eefe50ea3..f18c1f731 100644 --- a/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs +++ b/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs @@ -1,5 +1,6 @@ using System; using NzbDrone.Api.REST; +using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaFiles; namespace NzbDrone.Api.Config @@ -22,4 +23,29 @@ namespace NzbDrone.Api.Config public bool CopyUsingHardlinks { get; set; } public bool EnableMediaInfo { get; set; } } + + public static class MediaManagementConfigResourceMapper + { + public static MediaManagementConfigResource ToResource(IConfigService model) + { + return new MediaManagementConfigResource + { + AutoUnmonitorPreviouslyDownloadedEpisodes = model.AutoUnmonitorPreviouslyDownloadedEpisodes, + RecycleBin = model.RecycleBin, + AutoDownloadPropers = model.AutoDownloadPropers, + CreateEmptySeriesFolders = model.CreateEmptySeriesFolders, + FileDate = model.FileDate, + + SetPermissionsLinux = model.SetPermissionsLinux, + FileChmod = model.FileChmod, + FolderChmod = model.FolderChmod, + ChownUser = model.ChownUser, + ChownGroup = model.ChownGroup, + + SkipFreeSpaceCheckWhenImporting = model.SkipFreeSpaceCheckWhenImporting, + CopyUsingHardlinks = model.CopyUsingHardlinks, + EnableMediaInfo = model.EnableMediaInfo, + }; + } + } } diff --git a/src/NzbDrone.Api/Config/NamingConfigModule.cs b/src/NzbDrone.Api/Config/NamingConfigModule.cs index 18408bf1c..b43b22e56 100644 --- a/src/NzbDrone.Api/Config/NamingConfigModule.cs +++ b/src/NzbDrone.Api/Config/NamingConfigModule.cs @@ -7,9 +7,7 @@ using Nancy.Responses; using NzbDrone.Common.Extensions; using NzbDrone.Core.Organizer; using Nancy.ModelBinding; -using NzbDrone.Api.Mapping; using NzbDrone.Api.Extensions; -using Omu.ValueInjecter; namespace NzbDrone.Api.Config { @@ -46,7 +44,7 @@ namespace NzbDrone.Api.Config private void UpdateNamingConfig(NamingConfigResource resource) { - var nameSpec = resource.InjectTo(); + var nameSpec = resource.ToModel(); ValidateFormatResult(nameSpec); _namingConfigService.Save(nameSpec); @@ -55,16 +53,14 @@ namespace NzbDrone.Api.Config private NamingConfigResource GetNamingConfig() { var nameSpec = _namingConfigService.GetConfig(); - var resource = nameSpec.InjectTo(); + var resource = nameSpec.ToResource(); - if (string.IsNullOrWhiteSpace(resource.StandardEpisodeFormat)) + if (resource.StandardEpisodeFormat.IsNotNullOrWhiteSpace()) { - return resource; + var basicConfig = _filenameBuilder.GetBasicNamingConfig(nameSpec); + basicConfig.AddToResource(resource); } - var basicConfig = _filenameBuilder.GetBasicNamingConfig(nameSpec); - resource.InjectFrom(basicConfig); - return resource; } @@ -75,7 +71,7 @@ namespace NzbDrone.Api.Config private JsonResponse GetExamples(NamingConfigResource config) { - var nameSpec = config.InjectTo(); + var nameSpec = config.ToModel(); var sampleResource = new NamingSampleResource(); var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec); diff --git a/src/NzbDrone.Api/Config/NamingConfigResource.cs b/src/NzbDrone.Api/Config/NamingConfigResource.cs index 93665a5f4..2f36652e0 100644 --- a/src/NzbDrone.Api/Config/NamingConfigResource.cs +++ b/src/NzbDrone.Api/Config/NamingConfigResource.cs @@ -1,5 +1,6 @@ using System; using NzbDrone.Api.REST; +using NzbDrone.Core.Organizer; namespace NzbDrone.Api.Config { @@ -20,4 +21,57 @@ namespace NzbDrone.Api.Config public string Separator { get; set; } public string NumberStyle { get; set; } } + + public static class NamingConfigResourceMapper + { + public static NamingConfigResource ToResource(this NamingConfig model) + { + return new NamingConfigResource + { + Id = model.Id, + + RenameEpisodes = model.RenameEpisodes, + ReplaceIllegalCharacters = model.ReplaceIllegalCharacters, + MultiEpisodeStyle = model.MultiEpisodeStyle, + StandardEpisodeFormat = model.StandardEpisodeFormat, + DailyEpisodeFormat = model.DailyEpisodeFormat, + AnimeEpisodeFormat = model.AnimeEpisodeFormat, + SeriesFolderFormat = model.SeriesFolderFormat, + SeasonFolderFormat = model.SeasonFolderFormat + //IncludeSeriesTitle + //IncludeEpisodeTitle + //IncludeQuality + //ReplaceSpaces + //Separator + //NumberStyle + }; + } + + public static void AddToResource(this BasicNamingConfig basicNamingConfig, NamingConfigResource resource) + { + resource.IncludeSeriesTitle = basicNamingConfig.IncludeSeriesTitle; + resource.IncludeEpisodeTitle = basicNamingConfig.IncludeEpisodeTitle; + resource.IncludeQuality = basicNamingConfig.IncludeQuality; + resource.ReplaceSpaces = basicNamingConfig.ReplaceSpaces; + resource.Separator = basicNamingConfig.Separator; + resource.NumberStyle = basicNamingConfig.NumberStyle; + } + + public static NamingConfig ToModel(this NamingConfigResource resource) + { + return new NamingConfig + { + Id = resource.Id, + + RenameEpisodes = resource.RenameEpisodes, + ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters, + MultiEpisodeStyle = resource.MultiEpisodeStyle, + StandardEpisodeFormat = resource.StandardEpisodeFormat, + DailyEpisodeFormat = resource.DailyEpisodeFormat, + AnimeEpisodeFormat = resource.AnimeEpisodeFormat, + SeriesFolderFormat = resource.SeriesFolderFormat, + SeasonFolderFormat = resource.SeasonFolderFormat + }; + } + } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Config/NzbDroneConfigModule.cs b/src/NzbDrone.Api/Config/NzbDroneConfigModule.cs index 64b31014d..e5d324950 100644 --- a/src/NzbDrone.Api/Config/NzbDroneConfigModule.cs +++ b/src/NzbDrone.Api/Config/NzbDroneConfigModule.cs @@ -2,7 +2,6 @@ using System.Reflection; using NzbDrone.Api.REST; using NzbDrone.Core.Configuration; -using Omu.ValueInjecter; namespace NzbDrone.Api.Config { @@ -27,13 +26,14 @@ namespace NzbDrone.Api.Config private TResource GetConfig() { - var resource = new TResource(); - resource.InjectFrom(_configService); + var resource = ToResource(_configService); resource.Id = 1; return resource; } + protected abstract TResource ToResource(IConfigService model); + private TResource GetConfig(int id) { return GetConfig(); diff --git a/src/NzbDrone.Api/Config/UiConfigModule.cs b/src/NzbDrone.Api/Config/UiConfigModule.cs index 782e4ebf7..57ca88930 100644 --- a/src/NzbDrone.Api/Config/UiConfigModule.cs +++ b/src/NzbDrone.Api/Config/UiConfigModule.cs @@ -1,45 +1,21 @@ -using System.Linq; +using System; +using System.Linq; using System.Reflection; using NzbDrone.Core.Configuration; -using Omu.ValueInjecter; namespace NzbDrone.Api.Config { - public class UiConfigModule : NzbDroneRestModule + public class UiConfigModule : NzbDroneConfigModule { - private readonly IConfigService _configService; - public UiConfigModule(IConfigService configService) - : base("/config/ui") + : base(configService) { - _configService = configService; - GetResourceSingle = GetUiConfig; - GetResourceById = GetUiConfig; - UpdateResource = SaveUiConfig; } - private UiConfigResource GetUiConfig() + protected override UiConfigResource ToResource(IConfigService model) { - var resource = new UiConfigResource(); - resource.InjectFrom(_configService); - resource.Id = 1; - - return resource; - } - - private UiConfigResource GetUiConfig(int id) - { - return GetUiConfig(); - } - - private void SaveUiConfig(UiConfigResource resource) - { - var dictionary = resource.GetType() - .GetProperties(BindingFlags.Instance | BindingFlags.Public) - .ToDictionary(prop => prop.Name, prop => prop.GetValue(resource, null)); - - _configService.SaveConfigDictionary(dictionary); + return UiConfigResourceMapper.ToResource(model); } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Config/UiConfigResource.cs b/src/NzbDrone.Api/Config/UiConfigResource.cs index 4c4802e53..d68c6113d 100644 --- a/src/NzbDrone.Api/Config/UiConfigResource.cs +++ b/src/NzbDrone.Api/Config/UiConfigResource.cs @@ -1,5 +1,6 @@ using System; using NzbDrone.Api.REST; +using NzbDrone.Core.Configuration; namespace NzbDrone.Api.Config { @@ -17,4 +18,23 @@ namespace NzbDrone.Api.Config public bool EnableColorImpairedMode { get; set; } } + + public static class UiConfigResourceMapper + { + public static UiConfigResource ToResource(IConfigService model) + { + return new UiConfigResource + { + FirstDayOfWeek = model.FirstDayOfWeek, + CalendarWeekColumnHeader = model.CalendarWeekColumnHeader, + + ShortDateFormat = model.ShortDateFormat, + LongDateFormat = model.LongDateFormat, + TimeFormat = model.TimeFormat, + ShowRelativeDates = model.ShowRelativeDates, + + EnableColorImpairedMode = model.EnableColorImpairedMode, + }; + } + } } diff --git a/src/NzbDrone.Api/DiskSpace/DiskSpaceModule.cs b/src/NzbDrone.Api/DiskSpace/DiskSpaceModule.cs index 5ec9d5215..6b083db51 100644 --- a/src/NzbDrone.Api/DiskSpace/DiskSpaceModule.cs +++ b/src/NzbDrone.Api/DiskSpace/DiskSpaceModule.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using NzbDrone.Core.DiskSpace; namespace NzbDrone.Api.DiskSpace @@ -8,15 +9,16 @@ namespace NzbDrone.Api.DiskSpace private readonly IDiskSpaceService _diskSpaceService; public DiskSpaceModule(IDiskSpaceService diskSpaceService) - :base("diskspace") + : base("diskspace") { _diskSpaceService = diskSpaceService; GetResourceAll = GetFreeSpace; } + public List GetFreeSpace() { - return ToListResource(_diskSpaceService.GetFreeSpace); + return _diskSpaceService.GetFreeSpace().ConvertAll(DiskSpaceResourceMapper.MapToResource); } } } diff --git a/src/NzbDrone.Api/DiskSpace/DiskSpaceResource.cs b/src/NzbDrone.Api/DiskSpace/DiskSpaceResource.cs index c23af6a0a..3dc59288f 100644 --- a/src/NzbDrone.Api/DiskSpace/DiskSpaceResource.cs +++ b/src/NzbDrone.Api/DiskSpace/DiskSpaceResource.cs @@ -10,4 +10,20 @@ namespace NzbDrone.Api.DiskSpace public long FreeSpace { get; set; } public long TotalSpace { get; set; } } + + public static class DiskSpaceResourceMapper + { + public static DiskSpaceResource MapToResource(this Core.DiskSpace.DiskSpace model) + { + if (model == null) return null; + + return new DiskSpaceResource + { + Path = model.Path, + Label = model.Label, + FreeSpace = model.FreeSpace, + TotalSpace = model.TotalSpace + }; + } + } } diff --git a/src/NzbDrone.Api/DownloadClient/DownloadClientModule.cs b/src/NzbDrone.Api/DownloadClient/DownloadClientModule.cs index 78d997739..db3c65182 100644 --- a/src/NzbDrone.Api/DownloadClient/DownloadClientModule.cs +++ b/src/NzbDrone.Api/DownloadClient/DownloadClientModule.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Download; +using System; +using NzbDrone.Core.Download; namespace NzbDrone.Api.DownloadClient { @@ -9,6 +10,22 @@ namespace NzbDrone.Api.DownloadClient { } + protected override void MapToResource(DownloadClientResource resource, DownloadClientDefinition definition) + { + base.MapToResource(resource, definition); + + resource.Enable = definition.Enable; + resource.Protocol = definition.Protocol; + } + + protected override void MapToModel(DownloadClientDefinition definition, DownloadClientResource resource) + { + base.MapToModel(definition, resource); + + definition.Enable = resource.Enable; + definition.Protocol = resource.Protocol; + } + protected override void Validate(DownloadClientDefinition definition, bool includeWarnings) { if (!definition.Enable) return; diff --git a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs index 2981e96e4..257fa6ba7 100644 --- a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs +++ b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs @@ -5,7 +5,6 @@ using NLog; using NzbDrone.Api.REST; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.MediaFiles; -using NzbDrone.Api.Mapping; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; @@ -14,7 +13,7 @@ using NzbDrone.SignalR; namespace NzbDrone.Api.EpisodeFiles { - public class EpisodeModule : NzbDroneRestModuleWithSignalR, + public class EpisodeFileModule : NzbDroneRestModuleWithSignalR, IHandle { private readonly IMediaFileService _mediaFileService; @@ -23,7 +22,7 @@ namespace NzbDrone.Api.EpisodeFiles private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification; private readonly Logger _logger; - public EpisodeModule(IBroadcastSignalRMessage signalRBroadcaster, + public EpisodeFileModule(IBroadcastSignalRMessage signalRBroadcaster, IMediaFileService mediaFileService, IRecycleBinProvider recycleBinProvider, ISeriesService seriesService, @@ -47,7 +46,7 @@ namespace NzbDrone.Api.EpisodeFiles var episodeFile = _mediaFileService.Get(id); var series = _seriesService.GetSeries(episodeFile.SeriesId); - return MapToResource(series, episodeFile); + return episodeFile.ToResource(series, _qualityUpgradableSpecification); } private List GetEpisodeFiles() @@ -61,8 +60,7 @@ namespace NzbDrone.Api.EpisodeFiles var series = _seriesService.GetSeries(seriesId); - return _mediaFileService.GetFilesBySeries(seriesId) - .Select(f => MapToResource(series, f)).ToList(); + return _mediaFileService.GetFilesBySeries(seriesId).ConvertAll(f => f.ToResource(series, _qualityUpgradableSpecification)); } private void SetQuality(EpisodeFileResource episodeFileResource) @@ -83,16 +81,6 @@ namespace NzbDrone.Api.EpisodeFiles _mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual); } - private EpisodeFileResource MapToResource(Core.Tv.Series series, EpisodeFile episodeFile) - { - var resource = episodeFile.InjectTo(); - resource.Path = Path.Combine(series.Path, episodeFile.RelativePath); - - resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(series.Profile.Value, episodeFile.Quality); - - return resource; - } - public void Handle(EpisodeFileAddedEvent message) { BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id); diff --git a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileResource.cs b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileResource.cs index 8d9e22ca2..bd856776d 100644 --- a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileResource.cs +++ b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileResource.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using NzbDrone.Api.REST; using NzbDrone.Core.Qualities; @@ -17,4 +18,47 @@ namespace NzbDrone.Api.EpisodeFiles public bool QualityCutoffNotMet { get; set; } } + + public static class EpisodeFileResourceMapper + { + private static EpisodeFileResource ToResource(this Core.MediaFiles.EpisodeFile model) + { + if (model == null) return null; + + return new EpisodeFileResource + { + Id = model.Id, + + SeriesId = model.SeriesId, + SeasonNumber = model.SeasonNumber, + RelativePath = model.RelativePath, + //Path + Size = model.Size, + DateAdded = model.DateAdded, + SceneName = model.SceneName, + Quality = model.Quality, + //QualityCutoffNotMet + }; + } + + public static EpisodeFileResource ToResource(this Core.MediaFiles.EpisodeFile model, Core.Tv.Series series, Core.DecisionEngine.IQualityUpgradableSpecification qualityUpgradableSpecification) + { + if (model == null) return null; + + return new EpisodeFileResource + { + Id = model.Id, + + SeriesId = model.SeriesId, + SeasonNumber = model.SeasonNumber, + RelativePath = model.RelativePath, + Path = Path.Combine(series.Path, model.RelativePath), + Size = model.Size, + DateAdded = model.DateAdded, + SceneName = model.SceneName, + Quality = model.Quality, + QualityCutoffNotMet = qualityUpgradableSpecification.CutoffNotMet(series.Profile.Value, model.Quality) + }; + } + } } diff --git a/src/NzbDrone.Api/Episodes/EpisodeModule.cs b/src/NzbDrone.Api/Episodes/EpisodeModule.cs index 4535e4191..7f6f5692c 100644 --- a/src/NzbDrone.Api/Episodes/EpisodeModule.cs +++ b/src/NzbDrone.Api/Episodes/EpisodeModule.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Api.Episodes var seriesId = (int)Request.Query.SeriesId; - var resources = ToListResource(_episodeService.GetEpisodeBySeries(seriesId)); + var resources = MapToResource(_episodeService.GetEpisodeBySeries(seriesId), false, true); return resources; } @@ -36,10 +36,5 @@ namespace NzbDrone.Api.Episodes { _episodeService.SetEpisodeMonitored(episodeResource.Id, episodeResource.Monitored); } - - protected override List LoadSeries(List resources) - { - return resources; - } } } diff --git a/src/NzbDrone.Api/Episodes/EpisodeModuleWithSignalR.cs b/src/NzbDrone.Api/Episodes/EpisodeModuleWithSignalR.cs index 1b18d44c8..19bc32d3f 100644 --- a/src/NzbDrone.Api/Episodes/EpisodeModuleWithSignalR.cs +++ b/src/NzbDrone.Api/Episodes/EpisodeModuleWithSignalR.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using NzbDrone.Common.Extensions; +using NzbDrone.Api.EpisodeFiles; using NzbDrone.Api.Extensions; -using NzbDrone.Api.Mapping; using NzbDrone.Api.Series; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.DecisionEngine; @@ -53,41 +54,65 @@ namespace NzbDrone.Api.Episodes protected EpisodeResource GetEpisode(int id) { var episode = _episodeService.GetEpisode(id); - episode.EpisodeFile.LazyLoad(); - episode.Series = _seriesService.GetSeries(episode.SeriesId); - return ToResource(episode); + var resource = MapToResource(episode, true, true); + return resource; } - protected override EpisodeResource ToResource(TModel model) + protected EpisodeResource MapToResource(Episode episode, bool includeSeries, bool includeEpisodeFile) { - var resource = base.ToResource(model); + var resource = episode.ToResource(); - var episode = model as Episode; - if (episode != null) + if (includeSeries || includeEpisodeFile) { - if (episode.EpisodeFile.IsLoaded && episode.EpisodeFile.Value != null) + var series = episode.Series ?? _seriesService.GetSeries(episode.SeriesId); + + if (includeSeries) { - resource.EpisodeFile.Path = Path.Combine(episode.Series.Path, episode.EpisodeFile.Value.RelativePath); - resource.EpisodeFile.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(episode.Series.Profile.Value, episode.EpisodeFile.Value.Quality); + resource.Series = series.ToResource(); + } + if (includeEpisodeFile && episode.EpisodeFileId != 0) + { + resource.EpisodeFile = episode.EpisodeFile.Value.ToResource(series, _qualityUpgradableSpecification); } } return resource; } - protected override List ToListResource(IEnumerable modelList) + protected List MapToResource(List episodes, bool includeSeries, bool includeEpisodeFile) { - var resources = base.ToListResource(modelList); + var result = episodes.ToResource(); - return LoadSeries(resources); + if (includeSeries || includeEpisodeFile) + { + var seriesDict = new Dictionary(); + for (var i = 0; i < episodes.Count; i++) + { + var episode = episodes[i]; + var resource = result[i]; + var series = episode.Series ?? seriesDict.GetValueOrDefault(episodes[i].SeriesId) ?? _seriesService.GetSeries(episodes[i].SeriesId); + seriesDict[series.Id] = series; + + if (includeSeries) + { + resource.Series = series.ToResource(); + } + if (includeEpisodeFile && episodes[i].EpisodeFileId != 0) + { + resource.EpisodeFile = episodes[i].EpisodeFile.Value.ToResource(series, _qualityUpgradableSpecification); + } + } + } + + return result; } public void Handle(EpisodeGrabbedEvent message) { foreach (var episode in message.Episode.Episodes) { - var resource = episode.InjectTo(); + var resource = episode.ToResource(); resource.Grabbed = true; BroadcastResourceChange(ModelAction.Updated, resource); @@ -101,10 +126,5 @@ namespace NzbDrone.Api.Episodes BroadcastResourceChange(ModelAction.Updated, episode.Id); } } - - protected virtual List LoadSeries(List resources) - { - return resources.LoadSubtype(e => e.SeriesId, _seriesService.GetSeries).ToList(); - } } } diff --git a/src/NzbDrone.Api/Episodes/EpisodeResource.cs b/src/NzbDrone.Api/Episodes/EpisodeResource.cs index 5a2d72d90..3ff489f38 100644 --- a/src/NzbDrone.Api/Episodes/EpisodeResource.cs +++ b/src/NzbDrone.Api/Episodes/EpisodeResource.cs @@ -1,8 +1,11 @@ using System; +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using NzbDrone.Api.EpisodeFiles; using NzbDrone.Api.REST; using NzbDrone.Api.Series; +using NzbDrone.Core.Tv; namespace NzbDrone.Api.Episodes { @@ -25,8 +28,6 @@ namespace NzbDrone.Api.Episodes public int? SceneEpisodeNumber { get; set; } public int? SceneSeasonNumber { get; set; } public bool UnverifiedSceneNumbering { get; set; } - public DateTime? EndTime { get; set; } - public DateTime? GrabDate { get; set; } public string SeriesTitle { get; set; } public SeriesResource Series { get; set; } @@ -34,4 +35,44 @@ namespace NzbDrone.Api.Episodes [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public bool Grabbed { get; set; } } + + public static class EpisodeResourceMapper + { + public static EpisodeResource ToResource(this Episode model) + { + if (model == null) return null; + + return new EpisodeResource + { + Id = model.Id, + + SeriesId = model.SeriesId, + EpisodeFileId = model.EpisodeFileId, + SeasonNumber = model.SeasonNumber, + EpisodeNumber = model.EpisodeNumber, + Title = model.Title, + AirDate = model.AirDate, + AirDateUtc = model.AirDateUtc, + Overview = model.Overview, + //EpisodeFile + + HasFile = model.HasFile, + Monitored = model.Monitored, + AbsoluteEpisodeNumber = model.AbsoluteEpisodeNumber, + SceneAbsoluteEpisodeNumber = model.SceneAbsoluteEpisodeNumber, + SceneEpisodeNumber = model.SceneEpisodeNumber, + SceneSeasonNumber = model.SceneSeasonNumber, + UnverifiedSceneNumbering = model.UnverifiedSceneNumbering, + SeriesTitle = model.SeriesTitle, + //Series = model.Series.MapToResource(), + }; + } + + public static List ToResource(this IEnumerable models) + { + if (models == null) return null; + + return models.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/Episodes/RenameEpisodeModule.cs b/src/NzbDrone.Api/Episodes/RenameEpisodeModule.cs index e35c3f93e..87f39b964 100644 --- a/src/NzbDrone.Api/Episodes/RenameEpisodeModule.cs +++ b/src/NzbDrone.Api/Episodes/RenameEpisodeModule.cs @@ -28,10 +28,10 @@ namespace NzbDrone.Api.Episodes if (Request.Query.SeasonNumber.HasValue) { var seasonNumber = (int)Request.Query.SeasonNumber; - return ToListResource(() => _renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber)); + return _renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber).ToResource(); } - return ToListResource(() => _renameEpisodeFileService.GetRenamePreviews(seriesId)); + return _renameEpisodeFileService.GetRenamePreviews(seriesId).ToResource(); } } } diff --git a/src/NzbDrone.Api/Episodes/RenameEpisodeResource.cs b/src/NzbDrone.Api/Episodes/RenameEpisodeResource.cs index 1ffdbb674..e5529ab70 100644 --- a/src/NzbDrone.Api/Episodes/RenameEpisodeResource.cs +++ b/src/NzbDrone.Api/Episodes/RenameEpisodeResource.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using NzbDrone.Api.REST; namespace NzbDrone.Api.Episodes @@ -13,4 +14,27 @@ namespace NzbDrone.Api.Episodes public string ExistingPath { get; set; } public string NewPath { get; set; } } + + public static class RenameEpisodeResourceMapper + { + public static RenameEpisodeResource ToResource(this Core.MediaFiles.RenameEpisodeFilePreview model) + { + if (model == null) return null; + + return new RenameEpisodeResource + { + SeriesId = model.SeriesId, + SeasonNumber = model.SeasonNumber, + EpisodeNumbers = model.EpisodeNumbers.ToList(), + EpisodeFileId = model.EpisodeFileId, + ExistingPath = model.ExistingPath, + NewPath = model.NewPath + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/Extensions/LazyExtensions.cs b/src/NzbDrone.Api/Extensions/LazyExtensions.cs deleted file mode 100644 index b03f4ae35..000000000 --- a/src/NzbDrone.Api/Extensions/LazyExtensions.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using NzbDrone.Api.Mapping; -using NzbDrone.Api.REST; -using NzbDrone.Common.Cache; -using NzbDrone.Core.Datastore; - -namespace NzbDrone.Api.Extensions -{ - public static class LazyExtensions - { - private static readonly ICached SetterCache = new Cached(); - - public static IEnumerable LoadSubtype(this IEnumerable parents, Func foreignKeySelector, Func, IEnumerable> sourceChildSelector) - where TSourceChild : ModelBase, new() - where TChild : RestResource, new() - where TParent : RestResource - { - var parentList = parents.Where(p => foreignKeySelector(p) != 0).ToList(); - - if (!parentList.Any()) - { - return parents; - } - - var ids = parentList.Select(foreignKeySelector).Distinct(); - var childDictionary = sourceChildSelector(ids).ToDictionary(child => child.Id, child => child); - - var childSetter = GetChildSetter(); - - foreach (var episode in parentList) - { - childSetter.Invoke(episode, new object[] { childDictionary[foreignKeySelector(episode)].InjectTo() }); - } - - return parents; - } - - - private static MethodInfo GetChildSetter() - where TChild : RestResource - where TParent : RestResource - { - var key = typeof(TChild).FullName + typeof(TParent).FullName; - - return SetterCache.Get(key, () => - { - var property = typeof(TParent).GetProperties().Single(c => c.PropertyType == typeof(TChild)); - return property.GetSetMethod(); - }); - } - } -} diff --git a/src/NzbDrone.Api/Health/HealthModule.cs b/src/NzbDrone.Api/Health/HealthModule.cs index 4928f3613..2699fa7d6 100644 --- a/src/NzbDrone.Api/Health/HealthModule.cs +++ b/src/NzbDrone.Api/Health/HealthModule.cs @@ -20,7 +20,7 @@ namespace NzbDrone.Api.Health private List GetHealth() { - return ToListResource(_healthCheckService.Results); + return _healthCheckService.Results().ToResource(); } public void Handle(HealthCheckCompleteEvent message) diff --git a/src/NzbDrone.Api/Health/HealthResource.cs b/src/NzbDrone.Api/Health/HealthResource.cs index 65a849cb4..149553722 100644 --- a/src/NzbDrone.Api/Health/HealthResource.cs +++ b/src/NzbDrone.Api/Health/HealthResource.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using NzbDrone.Api.REST; using NzbDrone.Common.Http; using NzbDrone.Core.HealthCheck; @@ -11,4 +13,26 @@ namespace NzbDrone.Api.Health public string Message { get; set; } public HttpUri WikiUrl { get; set; } } + + public static class HealthResourceMapper + { + public static HealthResource ToResource(this HealthCheck model) + { + if (model == null) return null; + + return new HealthResource + { + Id = model.Id, + + Type = model.Type, + Message = model.Message, + WikiUrl = model.WikiUrl + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/History/HistoryModule.cs b/src/NzbDrone.Api/History/HistoryModule.cs index 9262a356c..bf59cbdb0 100644 --- a/src/NzbDrone.Api/History/HistoryModule.cs +++ b/src/NzbDrone.Api/History/HistoryModule.cs @@ -1,6 +1,8 @@ using System; using Nancy; +using NzbDrone.Api.Episodes; using NzbDrone.Api.Extensions; +using NzbDrone.Api.Series; using NzbDrone.Core.Datastore; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; @@ -26,15 +28,16 @@ namespace NzbDrone.Api.History Post["/failed"] = x => MarkAsFailed(); } - protected override HistoryResource ToResource(TModel model) + protected HistoryResource MapToResource(Core.History.History model) { - var resource = base.ToResource(model); + var resource = model.ToResource(); - var history = model as Core.History.History; + resource.Series = model.Series.ToResource(); + resource.Episode = model.Episode.ToResource(); - if (history != null && history.Series != null) + if (model.Series != null) { - resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(history.Series.Profile.Value, history.Quality); + resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Series.Profile.Value, model.Quality); } return resource; @@ -64,7 +67,7 @@ namespace NzbDrone.Api.History pagingSpec.FilterExpression = h => h.EpisodeId == i; } - return ApplyToPage(_historyService.Paged, pagingSpec); + return ApplyToPage(_historyService.Paged, pagingSpec, MapToResource); } private Response MarkAsFailed() diff --git a/src/NzbDrone.Api/History/HistoryResource.cs b/src/NzbDrone.Api/History/HistoryResource.cs index 93312baed..dba4149dd 100644 --- a/src/NzbDrone.Api/History/HistoryResource.cs +++ b/src/NzbDrone.Api/History/HistoryResource.cs @@ -17,8 +17,6 @@ namespace NzbDrone.Api.History public QualityModel Quality { get; set; } public bool QualityCutoffNotMet { get; set; } public DateTime Date { get; set; } - public string Indexer { get; set; } - public string ReleaseGroup { get; set; } public string DownloadId { get; set; } public HistoryEventType EventType { get; set; } @@ -28,4 +26,31 @@ namespace NzbDrone.Api.History public EpisodeResource Episode { get; set; } public SeriesResource Series { get; set; } } + + public static class HistoryResourceMapper + { + public static HistoryResource ToResource(this Core.History.History model) + { + if (model == null) return null; + + return new HistoryResource + { + Id = model.Id, + + EpisodeId = model.EpisodeId, + SeriesId = model.SeriesId, + SourceTitle = model.SourceTitle, + Quality = model.Quality, + //QualityCutoffNotMet + Date = model.Date, + DownloadId = model.DownloadId, + + EventType = model.EventType, + + Data = model.Data + //Episode + //Series + }; + } + } } diff --git a/src/NzbDrone.Api/Indexers/IndexerModule.cs b/src/NzbDrone.Api/Indexers/IndexerModule.cs index 0ad5f5e25..09362b62a 100644 --- a/src/NzbDrone.Api/Indexers/IndexerModule.cs +++ b/src/NzbDrone.Api/Indexers/IndexerModule.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Indexers; +using System; +using NzbDrone.Core.Indexers; namespace NzbDrone.Api.Indexers { @@ -9,6 +10,25 @@ namespace NzbDrone.Api.Indexers { } + protected override void MapToResource(IndexerResource resource, IndexerDefinition definition) + { + base.MapToResource(resource, definition); + + resource.EnableRss = definition.EnableRss; + resource.EnableSearch = definition.EnableSearch; + resource.SupportsRss = definition.SupportsRss; + resource.SupportsSearch = definition.SupportsSearch; + resource.Protocol = definition.Protocol; + } + + protected override void MapToModel(IndexerDefinition definition, IndexerResource resource) + { + base.MapToModel(definition, resource); + + definition.EnableRss = resource.EnableRss; + definition.EnableSearch = resource.EnableSearch; + } + protected override void Validate(IndexerDefinition definition, bool includeWarnings) { if (!definition.Enable) return; diff --git a/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs b/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs index a1bdc24f8..70127abff 100644 --- a/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs +++ b/src/NzbDrone.Api/Indexers/ReleaseModuleBase.cs @@ -2,7 +2,6 @@ using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Indexers; using NzbDrone.Core.Parser.Model; -using Omu.ValueInjecter; using System.Linq; namespace NzbDrone.Api.Indexers @@ -25,42 +24,20 @@ namespace NzbDrone.Api.Indexers protected virtual ReleaseResource MapDecision(DownloadDecision decision, int initialWeight) { - var release = new ReleaseResource(); + var release = decision.ToResource(); - release.InjectFrom(decision.RemoteEpisode.Release); - release.InjectFrom(decision.RemoteEpisode.ParsedEpisodeInfo); - release.InjectFrom(decision); - release.Rejections = decision.Rejections.Select(r => r.Reason).ToList(); - release.DownloadAllowed = decision.RemoteEpisode.DownloadAllowed; release.ReleaseWeight = initialWeight; if (decision.RemoteEpisode.Series != null) { - release.QualityWeight = decision.RemoteEpisode - .Series - .Profile - .Value - .Items - .FindIndex(v => v.Quality == release.Quality.Quality) * 100; + release.QualityWeight = decision.RemoteEpisode.Series + .Profile.Value + .Items.FindIndex(v => v.Quality == release.Quality.Quality) * 100; } release.QualityWeight += release.Quality.Revision.Real * 10; release.QualityWeight += release.Quality.Revision.Version; - var torrentRelease = decision.RemoteEpisode.Release as TorrentInfo; - - if (torrentRelease != null) - { - release.Protocol = DownloadProtocol.Torrent; - release.Seeders = torrentRelease.Seeders; - //TODO: move this up the chains - release.Leechers = torrentRelease.Peers - torrentRelease.Seeders; - } - else - { - release.Protocol = DownloadProtocol.Usenet; - } - return release; } } diff --git a/src/NzbDrone.Api/Indexers/ReleasePushModule.cs b/src/NzbDrone.Api/Indexers/ReleasePushModule.cs index 01e80d569..7ee23e982 100644 --- a/src/NzbDrone.Api/Indexers/ReleasePushModule.cs +++ b/src/NzbDrone.Api/Indexers/ReleasePushModule.cs @@ -6,7 +6,6 @@ using NzbDrone.Core.Download; using System.Collections.Generic; using System.Linq; using NzbDrone.Core.Parser.Model; -using NzbDrone.Api.Mapping; using NzbDrone.Api.Extensions; using NLog; using NzbDrone.Core.Indexers; @@ -39,9 +38,7 @@ namespace NzbDrone.Api.Indexers { _logger.Info("Release pushed: {0} - {1}", release.Title, release.DownloadUrl); - var info = release.Protocol == DownloadProtocol.Usenet ? - release.InjectTo() : - release.InjectTo(); + var info = release.ToModel(); info.Guid = "PUSH-" + info.DownloadUrl; diff --git a/src/NzbDrone.Api/Indexers/ReleaseResource.cs b/src/NzbDrone.Api/Indexers/ReleaseResource.cs index 91f1174f6..b951b0fe0 100644 --- a/src/NzbDrone.Api/Indexers/ReleaseResource.cs +++ b/src/NzbDrone.Api/Indexers/ReleaseResource.cs @@ -5,6 +5,9 @@ using NzbDrone.Api.REST; using NzbDrone.Core.Parser; using NzbDrone.Core.Qualities; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.DecisionEngine; +using System.Linq; namespace NzbDrone.Api.Indexers { @@ -20,11 +23,9 @@ namespace NzbDrone.Api.Indexers public int IndexerId { get; set; } public string Indexer { get; set; } public string ReleaseGroup { get; set; } - public string SubGroup { get; set; } public string ReleaseHash { get; set; } public string Title { get; set; } public bool FullSeason { get; set; } - public bool SceneSource { get; set; } public int SeasonNumber { get; set; } public Language Language { get; set; } public string AirDate { get; set; } @@ -45,6 +46,8 @@ namespace NzbDrone.Api.Indexers public int ReleaseWeight { get; set; } + public string MagnetUrl { get; set; } + public string InfoHash { get; set; } public int? Seeders { get; set; } public int? Leechers { get; set; } public DownloadProtocol Protocol { get; set; } @@ -74,4 +77,98 @@ namespace NzbDrone.Api.Indexers public bool IsPossibleSpecialEpisode { get; set; } public bool Special { get; set; } } + + public static class ReleaseResourceMapper + { + public static ReleaseResource ToResource(this DownloadDecision model) + { + var releaseInfo = model.RemoteEpisode.Release; + var parsedEpisodeInfo = model.RemoteEpisode.ParsedEpisodeInfo; + var remoteEpisode = model.RemoteEpisode; + var torrentInfo = (model.RemoteEpisode.Release as TorrentInfo) ?? new TorrentInfo(); + + // TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead? (Got a huge Deja Vu, didn't we talk about this already once?) + return new ReleaseResource + { + Guid = releaseInfo.Guid, + Quality = parsedEpisodeInfo.Quality, + //QualityWeight + Age = releaseInfo.Age, + AgeHours = releaseInfo.AgeHours, + AgeMinutes = releaseInfo.AgeMinutes, + Size = releaseInfo.Size, + IndexerId = releaseInfo.IndexerId, + Indexer = releaseInfo.Indexer, + ReleaseGroup = parsedEpisodeInfo.ReleaseGroup, + ReleaseHash = parsedEpisodeInfo.ReleaseHash, + Title = releaseInfo.Title, + FullSeason = parsedEpisodeInfo.FullSeason, + SeasonNumber = parsedEpisodeInfo.SeasonNumber, + Language = parsedEpisodeInfo.Language, + AirDate = parsedEpisodeInfo.AirDate, + SeriesTitle = parsedEpisodeInfo.SeriesTitle, + EpisodeNumbers = parsedEpisodeInfo.EpisodeNumbers, + AbsoluteEpisodeNumbers = parsedEpisodeInfo.AbsoluteEpisodeNumbers, + Approved = model.Approved, + TemporarilyRejected = model.TemporarilyRejected, + Rejected = model.Rejected, + TvdbId = releaseInfo.TvdbId, + TvRageId = releaseInfo.TvRageId, + Rejections = model.Rejections.Select(r => r.Reason).ToList(), + PublishDate = releaseInfo.PublishDate, + CommentUrl = releaseInfo.CommentUrl, + DownloadUrl = releaseInfo.DownloadUrl, + InfoUrl = releaseInfo.InfoUrl, + DownloadAllowed = remoteEpisode.DownloadAllowed, + //ReleaseWeight + + MagnetUrl = torrentInfo.MagnetUrl, + InfoHash = torrentInfo.InfoHash, + Seeders = torrentInfo.Seeders, + Leechers = (torrentInfo.Peers.HasValue && torrentInfo.Seeders.HasValue) ? (torrentInfo.Peers.Value - torrentInfo.Seeders.Value) : (int?)null, + Protocol = releaseInfo.DownloadProtocol, + + IsDaily = parsedEpisodeInfo.IsDaily, + IsAbsoluteNumbering = parsedEpisodeInfo.IsAbsoluteNumbering, + IsPossibleSpecialEpisode = parsedEpisodeInfo.IsPossibleSpecialEpisode, + Special = parsedEpisodeInfo.Special, + }; + + } + + public static ReleaseInfo ToModel(this ReleaseResource resource) + { + ReleaseInfo model; + + if (resource.Protocol == DownloadProtocol.Torrent) + { + model = new TorrentInfo + { + MagnetUrl = resource.MagnetUrl, + InfoHash = resource.InfoHash, + Seeders = resource.Seeders, + Peers = (resource.Seeders.HasValue && resource.Leechers.HasValue) ? (resource.Seeders + resource.Leechers) : null + }; + } + else + { + model = new ReleaseInfo(); + } + + model.Guid = resource.Guid; + model.Title = resource.Title; + model.Size = resource.Size; + model.DownloadUrl = resource.DownloadUrl; + model.InfoUrl = resource.InfoUrl; + model.CommentUrl = resource.CommentUrl; + model.IndexerId = resource.IndexerId; + model.Indexer = resource.Indexer; + model.DownloadProtocol = resource.DownloadProtocol; + model.TvdbId = resource.TvdbId; + model.TvRageId = resource.TvRageId; + model.PublishDate = resource.PublishDate; + + return model; + } + } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Logs/LogModule.cs b/src/NzbDrone.Api/Logs/LogModule.cs index 8684e3250..15c536cab 100644 --- a/src/NzbDrone.Api/Logs/LogModule.cs +++ b/src/NzbDrone.Api/Logs/LogModule.cs @@ -1,6 +1,5 @@ using NzbDrone.Core.Datastore; using NzbDrone.Core.Instrumentation; -using NzbDrone.Api.Mapping; namespace NzbDrone.Api.Logs { @@ -16,7 +15,7 @@ namespace NzbDrone.Api.Logs private PagingResource GetLogs(PagingResource pagingResource) { - var pageSpec = pagingResource.InjectTo>(); + var pageSpec = pagingResource.MapToPagingSpec(); if (pageSpec.SortKey == "time") { @@ -48,7 +47,7 @@ namespace NzbDrone.Api.Logs } } - return ApplyToPage(_logService.Paged, pageSpec); + return ApplyToPage(_logService.Paged, pageSpec, LogResourceMapper.ToResource); } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Logs/LogResource.cs b/src/NzbDrone.Api/Logs/LogResource.cs index 8e724dfcf..504a45839 100644 --- a/src/NzbDrone.Api/Logs/LogResource.cs +++ b/src/NzbDrone.Api/Logs/LogResource.cs @@ -11,6 +11,25 @@ namespace NzbDrone.Api.Logs public string Level { get; set; } public string Logger { get; set; } public string Message { get; set; } - public string Method { get; set; } + } + + public static class LogResourceMapper + { + public static LogResource ToResource(this Core.Instrumentation.Log model) + { + if (model == null) return null; + + return new LogResource + { + Id = model.Id, + + Time = model.Time, + Exception = model.Exception, + ExceptionType = model.ExceptionType, + Level = model.Level, + Logger = model.Logger, + Message = model.Message + }; + } } } diff --git a/src/NzbDrone.Api/ManualImport/ManualImportModule.cs b/src/NzbDrone.Api/ManualImport/ManualImportModule.cs index cfa981203..024b8e452 100644 --- a/src/NzbDrone.Api/ManualImport/ManualImportModule.cs +++ b/src/NzbDrone.Api/ManualImport/ManualImportModule.cs @@ -25,7 +25,7 @@ namespace NzbDrone.Api.ManualImport var downloadIdQuery = Request.Query.downloadId; var downloadId = (string)downloadIdQuery.Value; - return ToListResource(_manualImportService.GetMediaFiles(folder, downloadId)).Select(AddQualityWeight).ToList(); + return _manualImportService.GetMediaFiles(folder, downloadId).ToResource().Select(AddQualityWeight).ToList(); } private ManualImportResource AddQualityWeight(ManualImportResource item) diff --git a/src/NzbDrone.Api/ManualImport/ManualImportResource.cs b/src/NzbDrone.Api/ManualImport/ManualImportResource.cs index 72d39e8f9..bc7b87408 100644 --- a/src/NzbDrone.Api/ManualImport/ManualImportResource.cs +++ b/src/NzbDrone.Api/ManualImport/ManualImportResource.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using System.Linq; using NzbDrone.Api.Episodes; using NzbDrone.Api.REST; using NzbDrone.Api.Series; +using NzbDrone.Common.Crypto; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Qualities; @@ -20,13 +22,35 @@ namespace NzbDrone.Api.ManualImport public int QualityWeight { get; set; } public string DownloadId { get; set; } public IEnumerable Rejections { get; set; } + } - public int Id + public static class ManualImportResourceMapper + { + public static ManualImportResource ToResource(this Core.MediaFiles.EpisodeImport.Manual.ManualImportItem model) { - get + if (model == null) return null; + + return new ManualImportResource { - return Path.GetHashCode(); - } + Id = HashConverter.GetHashInt31(model.Path), + + Path = model.Path, + RelativePath = model.RelativePath, + Name = model.Name, + Size = model.Size, + Series = model.Series.ToResource(), + SeasonNumber = model.SeasonNumber, + Episodes = model.Episodes.ToResource(), + Quality = model.Quality, + //QualityWeight + DownloadId = model.DownloadId, + Rejections = model.Rejections + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); } } } diff --git a/src/NzbDrone.Api/Mapping/CloneInjection.cs b/src/NzbDrone.Api/Mapping/CloneInjection.cs deleted file mode 100644 index 2afc3df7e..000000000 --- a/src/NzbDrone.Api/Mapping/CloneInjection.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Marr.Data; -using Omu.ValueInjecter; - -namespace NzbDrone.Api.Mapping -{ - public class CloneInjection : ConventionInjection - { - protected override bool Match(ConventionInfo conventionInfo) - { - return conventionInfo.SourceProp.Name == conventionInfo.TargetProp.Name && - conventionInfo.SourceProp.Value != null; - } - - protected override object SetValue(ConventionInfo conventionInfo) - { - if (conventionInfo.SourceProp.Type == conventionInfo.TargetProp.Type) - return conventionInfo.SourceProp.Value; - - - if (conventionInfo.SourceProp.Type.IsArray) - { - var array = (Array)conventionInfo.SourceProp.Value; - var clone = (Array)array.Clone(); - - for (var index = 0; index < array.Length; index++) - { - var item = array.GetValue(index); - if (!item.GetType().IsValueType && !(item is string)) - { - clone.SetValue(Activator.CreateInstance(item.GetType()).InjectFrom(item), index); - } - } - - return clone; - } - - if (conventionInfo.SourceProp.Type.IsGenericType) - { - var genericInterfaces = conventionInfo.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces(); - if (genericInterfaces.Any(d => d == typeof(IEnumerable))) - { - return MapLists(conventionInfo); - } - - if (genericInterfaces.Any(i => i == typeof(ILazyLoaded))) - { - return MapLazy(conventionInfo); - } - - //unhandled generic type, you could also return null or throw - return conventionInfo.SourceProp.Value; - } - - //for simple object types create a new instace and apply the clone injection on it - return Activator.CreateInstance(conventionInfo.TargetProp.Type) - .InjectFrom(conventionInfo.SourceProp.Value); - } - - private static object MapLazy(ConventionInfo conventionInfo) - { - var sourceArgument = conventionInfo.SourceProp.Type.GetGenericArguments()[0]; - - dynamic lazy = conventionInfo.SourceProp.Value; - - if (lazy.IsLoaded && lazy.Value != null) - { - if (conventionInfo.TargetProp.Type.IsAssignableFrom(sourceArgument)) - { - return lazy.Value; - } - - var genericArgument = conventionInfo.TargetProp.Type; - - if (genericArgument.IsValueType || genericArgument == typeof(string)) - { - return lazy.Value; - } - - if (genericArgument.IsGenericType) - { - if (conventionInfo.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces().Any(d => d == typeof(IEnumerable))) - { - return MapLists(genericArgument, lazy.Value); - } - } - - return Activator.CreateInstance(genericArgument).InjectFrom((object)lazy.Value); - } - - return null; - } - - private static object MapLists(ConventionInfo conventionInfo) - { - var genericArgument = conventionInfo.TargetProp.Type.GetGenericArguments()[0]; - - return MapLists(genericArgument, conventionInfo.SourceProp.Value); - } - - private static object MapLists(Type targetType, object sourceValue) - { - if (targetType.IsValueType || targetType == typeof(string)) - { - return sourceValue; - } - - var listType = typeof(List<>).MakeGenericType(targetType); - var addMethod = listType.GetMethod("Add"); - - var result = Activator.CreateInstance(listType); - - foreach (var sourceItem in (IEnumerable)sourceValue) - { - var e = Activator.CreateInstance(targetType).InjectFrom(sourceItem); - addMethod.Invoke(result, new[] { e }); - } - - return result; - } - } -} diff --git a/src/NzbDrone.Api/Mapping/MappingValidation.cs b/src/NzbDrone.Api/Mapping/MappingValidation.cs deleted file mode 100644 index 29596c155..000000000 --- a/src/NzbDrone.Api/Mapping/MappingValidation.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using NzbDrone.Api.REST; -using NzbDrone.Common.Reflection; - -namespace NzbDrone.Api.Mapping -{ - public static class MappingValidation - { - public static void ValidateMapping(Type modelType, Type resourceType) - { - var errors = modelType.GetSimpleProperties().Where(c=>!c.GetGetMethod().IsStatic).Select(p => GetError(resourceType, p)).Where(c => c != null).ToList(); - - if (errors.Any()) - { - throw new ResourceMappingException(errors); - } - - PrintExtraProperties(modelType, resourceType); - } - - private static void PrintExtraProperties(Type modelType, Type resourceType) - { - var resourceBaseProperties = typeof(RestResource).GetProperties().Select(c => c.Name); - var resourceProperties = resourceType.GetProperties().Select(c => c.Name).Except(resourceBaseProperties); - var modelProperties = modelType.GetProperties().Select(c => c.Name); - - var extra = resourceProperties.Except(modelProperties); - - foreach (var extraProp in extra) - { - Console.WriteLine("Extra: [{0}]", extraProp); - } - } - - private static string GetError(Type resourceType, PropertyInfo modelProperty) - { - var resourceProperty = resourceType.GetProperties().FirstOrDefault(c => c.Name == modelProperty.Name); - - if (resourceProperty == null) - { - return string.Format("public {0} {1} {{ get; set; }}", modelProperty.PropertyType.Name, modelProperty.Name); - } - - if (resourceProperty.PropertyType != modelProperty.PropertyType && !typeof(RestResource).IsAssignableFrom(resourceProperty.PropertyType)) - { - return string.Format("Expected {0}.{1} to have type of {2} but found {3}", resourceType.Name, resourceProperty.Name, modelProperty.PropertyType, resourceProperty.PropertyType); - } - - return null; - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Api/Mapping/ResourceMappingException.cs b/src/NzbDrone.Api/Mapping/ResourceMappingException.cs deleted file mode 100644 index 70250914b..000000000 --- a/src/NzbDrone.Api/Mapping/ResourceMappingException.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace NzbDrone.Api.Mapping -{ - public class ResourceMappingException : ApplicationException - { - public ResourceMappingException(IEnumerable error) - : base(Environment.NewLine + string.Join(Environment.NewLine, error.OrderBy(c => c))) - { - - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Api/Mapping/ValueInjectorExtensions.cs b/src/NzbDrone.Api/Mapping/ValueInjectorExtensions.cs deleted file mode 100644 index e810bb17a..000000000 --- a/src/NzbDrone.Api/Mapping/ValueInjectorExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Omu.ValueInjecter; - -namespace NzbDrone.Api.Mapping -{ - public static class ValueInjectorExtensions - { - public static TTarget InjectTo(this object source) where TTarget : new() - { - if (source == null) return default(TTarget); - - var targetType = typeof(TTarget); - - if (targetType.IsGenericType && - targetType.GetGenericTypeDefinition() != null && - targetType.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)) && - source.GetType().IsGenericType && - source.GetType().GetGenericTypeDefinition() != null && - source.GetType().GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable))) - { - var result = new TTarget(); - - var listSubType = targetType.GetGenericArguments()[0]; - var listType = typeof(List<>).MakeGenericType(listSubType); - var addMethod = listType.GetMethod("Add"); - - foreach (var sourceItem in (IEnumerable)source) - { - var e = Activator.CreateInstance(listSubType).InjectFrom(sourceItem); - addMethod.Invoke(result, new[] { e }); - } - - return result; - } - - return (TTarget)new TTarget().InjectFrom(source); - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Api/Metadata/MetadataModule.cs b/src/NzbDrone.Api/Metadata/MetadataModule.cs index b831f1a49..77c828093 100644 --- a/src/NzbDrone.Api/Metadata/MetadataModule.cs +++ b/src/NzbDrone.Api/Metadata/MetadataModule.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Metadata; +using System; +using NzbDrone.Core.Metadata; namespace NzbDrone.Api.Metadata { @@ -9,6 +10,20 @@ namespace NzbDrone.Api.Metadata { } + protected override void MapToResource(MetadataResource resource, MetadataDefinition definition) + { + base.MapToResource(resource, definition); + + resource.Enable = definition.Enable; + } + + protected override void MapToModel(MetadataDefinition definition, MetadataResource resource) + { + base.MapToModel(definition, resource); + + definition.Enable = resource.Enable; + } + protected override void Validate(MetadataDefinition definition, bool includeWarnings) { if (!definition.Enable) return; diff --git a/src/NzbDrone.Api/Notifications/NotificationModule.cs b/src/NzbDrone.Api/Notifications/NotificationModule.cs index ec5d14d39..a8407f7a5 100644 --- a/src/NzbDrone.Api/Notifications/NotificationModule.cs +++ b/src/NzbDrone.Api/Notifications/NotificationModule.cs @@ -1,4 +1,5 @@ -using NzbDrone.Core.Notifications; +using System; +using NzbDrone.Core.Notifications; namespace NzbDrone.Api.Notifications { @@ -9,6 +10,36 @@ namespace NzbDrone.Api.Notifications { } + protected override void MapToResource(NotificationResource resource, NotificationDefinition definition) + { + base.MapToResource(resource, definition); + + resource.OnGrab = definition.OnGrab; + resource.OnDownload = definition.OnDownload; + resource.OnUpgrade = definition.OnUpgrade; + resource.OnRename = definition.OnRename; + resource.SupportsOnGrab = definition.SupportsOnGrab; + resource.SupportsOnDownload = definition.SupportsOnDownload; + resource.SupportsOnUpgrade = definition.SupportsOnUpgrade; + resource.SupportsOnRename = definition.SupportsOnRename; + resource.Tags = definition.Tags; + } + + protected override void MapToModel(NotificationDefinition definition, NotificationResource resource) + { + base.MapToModel(definition, resource); + + definition.OnGrab = resource.OnGrab; + definition.OnDownload = resource.OnDownload; + definition.OnUpgrade = resource.OnUpgrade; + definition.OnRename = resource.OnRename; + definition.SupportsOnGrab = resource.SupportsOnGrab; + definition.SupportsOnDownload = resource.SupportsOnDownload; + definition.SupportsOnUpgrade = resource.SupportsOnUpgrade; + definition.SupportsOnRename = resource.SupportsOnRename; + definition.Tags = resource.Tags; + } + protected override void Validate(NotificationDefinition definition, bool includeWarnings) { if (!definition.OnGrab && !definition.OnDownload) return; diff --git a/src/NzbDrone.Api/Notifications/NotificationResource.cs b/src/NzbDrone.Api/Notifications/NotificationResource.cs index 965e0af15..f3fa11327 100644 --- a/src/NzbDrone.Api/Notifications/NotificationResource.cs +++ b/src/NzbDrone.Api/Notifications/NotificationResource.cs @@ -4,7 +4,6 @@ namespace NzbDrone.Api.Notifications { public class NotificationResource : ProviderResource { - public string Link { get; set; } public bool OnGrab { get; set; } public bool OnDownload { get; set; } public bool OnUpgrade { get; set; } @@ -13,7 +12,6 @@ namespace NzbDrone.Api.Notifications public bool SupportsOnDownload { get; set; } public bool SupportsOnUpgrade { get; set; } public bool SupportsOnRename { get; set; } - public string TestCommand { get; set; } public HashSet Tags { get; set; } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj index 2e112d0a1..f497eeb06 100644 --- a/src/NzbDrone.Api/NzbDrone.Api.csproj +++ b/src/NzbDrone.Api/NzbDrone.Api.csproj @@ -70,9 +70,6 @@ ..\packages\DDay.iCal.1.0.2.575\lib\DDay.iCal.dll - - ..\packages\ValueInjecter.2.3.3\lib\net35\Omu.ValueInjecter.dll - False ..\Libraries\Sqlite\System.Data.SQLite.dll @@ -111,7 +108,6 @@ - @@ -146,7 +142,6 @@ - @@ -181,10 +176,6 @@ - - - - diff --git a/src/NzbDrone.Api/NzbDroneRestModule.cs b/src/NzbDrone.Api/NzbDroneRestModule.cs index e3c043ef5..4770a7743 100644 --- a/src/NzbDrone.Api/NzbDroneRestModule.cs +++ b/src/NzbDrone.Api/NzbDroneRestModule.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using NzbDrone.Api.REST; using NzbDrone.Api.Validation; using NzbDrone.Core.Datastore; -using NzbDrone.Api.Mapping; namespace NzbDrone.Api { @@ -25,42 +24,19 @@ namespace NzbDrone.Api PutValidator.RuleFor(r => r.Id).ValidId(); } - protected int GetNewId(Func function, TResource resource) where TModel : ModelBase, new() - { - var model = resource.InjectTo(); - function(model); - return model.Id; - } - - protected List ToListResource(Func> function) where TModel : class - { - var modelList = function(); - return ToListResource(modelList); - } - - protected virtual List ToListResource(IEnumerable modelList) where TModel : class - { - return modelList.Select(ToResource).ToList(); - } - - protected virtual TResource ToResource(TModel model) where TModel : class - { - return model.InjectTo(); - } - - protected PagingResource ApplyToPage(Func, PagingSpec> function, PagingSpec pagingSpec) where TModel : ModelBase, new() + protected PagingResource ApplyToPage(Func, PagingSpec> function, PagingSpec pagingSpec, Converter mapper) { pagingSpec = function(pagingSpec); return new PagingResource - { - Page = pagingSpec.Page, - PageSize = pagingSpec.PageSize, - SortDirection = pagingSpec.SortDirection, - SortKey = pagingSpec.SortKey, - TotalRecords = pagingSpec.TotalRecords, - Records = ToListResource(pagingSpec.Records) - }; + { + Page = pagingSpec.Page, + PageSize = pagingSpec.PageSize, + SortDirection = pagingSpec.SortDirection, + SortKey = pagingSpec.SortKey, + TotalRecords = pagingSpec.TotalRecords, + Records = pagingSpec.Records.ConvertAll(mapper) + }; } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/NzbDroneRestModuleWithSignalR.cs b/src/NzbDrone.Api/NzbDroneRestModuleWithSignalR.cs index 761503dfa..a2061a770 100644 --- a/src/NzbDrone.Api/NzbDroneRestModuleWithSignalR.cs +++ b/src/NzbDrone.Api/NzbDroneRestModuleWithSignalR.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Api { public abstract class NzbDroneRestModuleWithSignalR : NzbDroneRestModule, IHandle> where TResource : RestResource, new() - where TModel : ModelBase + where TModel : ModelBase, new() { private readonly IBroadcastSignalRMessage _signalRBroadcaster; diff --git a/src/NzbDrone.Api/PagingResource.cs b/src/NzbDrone.Api/PagingResource.cs index 96eeb7c48..50bbff8ab 100644 --- a/src/NzbDrone.Api/PagingResource.cs +++ b/src/NzbDrone.Api/PagingResource.cs @@ -3,7 +3,7 @@ using NzbDrone.Core.Datastore; namespace NzbDrone.Api { - public class PagingResource + public class PagingResource { public int Page { get; set; } public int PageSize { get; set; } @@ -12,6 +12,20 @@ namespace NzbDrone.Api public string FilterKey { get; set; } public string FilterValue { get; set; } public int TotalRecords { get; set; } - public List Records { get; set; } + public List Records { get; set; } + } + + public static class PagingResourceMapper + { + public static PagingSpec MapToPagingSpec(this PagingResource pagingSpec) + { + return new PagingSpec + { + Page = pagingSpec.Page, + PageSize = pagingSpec.PageSize, + SortKey = pagingSpec.SortKey, + SortDirection = pagingSpec.SortDirection, + }; + } } } diff --git a/src/NzbDrone.Api/Parse/ParseModule.cs b/src/NzbDrone.Api/Parse/ParseModule.cs index 6be81934c..df36307ff 100644 --- a/src/NzbDrone.Api/Parse/ParseModule.cs +++ b/src/NzbDrone.Api/Parse/ParseModule.cs @@ -1,5 +1,6 @@ -using NzbDrone.Core.Parser; -using NzbDrone.Core.Parser.Model; +using NzbDrone.Api.Episodes; +using NzbDrone.Api.Series; +using NzbDrone.Core.Parser; namespace NzbDrone.Api.Parse { @@ -16,7 +17,7 @@ namespace NzbDrone.Api.Parse private ParseResource Parse() { - var title = Request.Query.Title.Value; + var title = Request.Query.Title.Value as string; var parsedEpisodeInfo = Parser.ParseTitle(title); if (parsedEpisodeInfo == null) @@ -26,24 +27,24 @@ namespace NzbDrone.Api.Parse var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0); - if (remoteEpisode == null) + if (remoteEpisode != null) { - remoteEpisode = new RemoteEpisode - { - ParsedEpisodeInfo = parsedEpisodeInfo - }; - return new ParseResource - { - Title = title, - ParsedEpisodeInfo = parsedEpisodeInfo - }; + { + Title = title, + ParsedEpisodeInfo = remoteEpisode.ParsedEpisodeInfo, + Series = remoteEpisode.Series.ToResource(), + Episodes = remoteEpisode.Episodes.ToResource() + }; + } + else + { + return new ParseResource + { + Title = title, + ParsedEpisodeInfo = parsedEpisodeInfo + }; } - - var resource = ToResource(remoteEpisode); - resource.Title = title; - - return resource; } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Profiles/Delay/DelayProfileModule.cs b/src/NzbDrone.Api/Profiles/Delay/DelayProfileModule.cs index 33e2c949a..e7975b661 100644 --- a/src/NzbDrone.Api/Profiles/Delay/DelayProfileModule.cs +++ b/src/NzbDrone.Api/Profiles/Delay/DelayProfileModule.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using FluentValidation; -using NzbDrone.Api.Mapping; +using FluentValidation.Results; using NzbDrone.Api.REST; using NzbDrone.Api.Validation; using NzbDrone.Core.Profiles.Delay; @@ -26,12 +26,21 @@ namespace NzbDrone.Api.Profiles.Delay SharedValidator.RuleFor(d => d.Tags).SetValidator(tagInUseValidator); SharedValidator.RuleFor(d => d.UsenetDelay).GreaterThanOrEqualTo(0); SharedValidator.RuleFor(d => d.TorrentDelay).GreaterThanOrEqualTo(0); - SharedValidator.RuleFor(d => d.Id).SetValidator(new DelayProfileValidator()); + + SharedValidator.Custom(delayProfile => + { + if (!delayProfile.EnableUsenet && !delayProfile.EnableTorrent) + { + return new ValidationFailure("", "Either Usenet or Torrent should be enabled"); + } + + return null; + }); } private int Create(DelayProfileResource resource) { - var model = resource.InjectTo(); + var model = resource.ToModel(); model = _delayProfileService.Add(model); return model.Id; @@ -49,17 +58,18 @@ namespace NzbDrone.Api.Profiles.Delay private void Update(DelayProfileResource resource) { - GetNewId(_delayProfileService.Update, resource); + var model = resource.ToModel(); + _delayProfileService.Update(model); } private DelayProfileResource GetById(int id) { - return _delayProfileService.Get(id).InjectTo(); + return _delayProfileService.Get(id).ToResource(); } private List GetAll() { - return _delayProfileService.All().InjectTo>(); + return _delayProfileService.All().ToResource(); } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Profiles/Delay/DelayProfileResource.cs b/src/NzbDrone.Api/Profiles/Delay/DelayProfileResource.cs index bbc2fc67f..e35df9043 100644 --- a/src/NzbDrone.Api/Profiles/Delay/DelayProfileResource.cs +++ b/src/NzbDrone.Api/Profiles/Delay/DelayProfileResource.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; +using System.Linq; using NzbDrone.Api.REST; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Profiles.Delay; namespace NzbDrone.Api.Profiles.Delay { @@ -14,4 +16,48 @@ namespace NzbDrone.Api.Profiles.Delay public int Order { get; set; } public HashSet Tags { get; set; } } + + public static class DelayProfileResourceMapper + { + public static DelayProfileResource ToResource(this DelayProfile model) + { + if (model == null) return null; + + return new DelayProfileResource + { + Id = model.Id, + + EnableUsenet = model.EnableUsenet, + EnableTorrent = model.EnableTorrent, + PreferredProtocol = model.PreferredProtocol, + UsenetDelay = model.UsenetDelay, + TorrentDelay = model.TorrentDelay, + Order = model.Order, + Tags = new HashSet(model.Tags) + }; + } + + public static DelayProfile ToModel(this DelayProfileResource resource) + { + if (resource == null) return null; + + return new DelayProfile + { + Id = resource.Id, + + EnableUsenet = resource.EnableUsenet, + EnableTorrent = resource.EnableTorrent, + PreferredProtocol = resource.PreferredProtocol, + UsenetDelay = resource.UsenetDelay, + TorrentDelay = resource.TorrentDelay, + Order = resource.Order, + Tags = new HashSet(resource.Tags) + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/Profiles/Delay/DelayProfileValidator.cs b/src/NzbDrone.Api/Profiles/Delay/DelayProfileValidator.cs deleted file mode 100644 index b854d87a2..000000000 --- a/src/NzbDrone.Api/Profiles/Delay/DelayProfileValidator.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FluentValidation.Validators; -using NzbDrone.Core.Profiles.Delay; -using Omu.ValueInjecter; - -namespace NzbDrone.Api.Profiles.Delay -{ - public class DelayProfileValidator : PropertyValidator - { - public DelayProfileValidator() - : base("Usenet or Torrent must be enabled") - { - } - - protected override bool IsValid(PropertyValidatorContext context) - { - var delayProfile = new DelayProfile(); - delayProfile.InjectFrom(context.ParentContext.InstanceToValidate); - - if (!delayProfile.EnableUsenet && !delayProfile.EnableTorrent) - { - return false; - } - - return true; - } - } -} diff --git a/src/NzbDrone.Api/Profiles/Languages/LanguageResource.cs b/src/NzbDrone.Api/Profiles/Languages/LanguageResource.cs index 442e29177..182e86a29 100644 --- a/src/NzbDrone.Api/Profiles/Languages/LanguageResource.cs +++ b/src/NzbDrone.Api/Profiles/Languages/LanguageResource.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Api.Profiles.Languages public class LanguageResource : RestResource { [JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)] - public int Id { get; set; } + public new int Id { get; set; } public string Name { get; set; } public string NameLower { get { return Name.ToLowerInvariant(); } } } diff --git a/src/NzbDrone.Api/Profiles/ProfileModule.cs b/src/NzbDrone.Api/Profiles/ProfileModule.cs index ca1276dad..a8ed7e7e6 100644 --- a/src/NzbDrone.Api/Profiles/ProfileModule.cs +++ b/src/NzbDrone.Api/Profiles/ProfileModule.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using FluentValidation; -using NzbDrone.Api.Mapping; using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; using NzbDrone.Core.Validation; @@ -28,9 +27,9 @@ namespace NzbDrone.Api.Profiles private int Create(ProfileResource resource) { - var model = resource.InjectTo(); - model = _profileService.Add(model); - return model.Id; + var model = resource.ToModel(); + + return _profileService.Add(model).Id; } private void DeleteProfile(int id) @@ -40,26 +39,19 @@ namespace NzbDrone.Api.Profiles private void Update(ProfileResource resource) { - var model = _profileService.Get(resource.Id); - - model.Name = resource.Name; - model.Cutoff = (Quality)resource.Cutoff.Id; - model.Items = resource.Items.InjectTo>(); - model.Language = resource.Language; + var model = resource.ToModel(); _profileService.Update(model); } private ProfileResource GetById(int id) { - return _profileService.Get(id).InjectTo(); + return _profileService.Get(id).ToResource(); } private List GetAll() { - var profiles = _profileService.All().InjectTo>(); - - return profiles; + return _profileService.All().ToResource(); } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Profiles/ProfileResource.cs b/src/NzbDrone.Api/Profiles/ProfileResource.cs index 9de11708b..d4bfba0eb 100644 --- a/src/NzbDrone.Api/Profiles/ProfileResource.cs +++ b/src/NzbDrone.Api/Profiles/ProfileResource.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using NzbDrone.Api.REST; using NzbDrone.Core.Parser; +using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; namespace NzbDrone.Api.Profiles @@ -19,4 +21,64 @@ namespace NzbDrone.Api.Profiles public Quality Quality { get; set; } public bool Allowed { get; set; } } + + public static class ProfileResourceMapper + { + public static ProfileResource ToResource(this Profile model) + { + if (model == null) return null; + + return new ProfileResource + { + Id = model.Id, + + Name = model.Name, + Cutoff = model.Cutoff, + Items = model.Items.ConvertAll(ToResource), + Language = model.Language + }; + } + + public static ProfileQualityItemResource ToResource(this ProfileQualityItem model) + { + if (model == null) return null; + + return new ProfileQualityItemResource + { + Quality = model.Quality, + Allowed = model.Allowed + }; + } + + public static Profile ToModel(this ProfileResource resource) + { + if (resource == null) return null; + + return new Profile + { + Id = resource.Id, + + Name = resource.Name, + Cutoff = (Quality)resource.Cutoff.Id, + Items = resource.Items.ConvertAll(ToModel), + Language = resource.Language + }; + } + + public static ProfileQualityItem ToModel(this ProfileQualityItemResource resource) + { + if (resource == null) return null; + + return new ProfileQualityItem + { + Quality = (Quality)resource.Quality.Id, + Allowed = resource.Allowed + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Profiles/ProfileSchemaModule.cs b/src/NzbDrone.Api/Profiles/ProfileSchemaModule.cs index baa115cfe..ec5f3ae01 100644 --- a/src/NzbDrone.Api/Profiles/ProfileSchemaModule.cs +++ b/src/NzbDrone.Api/Profiles/ProfileSchemaModule.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using NzbDrone.Api.Mapping; using NzbDrone.Core.Parser; using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; @@ -31,7 +30,7 @@ namespace NzbDrone.Api.Profiles profile.Items = items; profile.Language = Language.English; - return new List { profile.InjectTo() }; + return new List { profile.ToResource() }; } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/ProviderModuleBase.cs b/src/NzbDrone.Api/ProviderModuleBase.cs index f40c52a98..a018a0744 100644 --- a/src/NzbDrone.Api/ProviderModuleBase.cs +++ b/src/NzbDrone.Api/ProviderModuleBase.cs @@ -6,11 +6,9 @@ using FluentValidation.Results; using Nancy; using NzbDrone.Api.ClientSchema; using NzbDrone.Api.Extensions; -using NzbDrone.Api.Mapping; using NzbDrone.Common.Reflection; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; -using Omu.ValueInjecter; using Newtonsoft.Json; namespace NzbDrone.Api @@ -48,9 +46,10 @@ namespace NzbDrone.Api private TProviderResource GetProviderById(int id) { var definition = _providerFactory.Get(id); - var resource = definition.InjectTo(); + _providerFactory.SetProviderCharacteristics(definition); - resource.InjectFrom(_providerFactory.GetProviderCharacteristics(_providerFactory.GetInstance(definition), definition)); + var resource = new TProviderResource(); + MapToResource(resource, definition); return resource; } @@ -63,10 +62,10 @@ namespace NzbDrone.Api foreach (var definition in providerDefinitions) { + _providerFactory.SetProviderCharacteristics(definition); + var providerResource = new TProviderResource(); - providerResource.InjectFrom(definition); - providerResource.InjectFrom(_providerFactory.GetProviderCharacteristics(_providerFactory.GetInstance(definition), definition)); - providerResource.Fields = SchemaBuilder.ToSchema(definition.Settings); + MapToResource(providerResource, definition); result.Add(providerResource); } @@ -99,15 +98,8 @@ namespace NzbDrone.Api { var definition = new TProviderDefinition(); - definition.InjectFrom(providerResource); + MapToModel(definition, providerResource); - var preset = _providerFactory.GetPresetDefinitions(definition) - .Where(v => v.Name == definition.Name) - .Select(v => v.Settings) - .FirstOrDefault(); - - var configContract = ReflectionExtensions.CoreAssembly.FindTypeByName(definition.ConfigContract); - definition.Settings = (IProviderConfig)SchemaBuilder.ReadFormSchema(providerResource.Fields, configContract, preset); if (validate) { Validate(definition, includeWarnings); @@ -116,6 +108,37 @@ namespace NzbDrone.Api return definition; } + protected virtual void MapToResource(TProviderResource resource, TProviderDefinition definition) + { + resource.Id = definition.Id; + + resource.Name = definition.Name; + resource.ImplementationName = definition.ImplementationName; + resource.Implementation = definition.Implementation; + resource.ConfigContract = definition.ConfigContract; + resource.Message = definition.Message; + + resource.Fields = SchemaBuilder.ToSchema(definition.Settings); + + resource.InfoLink = string.Format("https://github.com/Sonarr/Sonarr/wiki/Supported-{0}#{1}", + typeof(TProviderResource).Name.Replace("Resource", "s"), + definition.Implementation.ToLower()); + } + + protected virtual void MapToModel(TProviderDefinition definition, TProviderResource resource) + { + definition.Id = resource.Id; + + definition.Name = resource.Name; + definition.ImplementationName = resource.ImplementationName; + definition.Implementation = resource.Implementation; + definition.ConfigContract = resource.ConfigContract; + definition.Message = resource.Message; + + var configContract = ReflectionExtensions.CoreAssembly.FindTypeByName(definition.ConfigContract); + definition.Settings = (IProviderConfig)SchemaBuilder.ReadFromSchema(resource.Fields, configContract); + } + private void DeleteProvider(int id) { _providerFactory.Delete(id); @@ -130,19 +153,14 @@ namespace NzbDrone.Api foreach (var providerDefinition in defaultDefinitions) { var providerResource = new TProviderResource(); - providerResource.InjectFrom(providerDefinition); - providerResource.Fields = SchemaBuilder.ToSchema(providerDefinition.Settings); - providerResource.InfoLink = string.Format("https://github.com/NzbDrone/NzbDrone/wiki/Supported-{0}#{1}", - typeof(TProviderResource).Name.Replace("Resource", "s"), - providerDefinition.Implementation.ToLower()); + MapToResource(providerResource, providerDefinition); var presetDefinitions = _providerFactory.GetPresetDefinitions(providerDefinition); providerResource.Presets = presetDefinitions.Select(v => { var presetResource = new TProviderResource(); - presetResource.InjectFrom(v); - presetResource.Fields = SchemaBuilder.ToSchema(v.Settings); + MapToResource(presetResource, v); return presetResource as ProviderResource; }).ToList(); @@ -167,7 +185,7 @@ namespace NzbDrone.Api private Response ConnectData(string stage, TProviderResource providerResource) { - TProviderDefinition providerDefinition = GetDefinition(providerResource, true, false); + var providerDefinition = GetDefinition(providerResource, true, false); if (!providerDefinition.Enable) return "{}"; diff --git a/src/NzbDrone.Api/Qualities/QualityDefinitionModule.cs b/src/NzbDrone.Api/Qualities/QualityDefinitionModule.cs index 89c7060ad..1b5351300 100644 --- a/src/NzbDrone.Api/Qualities/QualityDefinitionModule.cs +++ b/src/NzbDrone.Api/Qualities/QualityDefinitionModule.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using NzbDrone.Core.Qualities; -using NzbDrone.Api.Mapping; namespace NzbDrone.Api.Qualities { @@ -21,18 +20,18 @@ namespace NzbDrone.Api.Qualities private void Update(QualityDefinitionResource resource) { - var model = resource.InjectTo(); + var model = resource.ToModel(); _qualityDefinitionService.Update(model); } private QualityDefinitionResource GetById(int id) { - return _qualityDefinitionService.GetById(id).InjectTo(); + return _qualityDefinitionService.GetById(id).ToResource(); } private List GetAll() { - return ToListResource(_qualityDefinitionService.All); + return _qualityDefinitionService.All().ToResource(); } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Qualities/QualityDefinitionResource.cs b/src/NzbDrone.Api/Qualities/QualityDefinitionResource.cs index 7c65ed777..397144a8c 100644 --- a/src/NzbDrone.Api/Qualities/QualityDefinitionResource.cs +++ b/src/NzbDrone.Api/Qualities/QualityDefinitionResource.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using NzbDrone.Api.REST; using NzbDrone.Core.Qualities; @@ -15,4 +17,50 @@ namespace NzbDrone.Api.Qualities public double? MinSize { get; set; } public double? MaxSize { get; set; } } + + public static class QualityDefinitionResourceMapper + { + public static QualityDefinitionResource ToResource(this QualityDefinition model) + { + if (model == null) return null; + + return new QualityDefinitionResource + { + Id = model.Id, + + Quality = model.Quality, + + Title = model.Title, + + Weight = model.Weight, + + MinSize = model.MinSize, + MaxSize = model.MaxSize + }; + } + + public static QualityDefinition ToModel(this QualityDefinitionResource resource) + { + if (resource == null) return null; + + return new QualityDefinition + { + Id = resource.Id, + + Quality = resource.Quality, + + Title = resource.Title, + + Weight = resource.Weight, + + MinSize = resource.MinSize, + MaxSize = resource.MaxSize + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Queue/QueueModule.cs b/src/NzbDrone.Api/Queue/QueueModule.cs index e3de5940e..00e614132 100644 --- a/src/NzbDrone.Api/Queue/QueueModule.cs +++ b/src/NzbDrone.Api/Queue/QueueModule.cs @@ -24,7 +24,7 @@ namespace NzbDrone.Api.Queue private List GetQueue() { - return ToListResource(GetQueueItems); + return GetQueueItems().ToResource(); } private IEnumerable GetQueueItems() diff --git a/src/NzbDrone.Api/Queue/QueueResource.cs b/src/NzbDrone.Api/Queue/QueueResource.cs index e9cab3a9f..cf1356c49 100644 --- a/src/NzbDrone.Api/Queue/QueueResource.cs +++ b/src/NzbDrone.Api/Queue/QueueResource.cs @@ -6,6 +6,7 @@ using NzbDrone.Api.Series; using NzbDrone.Api.Episodes; using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.Indexers; +using System.Linq; namespace NzbDrone.Api.Queue { @@ -25,4 +26,36 @@ namespace NzbDrone.Api.Queue public string DownloadId { get; set; } public DownloadProtocol Protocol { get; set; } } + + public static class QueueResourceMapper + { + public static QueueResource ToResource(this Core.Queue.Queue model) + { + if (model == null) return null; + + return new QueueResource + { + Id = model.Id, + + Series = model.Series.ToResource(), + Episode = model.Episode.ToResource(), + Quality = model.Quality, + Size = model.Size, + Title = model.Title, + Sizeleft = model.Sizeleft, + Timeleft = model.Timeleft, + EstimatedCompletionTime = model.EstimatedCompletionTime, + Status = model.Status, + TrackedDownloadStatus = model.TrackedDownloadStatus, + StatusMessages = model.StatusMessages, + DownloadId = model.DownloadId, + Protocol = model.Protocol + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/RemotePathMappings/RemotePathMappingModule.cs b/src/NzbDrone.Api/RemotePathMappings/RemotePathMappingModule.cs index a9dd2472b..a61b5f7b3 100644 --- a/src/NzbDrone.Api/RemotePathMappings/RemotePathMappingModule.cs +++ b/src/NzbDrone.Api/RemotePathMappings/RemotePathMappingModule.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using FluentValidation; -using NzbDrone.Api.Mapping; using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.Validation.Paths; -using Omu.ValueInjecter; namespace NzbDrone.Api.RemotePathMappings { @@ -39,17 +37,19 @@ namespace NzbDrone.Api.RemotePathMappings private RemotePathMappingResource GetMappingById(int id) { - return _remotePathMappingService.Get(id).InjectTo(); + return _remotePathMappingService.Get(id).ToResource(); } - private int CreateMapping(RemotePathMappingResource rootFolderResource) + private int CreateMapping(RemotePathMappingResource resource) { - return GetNewId(_remotePathMappingService.Add, rootFolderResource); + var model = resource.ToModel(); + + return _remotePathMappingService.Add(model).Id; } private List GetMappings() { - return ToListResource(_remotePathMappingService.All); + return _remotePathMappingService.All().ToResource(); } private void DeleteMapping(int id) @@ -59,9 +59,7 @@ namespace NzbDrone.Api.RemotePathMappings private void UpdateMapping(RemotePathMappingResource resource) { - var mapping = _remotePathMappingService.Get(resource.Id); - - mapping.InjectFrom(resource); + var mapping = resource.ToModel(); _remotePathMappingService.Update(mapping); } diff --git a/src/NzbDrone.Api/RemotePathMappings/RemotePathMappingResource.cs b/src/NzbDrone.Api/RemotePathMappings/RemotePathMappingResource.cs index 399282318..0d3c73778 100644 --- a/src/NzbDrone.Api/RemotePathMappings/RemotePathMappingResource.cs +++ b/src/NzbDrone.Api/RemotePathMappings/RemotePathMappingResource.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; +using System.Linq; using NzbDrone.Api.REST; +using NzbDrone.Core.RemotePathMappings; namespace NzbDrone.Api.RemotePathMappings { @@ -9,4 +12,40 @@ namespace NzbDrone.Api.RemotePathMappings public string RemotePath { get; set; } public string LocalPath { get; set; } } + + public static class RemotePathMappingResourceMapper + { + public static RemotePathMappingResource ToResource(this RemotePathMapping model) + { + if (model == null) return null; + + return new RemotePathMappingResource + { + Id = model.Id, + + Host = model.Host, + RemotePath = model.RemotePath, + LocalPath = model.LocalPath + }; + } + + public static RemotePathMapping ToModel(this RemotePathMappingResource resource) + { + if (resource == null) return null; + + return new RemotePathMapping + { + Id = resource.Id, + + Host = resource.Host, + RemotePath = resource.RemotePath, + LocalPath = resource.LocalPath + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/Restrictions/RestrictionModule.cs b/src/NzbDrone.Api/Restrictions/RestrictionModule.cs index 0e9943210..353409692 100644 --- a/src/NzbDrone.Api/Restrictions/RestrictionModule.cs +++ b/src/NzbDrone.Api/Restrictions/RestrictionModule.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using FluentValidation.Results; -using NzbDrone.Api.Mapping; using NzbDrone.Common.Extensions; using NzbDrone.Core.Restrictions; @@ -16,11 +15,11 @@ namespace NzbDrone.Api.Restrictions { _restrictionService = restrictionService; - GetResourceById = Get; - GetResourceAll = GetAll; - CreateResource = Create; - UpdateResource = Update; - DeleteResource = Delete; + GetResourceById = GetRestriction; + GetResourceAll = GetAllRestrictions; + CreateResource = CreateRestriction; + UpdateResource = UpdateRestriction; + DeleteResource = DeleteRestriction; SharedValidator.Custom(restriction => { @@ -33,27 +32,27 @@ namespace NzbDrone.Api.Restrictions }); } - private RestrictionResource Get(int id) + private RestrictionResource GetRestriction(int id) { - return _restrictionService.Get(id).InjectTo(); + return _restrictionService.Get(id).ToResource(); } - private List GetAll() + private List GetAllRestrictions() { - return ToListResource(_restrictionService.All); + return _restrictionService.All().ToResource(); } - private int Create(RestrictionResource resource) + private int CreateRestriction(RestrictionResource resource) { - return _restrictionService.Add(resource.InjectTo()).Id; + return _restrictionService.Add(resource.ToModel()).Id; } - private void Update(RestrictionResource resource) + private void UpdateRestriction(RestrictionResource resource) { - _restrictionService.Update(resource.InjectTo()); + _restrictionService.Update(resource.ToModel()); } - private void Delete(int id) + private void DeleteRestriction(int id) { _restrictionService.Delete(id); } diff --git a/src/NzbDrone.Api/Restrictions/RestrictionResource.cs b/src/NzbDrone.Api/Restrictions/RestrictionResource.cs index da7013fd1..e2c961cb3 100644 --- a/src/NzbDrone.Api/Restrictions/RestrictionResource.cs +++ b/src/NzbDrone.Api/Restrictions/RestrictionResource.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using NzbDrone.Api.REST; +using NzbDrone.Core.Restrictions; namespace NzbDrone.Api.Restrictions { @@ -16,4 +18,42 @@ namespace NzbDrone.Api.Restrictions Tags = new HashSet(); } } + + public static class RestrictionResourceMapper + { + public static RestrictionResource ToResource(this Restriction model) + { + if (model == null) return null; + + return new RestrictionResource + { + Id = model.Id, + + Required = model.Required, + Preferred = model.Preferred, + Ignored = model.Ignored, + Tags = new HashSet(model.Tags) + }; + } + + public static Restriction ToModel(this RestrictionResource resource) + { + if (resource == null) return null; + + return new Restriction + { + Id = resource.Id, + + Required = resource.Required, + Preferred = resource.Preferred, + Ignored = resource.Ignored, + Tags = new HashSet(resource.Tags) + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/RootFolders/RootFolderModule.cs b/src/NzbDrone.Api/RootFolders/RootFolderModule.cs index a2c4daed8..e87e581de 100644 --- a/src/NzbDrone.Api/RootFolders/RootFolderModule.cs +++ b/src/NzbDrone.Api/RootFolders/RootFolderModule.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using FluentValidation; using NzbDrone.Core.RootFolders; -using NzbDrone.Api.Mapping; using NzbDrone.Core.Validation.Paths; using NzbDrone.SignalR; @@ -41,17 +40,19 @@ namespace NzbDrone.Api.RootFolders private RootFolderResource GetRootFolder(int id) { - return _rootFolderService.Get(id).InjectTo(); + return _rootFolderService.Get(id).ToResource(); } private int CreateRootFolder(RootFolderResource rootFolderResource) { - return GetNewId(_rootFolderService.Add, rootFolderResource); + var model = rootFolderResource.ToModel(); + + return _rootFolderService.Add(model).Id; } private List GetRootFolders() { - return ToListResource(_rootFolderService.AllWithUnmappedFolders); + return _rootFolderService.AllWithUnmappedFolders().ToResource(); } private void DeleteFolder(int id) diff --git a/src/NzbDrone.Api/RootFolders/RootFolderResource.cs b/src/NzbDrone.Api/RootFolders/RootFolderResource.cs index 4bd5ad833..3fd8703f0 100644 --- a/src/NzbDrone.Api/RootFolders/RootFolderResource.cs +++ b/src/NzbDrone.Api/RootFolders/RootFolderResource.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using NzbDrone.Api.REST; using NzbDrone.Core.RootFolders; @@ -12,4 +13,40 @@ namespace NzbDrone.Api.RootFolders public List UnmappedFolders { get; set; } } + + public static class RootFolderResourceMapper + { + public static RootFolderResource ToResource(this RootFolder model) + { + if (model == null) return null; + + return new RootFolderResource + { + Id = model.Id, + + Path = model.Path, + FreeSpace = model.FreeSpace, + UnmappedFolders = model.UnmappedFolders + }; + } + + public static RootFolder ToModel(this RootFolderResource resource) + { + if (resource == null) return null; + + return new RootFolder + { + Id = resource.Id, + + Path = resource.Path, + //FreeSpace + //UnmappedFolders + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } \ No newline at end of file diff --git a/src/NzbDrone.Api/SeasonPass/SeasonPassModule.cs b/src/NzbDrone.Api/SeasonPass/SeasonPassModule.cs index eda106619..6024ab1e0 100644 --- a/src/NzbDrone.Api/SeasonPass/SeasonPassModule.cs +++ b/src/NzbDrone.Api/SeasonPass/SeasonPassModule.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using Nancy; using NzbDrone.Api.Extensions; -using NzbDrone.Api.Mapping; using NzbDrone.Core.Tv; namespace NzbDrone.Api.SeasonPass diff --git a/src/NzbDrone.Api/Series/AlternateTitleResource.cs b/src/NzbDrone.Api/Series/AlternateTitleResource.cs index 9cefcd842..b7d9050cc 100644 --- a/src/NzbDrone.Api/Series/AlternateTitleResource.cs +++ b/src/NzbDrone.Api/Series/AlternateTitleResource.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Api.Series public class AlternateTitleResource { public string Title { get; set; } - public int SeasonNumber { get; set; } - public int SceneSeasonNumber { get; set; } + public int? SeasonNumber { get; set; } + public int? SceneSeasonNumber { get; set; } } } diff --git a/src/NzbDrone.Api/Series/SeasonResource.cs b/src/NzbDrone.Api/Series/SeasonResource.cs index db6af1419..2231502d9 100644 --- a/src/NzbDrone.Api/Series/SeasonResource.cs +++ b/src/NzbDrone.Api/Series/SeasonResource.cs @@ -1,4 +1,7 @@ -namespace NzbDrone.Api.Series +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Core.Tv; +namespace NzbDrone.Api.Series { public class SeasonResource { @@ -6,4 +9,39 @@ public bool Monitored { get; set; } public SeasonStatisticsResource Statistics { get; set; } } + + public static class SeasonResourceMapper + { + public static SeasonResource ToResource(this Season model) + { + if (model == null) return null; + + return new SeasonResource + { + SeasonNumber = model.SeasonNumber, + Monitored = model.Monitored + }; + } + + public static Season ToModel(this SeasonResource resource) + { + if (resource == null) return null; + + return new Season + { + SeasonNumber = resource.SeasonNumber, + Monitored = resource.Monitored + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + + public static List ToModel(this IEnumerable resources) + { + return resources.Select(ToModel).ToList(); + } + } } diff --git a/src/NzbDrone.Api/Series/SeasonStatisticsResource.cs b/src/NzbDrone.Api/Series/SeasonStatisticsResource.cs index aad49032c..34acc721e 100644 --- a/src/NzbDrone.Api/Series/SeasonStatisticsResource.cs +++ b/src/NzbDrone.Api/Series/SeasonStatisticsResource.cs @@ -1,4 +1,5 @@ using System; +using NzbDrone.Core.SeriesStats; namespace NzbDrone.Api.Series { @@ -20,6 +21,23 @@ namespace NzbDrone.Api.Series return (decimal)EpisodeFileCount / (decimal)EpisodeCount * 100; } } + } + public static class SeasonStatisticsResourceMapper + { + public static SeasonStatisticsResource ToResource(this SeasonStatistics model) + { + if (model == null) return null; + + return new SeasonStatisticsResource + { + NextAiring = model.NextAiring, + PreviousAiring = model.PreviousAiring, + EpisodeFileCount = model.EpisodeFileCount, + EpisodeCount = model.EpisodeFileCount, + TotalEpisodeCount = model.TotalEpisodeCount, + SizeOnDisk = model.SizeOnDisk + }; + } } } diff --git a/src/NzbDrone.Api/Series/SeriesEditorModule.cs b/src/NzbDrone.Api/Series/SeriesEditorModule.cs index 93e0ad810..87cd53113 100644 --- a/src/NzbDrone.Api/Series/SeriesEditorModule.cs +++ b/src/NzbDrone.Api/Series/SeriesEditorModule.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; +using System.Linq; using Nancy; using NzbDrone.Api.Extensions; -using NzbDrone.Api.Mapping; using NzbDrone.Core.Tv; namespace NzbDrone.Api.Series @@ -19,11 +19,12 @@ namespace NzbDrone.Api.Series private Response SaveAll() { - //Read from request - var series = Request.Body.FromJson>().InjectTo>(); + var resources = Request.Body.FromJson>(); + + var series = resources.Select(seriesResource => seriesResource.ToModel(_seriesService.GetSeries(seriesResource.Id))).ToList(); return _seriesService.UpdateSeries(series) - .InjectTo>() + .ToResource() .AsResponse(HttpStatusCode.Accepted); } } diff --git a/src/NzbDrone.Api/Series/SeriesLookupModule.cs b/src/NzbDrone.Api/Series/SeriesLookupModule.cs index f799754a9..6506c1f82 100644 --- a/src/NzbDrone.Api/Series/SeriesLookupModule.cs +++ b/src/NzbDrone.Api/Series/SeriesLookupModule.cs @@ -4,7 +4,6 @@ using NzbDrone.Api.Extensions; using NzbDrone.Core.MediaCover; using NzbDrone.Core.MetadataSource; using System.Linq; -using NzbDrone.Api.Mapping; namespace NzbDrone.Api.Series { @@ -31,7 +30,7 @@ namespace NzbDrone.Api.Series { foreach (var currentSeries in series) { - var resource = currentSeries.InjectTo(); + var resource = currentSeries.ToResource(); var poster = currentSeries.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster); if (poster != null) { diff --git a/src/NzbDrone.Api/Series/SeriesModule.cs b/src/NzbDrone.Api/Series/SeriesModule.cs index b52e41a4d..239598912 100644 --- a/src/NzbDrone.Api/Series/SeriesModule.cs +++ b/src/NzbDrone.Api/Series/SeriesModule.cs @@ -10,7 +10,6 @@ using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.SeriesStats; using NzbDrone.Core.Tv; -using NzbDrone.Api.Mapping; using NzbDrone.Core.Tv.Events; using NzbDrone.Core.Validation.Paths; using NzbDrone.Core.DataAugmentation.Scene; @@ -84,14 +83,14 @@ namespace NzbDrone.Api.Series private SeriesResource GetSeries(int id) { var series = _seriesService.GetSeries(id); - return GetSeriesResource(series); + return MapToResource(series); } - private SeriesResource GetSeriesResource(Core.Tv.Series series) + private SeriesResource MapToResource(Core.Tv.Series series) { if (series == null) return null; - var resource = series.InjectTo(); + var resource = series.ToResource(); MapCoversToLocal(resource); FetchAndLinkSeriesStatistics(resource); PopulateAlternateTitles(resource); @@ -102,7 +101,7 @@ namespace NzbDrone.Api.Series private List AllSeries() { var seriesStats = _seriesStatisticsService.SeriesStatistics(); - var seriesResources = ToListResource(_seriesService.GetAllSeries); + var seriesResources = _seriesService.GetAllSeries().ToResource(); MapCoversToLocal(seriesResources.ToArray()); LinkSeriesStatistics(seriesResources, seriesStats); @@ -113,14 +112,16 @@ namespace NzbDrone.Api.Series private int AddSeries(SeriesResource seriesResource) { - var series = _seriesService.AddSeries(seriesResource.InjectTo()); + var model = seriesResource.ToModel(); - return series.Id; + return _seriesService.AddSeries(model).Id; } private void UpdateSeries(SeriesResource seriesResource) { - GetNewId(_seriesService.UpdateSeries, seriesResource); + var model = seriesResource.ToModel(_seriesService.GetSeries(seriesResource.Id)); + + _seriesService.UpdateSeries(model); BroadcastResourceChange(ModelAction.Updated, seriesResource); } @@ -153,9 +154,11 @@ namespace NzbDrone.Api.Series private void LinkSeriesStatistics(List resources, List seriesStatistics) { + var dictSeriesStats = seriesStatistics.ToDictionary(v => v.SeriesId); + foreach (var series in resources) { - var stats = seriesStatistics.SingleOrDefault(ss => ss.SeriesId == series.Id); + var stats = dictSeriesStats.GetValueOrDefault(series.Id); if (stats == null) continue; LinkSeriesStatistics(series, stats); @@ -173,9 +176,11 @@ namespace NzbDrone.Api.Series if (seriesStatistics.SeasonStatistics != null) { - foreach (var season in resource.Seasons) + var dictSeasonStats = seriesStatistics.SeasonStatistics.ToDictionary(v => v.SeasonNumber); + + foreach (var season in resource.Seasons) { - season.Statistics = seriesStatistics.SeasonStatistics.SingleOrDefault(s => s.SeasonNumber == season.SeasonNumber).InjectTo(); + season.Statistics = SeasonStatisticsResourceMapper.ToResource(dictSeasonStats.GetValueOrDefault(season.SeasonNumber)); } } } @@ -194,7 +199,7 @@ namespace NzbDrone.Api.Series if (mappings == null) return; - resource.AlternateTitles = mappings.InjectTo>(); + resource.AlternateTitles = mappings.Select(v => new AlternateTitleResource { Title = v.Title, SeasonNumber = v.SeasonNumber, SceneSeasonNumber = v.SceneSeasonNumber }).ToList(); } public void Handle(EpisodeImportedEvent message) @@ -221,7 +226,7 @@ namespace NzbDrone.Api.Series public void Handle(SeriesDeletedEvent message) { - BroadcastResourceChange(ModelAction.Deleted, message.Series.InjectTo()); + BroadcastResourceChange(ModelAction.Deleted, message.Series.ToResource()); } public void Handle(SeriesRenamedEvent message) diff --git a/src/NzbDrone.Api/Series/SeriesResource.cs b/src/NzbDrone.Api/Series/SeriesResource.cs index 2dbcfd977..6a5f749a7 100644 --- a/src/NzbDrone.Api/Series/SeriesResource.cs +++ b/src/NzbDrone.Api/Series/SeriesResource.cs @@ -33,7 +33,6 @@ namespace NzbDrone.Api.Series public int? EpisodeFileCount { get; set; } public long? SizeOnDisk { get; set; } public SeriesStatusType Status { get; set; } - public string ProfileName { get; set; } public string Overview { get; set; } public DateTime? NextAiring { get; set; } public DateTime? PreviousAiring { get; set; } @@ -90,4 +89,139 @@ namespace NzbDrone.Api.Series } } } + + public static class SeriesResourceMapper + { + public static SeriesResource ToResource(this Core.Tv.Series model) + { + if (model == null) return null; + + return new SeriesResource + { + Id = model.Id, + + Title = model.Title, + //AlternateTitles + SortTitle = model.SortTitle, + + //TotalEpisodeCount + //EpisodeCount + //EpisodeFileCount + //SizeOnDisk + Status = model.Status, + Overview = model.Overview, + //NextAiring + //PreviousAiring + Network = model.Network, + AirTime = model.AirTime, + Images = model.Images, + + Seasons = model.Seasons.ToResource(), + Year = model.Year, + + Path = model.Path, + ProfileId = model.ProfileId, + + SeasonFolder = model.SeasonFolder, + Monitored = model.Monitored, + + UseSceneNumbering = model.UseSceneNumbering, + Runtime = model.Runtime, + TvdbId = model.TvdbId, + TvRageId = model.TvRageId, + TvMazeId = model.TvMazeId, + FirstAired = model.FirstAired, + LastInfoSync = model.LastInfoSync, + SeriesType = model.SeriesType, + CleanTitle = model.CleanTitle, + ImdbId = model.ImdbId, + TitleSlug = model.TitleSlug, + RootFolderPath = model.RootFolderPath, + Certification = model.Certification, + Genres = model.Genres, + Tags = model.Tags, + Added = model.Added, + AddOptions = model.AddOptions, + Ratings = model.Ratings + }; + } + + public static Core.Tv.Series ToModel(this SeriesResource resource) + { + if (resource == null) return null; + + return new Core.Tv.Series + { + Id = resource.Id, + + Title = resource.Title, + //AlternateTitles + SortTitle = resource.SortTitle, + + //TotalEpisodeCount + //EpisodeCount + //EpisodeFileCount + //SizeOnDisk + Status = resource.Status, + Overview = resource.Overview, + //NextAiring + //PreviousAiring + Network = resource.Network, + AirTime = resource.AirTime, + Images = resource.Images, + + Seasons = resource.Seasons.ToModel(), + Year = resource.Year, + + Path = resource.Path, + ProfileId = resource.ProfileId, + + SeasonFolder = resource.SeasonFolder, + Monitored = resource.Monitored, + + UseSceneNumbering = resource.UseSceneNumbering, + Runtime = resource.Runtime, + TvdbId = resource.TvdbId, + TvRageId = resource.TvRageId, + TvMazeId = resource.TvMazeId, + FirstAired = resource.FirstAired, + LastInfoSync = resource.LastInfoSync, + SeriesType = resource.SeriesType, + CleanTitle = resource.CleanTitle, + ImdbId = resource.ImdbId, + TitleSlug = resource.TitleSlug, + RootFolderPath = resource.RootFolderPath, + Certification = resource.Certification, + Genres = resource.Genres, + Tags = resource.Tags, + Added = resource.Added, + AddOptions = resource.AddOptions, + Ratings = resource.Ratings + }; + } + + public static Core.Tv.Series ToModel(this SeriesResource resource, Core.Tv.Series series) + { + series.TvdbId = resource.TvdbId; + + series.Seasons = resource.Seasons.ToModel(); + series.Path = resource.Path; + series.ProfileId = resource.ProfileId; + + series.SeasonFolder = resource.SeasonFolder; + series.Monitored = resource.Monitored; + + series.SeriesType = resource.SeriesType; + series.RootFolderPath = resource.RootFolderPath; + series.Tags = resource.Tags; + series.AddOptions = resource.AddOptions; + + return series; + } + + public static List ToResource(this IEnumerable series) + { + return series.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/Tags/TagModule.cs b/src/NzbDrone.Api/Tags/TagModule.cs index 846c82b11..4dea67108 100644 --- a/src/NzbDrone.Api/Tags/TagModule.cs +++ b/src/NzbDrone.Api/Tags/TagModule.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using NzbDrone.Api.Mapping; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tags; @@ -18,34 +17,38 @@ namespace NzbDrone.Api.Tags { _tagService = tagService; - GetResourceById = Get; - GetResourceAll = GetAll; - CreateResource = Create; - UpdateResource = Update; - DeleteResource = Delete; + GetResourceById = GetTag; + GetResourceAll = GetAllTags; + CreateResource = CreateTag; + UpdateResource = UpdateTag; + DeleteResource = DeleteTag; } - private TagResource Get(int id) + private TagResource GetTag(int id) { - return _tagService.GetTag(id).InjectTo(); + return _tagService.GetTag(id).ToResource(); } - private List GetAll() + private List GetAllTags() { - return ToListResource(_tagService.All); + return _tagService.All().ToResource(); } - private int Create(TagResource resource) + private int CreateTag(TagResource resource) { - return _tagService.Add(resource.InjectTo()).Id; + var model = resource.ToModel(); + + return _tagService.Add(model).Id; } - private void Update(TagResource resource) + private void UpdateTag(TagResource resource) { - _tagService.Update(resource.InjectTo()); + var model = resource.ToModel(); + + _tagService.Update(model); } - private void Delete(int id) + private void DeleteTag(int id) { _tagService.Delete(id); } diff --git a/src/NzbDrone.Api/Tags/TagResource.cs b/src/NzbDrone.Api/Tags/TagResource.cs index b42fd6f9a..d095f5460 100644 --- a/src/NzbDrone.Api/Tags/TagResource.cs +++ b/src/NzbDrone.Api/Tags/TagResource.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; +using System.Linq; using NzbDrone.Api.REST; +using NzbDrone.Core.Tags; namespace NzbDrone.Api.Tags { @@ -7,4 +10,36 @@ namespace NzbDrone.Api.Tags { public string Label { get; set; } } + + public static class TagResourceMapper + { + public static TagResource ToResource(this Tag model) + { + if (model == null) return null; + + return new TagResource + { + Id = model.Id, + + Label = model.Label + }; + } + + public static Tag ToModel(this TagResource resource) + { + if (resource == null) return null; + + return new Tag + { + Id = resource.Id, + + Label = resource.Label + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/Update/UpdateModule.cs b/src/NzbDrone.Api/Update/UpdateModule.cs index 2cd9c0743..2104f23ea 100644 --- a/src/NzbDrone.Api/Update/UpdateModule.cs +++ b/src/NzbDrone.Api/Update/UpdateModule.cs @@ -2,7 +2,6 @@ using System.Linq; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Update; -using NzbDrone.Api.Mapping; namespace NzbDrone.Api.Update { @@ -20,7 +19,7 @@ namespace NzbDrone.Api.Update { var resources = _recentUpdateProvider.GetRecentUpdatePackages() .OrderByDescending(u => u.Version) - .InjectTo>(); + .ToResource(); if (resources.Any()) { diff --git a/src/NzbDrone.Api/Update/UpdateResource.cs b/src/NzbDrone.Api/Update/UpdateResource.cs index 7da017161..dca6f6725 100644 --- a/src/NzbDrone.Api/Update/UpdateResource.cs +++ b/src/NzbDrone.Api/Update/UpdateResource.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using NzbDrone.Api.REST; using NzbDrone.Core.Update; @@ -20,4 +22,32 @@ namespace NzbDrone.Api.Update public UpdateChanges Changes { get; set; } public string Hash { get; set; } } + + public static class UpdateResourceMapper + { + public static UpdateResource ToResource(this UpdatePackage model) + { + if (model == null) return null; + + return new UpdateResource + { + Version = model.Version, + + Branch = model.Branch, + ReleaseDate = model.ReleaseDate, + FileName = model.FileName, + Url = model.Url, + //Installed + //Installable + //Latest + Changes = model.Changes, + Hash = model.Hash, + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } } diff --git a/src/NzbDrone.Api/Wanted/CutoffModule.cs b/src/NzbDrone.Api/Wanted/CutoffModule.cs index e3697781f..13725e2bb 100644 --- a/src/NzbDrone.Api/Wanted/CutoffModule.cs +++ b/src/NzbDrone.Api/Wanted/CutoffModule.cs @@ -40,7 +40,7 @@ namespace NzbDrone.Api.Wanted pagingSpec.FilterExpression = v => v.Monitored == true && v.Series.Monitored == true; } - PagingResource resource = ApplyToPage(_episodeCutoffService.EpisodesWhereCutoffUnmet, pagingSpec); + var resource = ApplyToPage(_episodeCutoffService.EpisodesWhereCutoffUnmet, pagingSpec, v => MapToResource(v, true, true)); return resource; } diff --git a/src/NzbDrone.Api/Wanted/MissingModule.cs b/src/NzbDrone.Api/Wanted/MissingModule.cs index 260db22e6..7fb31b000 100644 --- a/src/NzbDrone.Api/Wanted/MissingModule.cs +++ b/src/NzbDrone.Api/Wanted/MissingModule.cs @@ -36,7 +36,7 @@ namespace NzbDrone.Api.Wanted pagingSpec.FilterExpression = v => v.Monitored == true && v.Series.Monitored == true; } - PagingResource resource = ApplyToPage(v => _episodeService.EpisodesWithoutFiles(v), pagingSpec); + var resource = ApplyToPage(_episodeService.EpisodesWithoutFiles, pagingSpec, v => MapToResource(v, true, false)); return resource; } diff --git a/src/NzbDrone.Api/packages.config b/src/NzbDrone.Api/packages.config index 45aa1db24..cb1774c30 100644 --- a/src/NzbDrone.Api/packages.config +++ b/src/NzbDrone.Api/packages.config @@ -7,5 +7,4 @@ - \ No newline at end of file diff --git a/src/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs b/src/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs index 1e443c23e..22c4824af 100644 --- a/src/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs @@ -27,7 +27,6 @@ namespace NzbDrone.Core.DecisionEngine int compare = new QualityModelComparer(profile).Compare(newQuality, currentQuality); if (compare <= 0) { - _logger.Debug("existing item has better or equal quality. skipping"); return false; } @@ -42,29 +41,28 @@ namespace NzbDrone.Core.DecisionEngine public bool CutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null) { - int compare = new QualityModelComparer(profile).Compare(currentQuality.Quality, profile.Cutoff); + var compare = new QualityModelComparer(profile).Compare(currentQuality.Quality, profile.Cutoff); - if (compare >= 0) + if (compare < 0) { - if (newQuality != null && IsRevisionUpgrade(currentQuality, newQuality)) - { - return true; - } - - _logger.Debug("Existing item meets cut-off. skipping."); - return false; + return true; } - return true; + if (newQuality != null && IsRevisionUpgrade(currentQuality, newQuality)) + { + return true; + } + + return false; + } public bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality) { - int compare = newQuality.Revision.CompareTo(currentQuality.Revision); + var compare = newQuality.Revision.CompareTo(currentQuality.Revision); if (currentQuality.Quality == newQuality.Quality && compare > 0) { - _logger.Debug("New quality is a better revision for existing quality"); return true; } diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/ScanWatchFolder.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/ScanWatchFolder.cs index 8d28fbd6a..d6e80e3fd 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/ScanWatchFolder.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/ScanWatchFolder.cs @@ -164,7 +164,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole } catch (Exception ex) { - _logger.TraceException(string.Format("Ignored hashing error during scan for {0}", folder), ex); + _logger.Trace(ex, "Ignored hashing error during scan for {0}", folder); } foreach (var file in files.OrderBy(v => v)) @@ -187,7 +187,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole } catch (Exception ex) { - _logger.TraceException(string.Format("Ignored hashing error during scan for {0}", file), ex); + _logger.Trace(ex, "Ignored hashing error during scan for {0}", file); } return HashConverter.GetHash(data.ToString()).ToHexString(); diff --git a/src/NzbDrone.Core/Download/DownloadClientFactory.cs b/src/NzbDrone.Core/Download/DownloadClientFactory.cs index 08fe2ffe3..dc0f218b5 100644 --- a/src/NzbDrone.Core/Download/DownloadClientFactory.cs +++ b/src/NzbDrone.Core/Download/DownloadClientFactory.cs @@ -27,13 +27,11 @@ namespace NzbDrone.Core.Download return base.Active().Where(c => c.Enable).ToList(); } - public override DownloadClientDefinition GetProviderCharacteristics(IDownloadClient provider, DownloadClientDefinition definition) + public override void SetProviderCharacteristics(IDownloadClient provider, DownloadClientDefinition definition) { - definition = base.GetProviderCharacteristics(provider, definition); + base.SetProviderCharacteristics(provider, definition); definition.Protocol = provider.Protocol; - - return definition; } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Download/DownloadClientProvider.cs b/src/NzbDrone.Core/Download/DownloadClientProvider.cs index 8e0b3b475..5cb899806 100644 --- a/src/NzbDrone.Core/Download/DownloadClientProvider.cs +++ b/src/NzbDrone.Core/Download/DownloadClientProvider.cs @@ -37,7 +37,8 @@ namespace NzbDrone.Core.Download public IDownloadClient MapDownloadClient(IDownloadClient downloadClient) { - downloadClient.Definition = _downloadClientFactory.GetProviderCharacteristics(downloadClient, (DownloadClientDefinition)downloadClient.Definition); + _downloadClientFactory.SetProviderCharacteristics(downloadClient, (DownloadClientDefinition)downloadClient.Definition); + return downloadClient; } } diff --git a/src/NzbDrone.Core/HealthCheck/HealthCheck.cs b/src/NzbDrone.Core/HealthCheck/HealthCheck.cs index 23b2f7dcf..e46746d4b 100644 --- a/src/NzbDrone.Core/HealthCheck/HealthCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/HealthCheck.cs @@ -14,6 +14,10 @@ namespace NzbDrone.Core.HealthCheck public string Message { get; set; } public HttpUri WikiUrl { get; set; } + public HealthCheck() + { + } + public HealthCheck(Type source) { Source = source; diff --git a/src/NzbDrone.Core/Indexers/IndexerFactory.cs b/src/NzbDrone.Core/Indexers/IndexerFactory.cs index 39cf9c4a3..8e45a031c 100644 --- a/src/NzbDrone.Core/Indexers/IndexerFactory.cs +++ b/src/NzbDrone.Core/Indexers/IndexerFactory.cs @@ -37,15 +37,13 @@ namespace NzbDrone.Core.Indexers return base.Active().Where(c => c.Enable).ToList(); } - public override IndexerDefinition GetProviderCharacteristics(IIndexer provider, IndexerDefinition definition) + public override void SetProviderCharacteristics(IIndexer provider, IndexerDefinition definition) { - definition = base.GetProviderCharacteristics(provider, definition); + base.SetProviderCharacteristics(provider, definition); definition.Protocol = provider.Protocol; definition.SupportsRss = provider.SupportsRss; definition.SupportsSearch = provider.SupportsSearch; - - return definition; } public List RssEnabled() diff --git a/src/NzbDrone.Core/Notifications/NotificationFactory.cs b/src/NzbDrone.Core/Notifications/NotificationFactory.cs index ffc94a9cc..235a6ba3c 100644 --- a/src/NzbDrone.Core/Notifications/NotificationFactory.cs +++ b/src/NzbDrone.Core/Notifications/NotificationFactory.cs @@ -42,16 +42,14 @@ namespace NzbDrone.Core.Notifications return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnRename).ToList(); } - public override NotificationDefinition GetProviderCharacteristics(INotification provider, NotificationDefinition definition) + public override void SetProviderCharacteristics(INotification provider, NotificationDefinition definition) { - definition = base.GetProviderCharacteristics(provider, definition); + base.SetProviderCharacteristics(provider, definition); definition.SupportsOnGrab = provider.SupportsOnGrab; definition.SupportsOnDownload = provider.SupportsOnDownload; definition.SupportsOnUpgrade = provider.SupportsOnUpgrade; definition.SupportsOnRename = provider.SupportsOnRename; - - return definition; } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 07605932e..1245b562b 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -105,9 +105,6 @@ - - ..\packages\ValueInjecter.2.3.3\lib\net35\Omu.ValueInjecter.dll - ..\packages\Prowlin.0.9.4456.26422\lib\net40\Prowlin.dll diff --git a/src/NzbDrone.Core/Profiles/Delay/DelayProfileTagInUseValidator.cs b/src/NzbDrone.Core/Profiles/Delay/DelayProfileTagInUseValidator.cs index 3a5d2a5c7..4e7ad94fb 100644 --- a/src/NzbDrone.Core/Profiles/Delay/DelayProfileTagInUseValidator.cs +++ b/src/NzbDrone.Core/Profiles/Delay/DelayProfileTagInUseValidator.cs @@ -2,7 +2,6 @@ using System.Linq; using FluentValidation.Validators; using NzbDrone.Common.Extensions; -using Omu.ValueInjecter; namespace NzbDrone.Core.Profiles.Delay { @@ -20,14 +19,14 @@ namespace NzbDrone.Core.Profiles.Delay { if (context.PropertyValue == null) return true; - var delayProfile = new DelayProfile(); - delayProfile.InjectFrom(context.ParentContext.InstanceToValidate); + dynamic instance = context.ParentContext.InstanceToValidate; + var instanceId = (int)instance.Id; var collection = context.PropertyValue as HashSet; if (collection == null || collection.Empty()) return true; - return _delayProfileService.All().None(d => d.Id != delayProfile.Id && d.Tags.Intersect(collection).Any()); + return _delayProfileService.All().None(d => d.Id != instanceId && d.Tags.Intersect(collection).Any()); } } } diff --git a/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs b/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs index d3cc0dbe7..22d7771c7 100644 --- a/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs +++ b/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs @@ -16,7 +16,8 @@ namespace NzbDrone.Core.ThingiProvider void Delete(int id); IEnumerable GetDefaultDefinitions(); IEnumerable GetPresetDefinitions(TProviderDefinition providerDefinition); - TProviderDefinition GetProviderCharacteristics(TProvider provider, TProviderDefinition definition); + void SetProviderCharacteristics(TProviderDefinition definition); + void SetProviderCharacteristics(TProvider provider, TProviderDefinition definition); TProvider GetInstance(TProviderDefinition definition); ValidationResult Test(TProviderDefinition definition); object ConnectData(TProviderDefinition definition, string stage, IDictionary query ); diff --git a/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs b/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs index a9f0b0bc2..6aa20c4d1 100644 --- a/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs +++ b/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs @@ -58,7 +58,7 @@ namespace NzbDrone.Core.ThingiProvider }; } - definition = GetProviderCharacteristics(provider, definition); + SetProviderCharacteristics(provider, definition); yield return definition; } @@ -117,7 +117,8 @@ namespace NzbDrone.Core.ThingiProvider { var type = GetImplementation(definition); var instance = (TProvider)_container.Resolve(type); - instance.Definition = GetProviderCharacteristics(instance, definition); + instance.Definition = definition; + SetProviderCharacteristics(instance, definition); return instance; } @@ -144,12 +145,15 @@ namespace NzbDrone.Core.ThingiProvider return All().Where(c => c.Settings.Validate().IsValid).ToList(); } - public virtual TProviderDefinition GetProviderCharacteristics(TProvider provider, TProviderDefinition definition) + public void SetProviderCharacteristics(TProviderDefinition definition) + { + GetInstance(definition); + } + + public virtual void SetProviderCharacteristics(TProvider provider, TProviderDefinition definition) { definition.ImplementationName = provider.Name; definition.Message = provider.Message; - - return definition; } //TODO: Remove providers even if the ConfigContract can't be deserialized (this will fail to remove providers if the settings can't be deserialized). diff --git a/src/NzbDrone.Core/Validation/Paths/SeriesPathValidator.cs b/src/NzbDrone.Core/Validation/Paths/SeriesPathValidator.cs index e39e24781..fa4d8fa59 100644 --- a/src/NzbDrone.Core/Validation/Paths/SeriesPathValidator.cs +++ b/src/NzbDrone.Core/Validation/Paths/SeriesPathValidator.cs @@ -1,7 +1,6 @@ using FluentValidation.Validators; using NzbDrone.Common.Extensions; using NzbDrone.Core.Tv; -using Omu.ValueInjecter; namespace NzbDrone.Core.Validation.Paths { @@ -19,12 +18,10 @@ namespace NzbDrone.Core.Validation.Paths { if (context.PropertyValue == null) return true; - var series = new Series(); - series.InjectFrom(context.ParentContext.InstanceToValidate); + dynamic instance = context.ParentContext.InstanceToValidate; + var instanceId = (int)instance.Id; - if (series.Id == 0) return true; - - return (!_seriesService.GetAllSeries().Exists(s => s.Path.PathEquals(context.PropertyValue.ToString()) && s.Id != series.Id)); + return (!_seriesService.GetAllSeries().Exists(s => s.Path.PathEquals(context.PropertyValue.ToString()) && s.Id != instanceId)); } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/packages.config b/src/NzbDrone.Core/packages.config index d235d92d8..3d53759f9 100644 --- a/src/NzbDrone.Core/packages.config +++ b/src/NzbDrone.Core/packages.config @@ -10,6 +10,5 @@ - \ No newline at end of file diff --git a/src/NzbDrone.Integration.Test/ApiTests/CalendarFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/CalendarFixture.cs index 7c3014629..256dabb71 100644 --- a/src/NzbDrone.Integration.Test/ApiTests/CalendarFixture.cs +++ b/src/NzbDrone.Integration.Test/ApiTests/CalendarFixture.cs @@ -15,8 +15,6 @@ namespace NzbDrone.Integration.Test.ApiTests { public ClientBase Calendar; - private SeriesResource _series; - protected override void InitRestClients() { base.InitRestClients(); @@ -27,14 +25,14 @@ namespace NzbDrone.Integration.Test.ApiTests [Test] public void should_be_able_to_get_episodes() { - _series = EnsureSeries(266189, "The Blacklist", true); + var series = EnsureSeries(266189, "The Blacklist", true); var request = Calendar.BuildRequest(); request.AddParameter("start", new DateTime(2015, 10, 1).ToString("s") + "Z"); request.AddParameter("end", new DateTime(2015, 10, 3).ToString("s") + "Z"); var items = Calendar.Get>(request); - items = items.Where(v => v.SeriesId == _series.Id).ToList(); + items = items.Where(v => v.SeriesId == series.Id).ToList(); items.Should().HaveCount(1); items.First().Title.Should().Be("The Troll Farmer"); @@ -43,7 +41,7 @@ namespace NzbDrone.Integration.Test.ApiTests [Test] public void should_not_be_able_to_get_unmonitored_episodes() { - _series = EnsureSeries(266189, "The Blacklist", false); + var series = EnsureSeries(266189, "The Blacklist", false); var request = Calendar.BuildRequest(); request.AddParameter("start", new DateTime(2015, 10, 1).ToString("s") + "Z"); @@ -51,7 +49,7 @@ namespace NzbDrone.Integration.Test.ApiTests request.AddParameter("unmonitored", "false"); var items = Calendar.Get>(request); - items = items.Where(v => v.SeriesId == _series.Id).ToList(); + items = items.Where(v => v.SeriesId == series.Id).ToList(); items.Should().BeEmpty(); } @@ -59,7 +57,7 @@ namespace NzbDrone.Integration.Test.ApiTests [Test] public void should_be_able_to_get_unmonitored_episodes() { - _series = EnsureSeries(266189, "The Blacklist", false); + var series = EnsureSeries(266189, "The Blacklist", false); var request = Calendar.BuildRequest(); request.AddParameter("start", new DateTime(2015, 10, 1).ToString("s") + "Z"); @@ -67,7 +65,7 @@ namespace NzbDrone.Integration.Test.ApiTests request.AddParameter("unmonitored", "true"); var items = Calendar.Get>(request); - items = items.Where(v => v.SeriesId == _series.Id).ToList(); + items = items.Where(v => v.SeriesId == series.Id).ToList(); items.Should().HaveCount(1); items.First().Title.Should().Be("The Troll Farmer"); diff --git a/src/NzbDrone.Integration.Test/ApiTests/DiskSpaceFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/DiskSpaceFixture.cs new file mode 100644 index 000000000..33b83d995 --- /dev/null +++ b/src/NzbDrone.Integration.Test/ApiTests/DiskSpaceFixture.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Api.DiskSpace; +using NzbDrone.Integration.Test.Client; + +namespace NzbDrone.Integration.Test.ApiTests +{ + [TestFixture] + public class DiskSpaceFixture : IntegrationTest + { + public ClientBase DiskSpace; + + protected override void InitRestClients() + { + base.InitRestClients(); + + DiskSpace = new ClientBase(RestClient, ApiKey, "diskSpace"); + } + + [Test] + [Ignore("Fails on build agent")] + public void get_all_diskspace() + { + var items = DiskSpace.All(); + + items.Should().NotBeEmpty(); + items.First().FreeSpace.Should().NotBe(0); + items.First().TotalSpace.Should().NotBe(0); + items.First().Path.Should().NotBeNullOrEmpty(); + } + } +} diff --git a/src/NzbDrone.Integration.Test/ApiTests/DownloadClientFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/DownloadClientFixture.cs index 57da20349..1b0fb555f 100644 --- a/src/NzbDrone.Integration.Test/ApiTests/DownloadClientFixture.cs +++ b/src/NzbDrone.Integration.Test/ApiTests/DownloadClientFixture.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Net; using FluentAssertions; using NUnit.Framework; using NzbDrone.Test.Common; @@ -8,9 +9,54 @@ namespace NzbDrone.Integration.Test.ApiTests [TestFixture] public class DownloadClientFixture : IntegrationTest { - [Test] - public void should_be_able_to_add() + + [Test, Order(0)] + public void add_downloadclient_without_name_should_return_badrequest() { + EnsureNoDownloadClient(); + + var schema = DownloadClients.Schema().First(v => v.Implementation == "UsenetBlackhole"); + + schema.Enable = true; + schema.Fields.First(v => v.Name == "WatchFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Watch"); + schema.Fields.First(v => v.Name == "NzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb"); + + DownloadClients.InvalidPost(schema); + } + + [Test, Order(0)] + public void add_downloadclient_without_nzbfolder_should_return_badrequest() + { + EnsureNoDownloadClient(); + + var schema = DownloadClients.Schema().First(v => v.Implementation == "UsenetBlackhole"); + + schema.Enable = true; + schema.Name = "Test UsenetBlackhole"; + schema.Fields.First(v => v.Name == "WatchFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Watch"); + + DownloadClients.InvalidPost(schema); + } + + [Test, Order(0)] + public void add_downloadclient_without_watchfolder_should_return_badrequest() + { + EnsureNoDownloadClient(); + + var schema = DownloadClients.Schema().First(v => v.Implementation == "UsenetBlackhole"); + + schema.Enable = true; + schema.Name = "Test UsenetBlackhole"; + schema.Fields.First(v => v.Name == "NzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb"); + + DownloadClients.InvalidPost(schema); + } + + [Test, Order(1)] + public void add_downloadclient() + { + EnsureNoDownloadClient(); + var schema = DownloadClients.Schema().First(v => v.Implementation == "UsenetBlackhole"); schema.Enable = true; @@ -23,22 +69,54 @@ namespace NzbDrone.Integration.Test.ApiTests result.Enable.Should().BeTrue(); } - [Test] - public void should_be_able_to_get() + [Test, Order(2)] + public void get_all_downloadclients() { - Assert.Ignore("TODO"); + EnsureDownloadClient(); + + var clients = DownloadClients.All(); + + clients.Should().NotBeNullOrEmpty(); + } + + [Test, Order(2)] + public void get_downloadclient_by_id() + { + var client = EnsureDownloadClient(); + + var result = DownloadClients.Get(client.Id); + + result.Should().NotBeNull(); } [Test] - public void should_be_able_to_get_by_id() + public void get_downloadclient_by_unknown_id_should_return_404() { - Assert.Ignore("TODO"); + var result = DownloadClients.InvalidGet(1000000); } - [Test] - public void should_be_enabled() + [Test, Order(3)] + public void update_downloadclient() { - Assert.Ignore("TODO"); + EnsureNoDownloadClient(); + var client = EnsureDownloadClient(); + + client.Fields.First(v => v.Name == "NzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb2"); + var result = DownloadClients.Put(client); + + result.Should().NotBeNull(); + } + + [Test, Order(4)] + public void delete_downloadclient() + { + var client = EnsureDownloadClient(); + + DownloadClients.Get(client.Id).Should().NotBeNull(); + + DownloadClients.Delete(client.Id); + + DownloadClients.All().Should().NotContain(v => v.Id == client.Id); } } } \ No newline at end of file diff --git a/src/NzbDrone.Integration.Test/ApiTests/EpisodeFileFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/EpisodeFileFixture.cs new file mode 100644 index 000000000..d0badef35 --- /dev/null +++ b/src/NzbDrone.Integration.Test/ApiTests/EpisodeFileFixture.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Api.Series; +using System.Linq; +using NzbDrone.Test.Common; + +namespace NzbDrone.Integration.Test.ApiTests +{ + [TestFixture] + public class EpisodeFileFixture : IntegrationTest + { + [Test] + public void get_all_episodefiles() + { + Assert.Ignore("TODO"); + } + } +} diff --git a/src/NzbDrone.Integration.Test/ApiTests/FileSystemFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/FileSystemFixture.cs new file mode 100644 index 000000000..1a04ddbe7 --- /dev/null +++ b/src/NzbDrone.Integration.Test/ApiTests/FileSystemFixture.cs @@ -0,0 +1,128 @@ +using System; +using System.Threading; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Api.Series; +using System.Linq; +using NzbDrone.Test.Common; +using NzbDrone.Integration.Test.Client; +using RestSharp; +using System.Net; +using Newtonsoft.Json.Linq; +using NzbDrone.Common.Disk; +using System.Reflection; +using System.IO; +using System.Collections.Generic; + +namespace NzbDrone.Integration.Test.ApiTests +{ + [TestFixture] + public class FileSystemFixture : IntegrationTest + { + public ClientBase FileSystem; + + private string _file; + private string _folder; + + protected override void InitRestClients() + { + base.InitRestClients(); + + FileSystem = new ClientBase(RestClient, ApiKey, "filesystem"); + } + + [SetUp] + public void SetUp() + { + _file = Assembly.GetExecutingAssembly().Location; + _folder = Path.GetDirectoryName(_file) + Path.DirectorySeparatorChar; + } + + [Test] + public void get_filesystem_content_excluding_files() + { + var request = FileSystem.BuildRequest(); + request.Method = Method.GET; + request.AddQueryParameter("path", _folder); + + var result = FileSystem.Execute(request, HttpStatusCode.OK); + + result.Should().NotBeNull(); + result.Directories.Should().NotBeNullOrEmpty(); + result.Files.Should().BeNullOrEmpty(); + } + + [Test] + public void get_filesystem_content_including_files() + { + var request = FileSystem.BuildRequest(); + request.Method = Method.GET; + request.AddQueryParameter("path", _folder); + request.AddQueryParameter("includeFiles", "true"); + + var result = FileSystem.Execute(request, HttpStatusCode.OK); + + result.Should().NotBeNull(); + result.Directories.Should().NotBeNullOrEmpty(); + result.Files.Should().NotBeNullOrEmpty(); + + result.Files.Should().Contain(v => v.Path == _file && v.Type == FileSystemEntityType.File); + } + + [Test] + public void get_entity_type_should_return_file() + { + var request = FileSystem.BuildRequest("type"); + request.Method = Method.GET; + request.AddQueryParameter("path", _file); + + var result = FileSystem.Execute(request, HttpStatusCode.OK); + + result.Type.Should().Be(FileSystemEntityType.File); + } + + [Test] + public void get_entity_type_should_return_folder() + { + var request = FileSystem.BuildRequest("type"); + request.Method = Method.GET; + request.AddQueryParameter("path", _folder); + + var result = FileSystem.Execute(request, HttpStatusCode.OK); + + result.Type.Should().Be(FileSystemEntityType.Folder); + } + + [Test] + public void get_entity_type_should_return_folder_for_unknown() + { + var request = FileSystem.BuildRequest("type"); + request.Method = Method.GET; + request.AddQueryParameter("path", _file + ".unknown"); + + var result = FileSystem.Execute(request, HttpStatusCode.OK); + + result.Type.Should().Be(FileSystemEntityType.Folder); + } + + [Test] + public void get_all_mediafiles() + { + var tempDir = GetTempDirectory("mediaDir"); + File.WriteAllText(Path.Combine(tempDir, "somevideo.mkv"), "video"); + + var request = FileSystem.BuildRequest("mediafiles"); + request.Method = Method.GET; + request.AddQueryParameter("path", tempDir); + + var result = FileSystem.Execute>>(request, HttpStatusCode.OK); + + result.Should().HaveCount(1); + result.First().Should().ContainKey("path"); + result.First().Should().ContainKey("relativePath"); + result.First().Should().ContainKey("name"); + + result.First()["name"].Should().Be("somevideo.mkv"); + } + } +} diff --git a/src/NzbDrone.Integration.Test/ApiTests/HistoryFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/HistoryFixture.cs index 4b33e9c71..2a6faf9d1 100644 --- a/src/NzbDrone.Integration.Test/ApiTests/HistoryFixture.cs +++ b/src/NzbDrone.Integration.Test/ApiTests/HistoryFixture.cs @@ -16,6 +16,5 @@ namespace NzbDrone.Integration.Test.ApiTests history.PageSize.Should().Be(15); history.Records.Should().BeEmpty(); } - } } \ No newline at end of file diff --git a/src/NzbDrone.Integration.Test/ApiTests/NamingConfigFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/NamingConfigFixture.cs index a431a218e..3dcfa5c83 100644 --- a/src/NzbDrone.Integration.Test/ApiTests/NamingConfigFixture.cs +++ b/src/NzbDrone.Integration.Test/ApiTests/NamingConfigFixture.cs @@ -47,7 +47,7 @@ namespace NzbDrone.Integration.Test.ApiTests config.AnimeEpisodeFormat = "{Series Title} - {season}x{episode:00} - {Episode Title}"; var errors = NamingConfig.InvalidPut(config); - errors.Should().NotBeEmpty(); + errors.Should().NotBeNull(); } [Test] @@ -60,7 +60,7 @@ namespace NzbDrone.Integration.Test.ApiTests config.AnimeEpisodeFormat = "{Series Title} - {season}x{episode:00} - {Episode Title}"; var errors = NamingConfig.InvalidPut(config); - errors.Should().NotBeEmpty(); + errors.Should().NotBeNull(); } [Test] @@ -73,7 +73,7 @@ namespace NzbDrone.Integration.Test.ApiTests config.AnimeEpisodeFormat = "{Series Title} - {season}x{episode:00} - {Episode Title}"; var errors = NamingConfig.InvalidPut(config); - errors.Should().NotBeEmpty(); + errors.Should().NotBeNull(); } [Test] @@ -86,7 +86,7 @@ namespace NzbDrone.Integration.Test.ApiTests config.AnimeEpisodeFormat = "{Series Title} - {season} - {Episode Title}"; var errors = NamingConfig.InvalidPut(config); - errors.Should().NotBeEmpty(); + errors.Should().NotBeNull(); } [Test] @@ -98,7 +98,7 @@ namespace NzbDrone.Integration.Test.ApiTests config.DailyEpisodeFormat = ""; var errors = NamingConfig.InvalidPut(config); - errors.Should().NotBeEmpty(); + errors.Should().NotBeNull(); } [Test] @@ -110,7 +110,7 @@ namespace NzbDrone.Integration.Test.ApiTests config.DailyEpisodeFormat = ""; var errors = NamingConfig.InvalidPut(config); - errors.Should().NotBeEmpty(); + errors.Should().NotBeNull(); } [Test] @@ -121,7 +121,7 @@ namespace NzbDrone.Integration.Test.ApiTests config.SeriesFolderFormat = "This and That"; var errors = NamingConfig.InvalidPut(config); - errors.Should().NotBeEmpty(); + errors.Should().NotBeNull(); } } } \ No newline at end of file diff --git a/src/NzbDrone.Integration.Test/ApiTests/RootFolderFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/RootFolderFixture.cs index 6e8de795f..5133a5da7 100644 --- a/src/NzbDrone.Integration.Test/ApiTests/RootFolderFixture.cs +++ b/src/NzbDrone.Integration.Test/ApiTests/RootFolderFixture.cs @@ -50,7 +50,7 @@ namespace NzbDrone.Integration.Test.ApiTests }; var postResponse = RootFolders.InvalidPost(rootFolder); - postResponse.Should().NotBeEmpty(); + postResponse.Should().NotBeNull(); } } } \ No newline at end of file diff --git a/src/NzbDrone.Integration.Test/ApiTests/SeriesFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/SeriesFixture.cs index 111af9318..02b2a8be9 100644 --- a/src/NzbDrone.Integration.Test/ApiTests/SeriesFixture.cs +++ b/src/NzbDrone.Integration.Test/ApiTests/SeriesFixture.cs @@ -12,25 +12,7 @@ namespace NzbDrone.Integration.Test.ApiTests [TestFixture] public class SeriesFixture : IntegrationTest { - [Test] - public void add_series() - { - EnsureNoSeries(266189, "The Blacklist"); - - var series = Series.Lookup("tvdb:266189").Single(); - - series.ProfileId = 1; - series.Path = Path.Combine(SeriesRootFolder, series.Title); - - var result = Series.Post(series); - - result.Should().NotBeNull(); - result.Id.Should().NotBe(0); - result.ProfileId.Should().Be(1); - result.Path.Should().Be(Path.Combine(SeriesRootFolder, series.Title)); - } - - [Test] + [Test, Order(0)] public void add_series_with_tags_should_store_them() { EnsureNoSeries(266189, "The Blacklist"); @@ -49,7 +31,7 @@ namespace NzbDrone.Integration.Test.ApiTests result.Tags.Should().Equal(tag.Id); } - [Test] + [Test, Order(0)] public void add_series_without_profileid_should_return_badrequest() { EnsureNoSeries(266189, "The Blacklist"); @@ -61,7 +43,7 @@ namespace NzbDrone.Integration.Test.ApiTests Series.InvalidPost(series); } - [Test] + [Test, Order(0)] public void add_series_without_path_should_return_badrequest() { EnsureNoSeries(266189, "The Blacklist"); @@ -73,7 +55,26 @@ namespace NzbDrone.Integration.Test.ApiTests Series.InvalidPost(series); } - [Test] + [Test, Order(1)] + public void add_series() + { + EnsureNoSeries(266189, "The Blacklist"); + + var series = Series.Lookup("tvdb:266189").Single(); + + series.ProfileId = 1; + series.Path = Path.Combine(SeriesRootFolder, series.Title); + + var result = Series.Post(series); + + result.Should().NotBeNull(); + result.Id.Should().NotBe(0); + result.ProfileId.Should().Be(1); + result.Path.Should().Be(Path.Combine(SeriesRootFolder, series.Title)); + } + + + [Test, Order(2)] public void get_all_series() { EnsureSeries(266189, "The Blacklist"); @@ -84,7 +85,7 @@ namespace NzbDrone.Integration.Test.ApiTests Series.All().Should().Contain(v => v.TvdbId == 266189); } - [Test] + [Test, Order(2)] public void get_series_by_id() { var series = EnsureSeries(266189, "The Blacklist"); @@ -97,10 +98,10 @@ namespace NzbDrone.Integration.Test.ApiTests [Test] public void get_series_by_unknown_id_should_return_404() { - var result = Series.Get(1000000, HttpStatusCode.NotFound); + var result = Series.InvalidGet(1000000); } - [Test] + [Test, Order(2)] public void update_series_profile_id() { var series = EnsureSeries(266189, "The Blacklist"); @@ -118,7 +119,7 @@ namespace NzbDrone.Integration.Test.ApiTests Series.Get(series.Id).ProfileId.Should().Be(profileId); } - [Test] + [Test, Order(3)] public void update_series_monitored() { var series = EnsureSeries(266189, "The Blacklist", false); @@ -138,7 +139,7 @@ namespace NzbDrone.Integration.Test.ApiTests result.Seasons.First().Monitored.Should().BeTrue(); } - [Test] + [Test, Order(3)] public void update_series_tags() { var series = EnsureSeries(266189, "The Blacklist"); @@ -160,7 +161,7 @@ namespace NzbDrone.Integration.Test.ApiTests } } - [Test] + [Test, Order(4)] public void delete_series() { var series = EnsureSeries(266189, "The Blacklist"); diff --git a/src/NzbDrone.Integration.Test/ApiTests/WantedFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/WantedFixture.cs new file mode 100644 index 000000000..01e5df8e5 --- /dev/null +++ b/src/NzbDrone.Integration.Test/ApiTests/WantedFixture.cs @@ -0,0 +1,111 @@ +using System.Linq; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Qualities; + +namespace NzbDrone.Integration.Test.ApiTests +{ + [TestFixture] + public class WantedFixture : IntegrationTest + { + [Test, Order(0)] + public void missing_should_be_empty() + { + EnsureNoSeries(266189, "The Blacklist"); + + var result = WantedMissing.GetPaged(0, 15, "airDateUtc", "desc"); + + result.Records.Should().BeEmpty(); + } + + [Test, Order(1)] + public void missing_should_have_monitored_items() + { + EnsureSeries(266189, "The Blacklist", true); + + var result = WantedMissing.GetPaged(0, 15, "airDateUtc", "desc"); + + result.Records.Should().NotBeEmpty(); + } + + [Test, Order(1)] + public void missing_should_have_series() + { + EnsureSeries(266189, "The Blacklist", true); + + var result = WantedMissing.GetPaged(0, 15, "airDateUtc", "desc"); + + result.Records.First().Series.Should().NotBeNull(); + result.Records.First().Series.Title.Should().Be("The Blacklist"); + } + + [Test, Order(1)] + public void cutoff_should_have_monitored_items() + { + EnsureProfileCutoff(1, Quality.HDTV720p); + var series = EnsureSeries(266189, "The Blacklist", true); + EnsureEpisodeFile(series, 1, 1, Quality.SDTV); + + var result = WantedCutoffUnmet.GetPaged(0, 15, "airDateUtc", "desc"); + + result.Records.Should().NotBeEmpty(); + } + + [Test, Order(1)] + public void missing_should_not_have_unmonitored_items() + { + EnsureSeries(266189, "The Blacklist", false); + + var result = WantedMissing.GetPaged(0, 15, "airDateUtc", "desc"); + + result.Records.Should().BeEmpty(); + } + + [Test, Order(1)] + public void cutoff_should_not_have_unmonitored_items() + { + EnsureProfileCutoff(1, Quality.HDTV720p); + var series = EnsureSeries(266189, "The Blacklist", false); + EnsureEpisodeFile(series, 1, 1, Quality.SDTV); + + var result = WantedCutoffUnmet.GetPaged(0, 15, "airDateUtc", "desc"); + + result.Records.Should().BeEmpty(); + } + + [Test, Order(1)] + public void cutoff_should_have_series() + { + EnsureProfileCutoff(1, Quality.HDTV720p); + var series = EnsureSeries(266189, "The Blacklist", true); + EnsureEpisodeFile(series, 1, 1, Quality.SDTV); + + var result = WantedCutoffUnmet.GetPaged(0, 15, "airDateUtc", "desc"); + + result.Records.First().Series.Should().NotBeNull(); + result.Records.First().Series.Title.Should().Be("The Blacklist"); + } + + [Test, Order(2)] + public void missing_should_have_unmonitored_items() + { + EnsureSeries(266189, "The Blacklist", false); + + var result = WantedMissing.GetPaged(0, 15, "airDateUtc", "desc", "monitored", "false"); + + result.Records.Should().NotBeEmpty(); + } + + [Test, Order(2)] + public void cutoff_should_have_unmonitored_items() + { + EnsureProfileCutoff(1, Quality.HDTV720p); + var series = EnsureSeries(266189, "The Blacklist", false); + EnsureEpisodeFile(series, 1, 1, Quality.SDTV); + + var result = WantedCutoffUnmet.GetPaged(0, 15, "airDateUtc", "desc", "monitored", "false"); + + result.Records.Should().NotBeEmpty(); + } + } +} diff --git a/src/NzbDrone.Integration.Test/Client/ClientBase.cs b/src/NzbDrone.Integration.Test/Client/ClientBase.cs index a40c7cf2c..884fe992a 100644 --- a/src/NzbDrone.Integration.Test/Client/ClientBase.cs +++ b/src/NzbDrone.Integration.Test/Client/ClientBase.cs @@ -10,20 +10,15 @@ using System.Linq; namespace NzbDrone.Integration.Test.Client { - public class ClientBase where TResource : RestResource, new() + public class ClientBase { - private readonly IRestClient _restClient; - private readonly string _resource; - private readonly string _apiKey; - private readonly Logger _logger; + protected readonly IRestClient _restClient; + protected readonly string _resource; + protected readonly string _apiKey; + protected readonly Logger _logger; - public ClientBase(IRestClient restClient, string apiKey, string resource = null) + public ClientBase(IRestClient restClient, string apiKey, string resource) { - if (resource == null) - { - resource = new TResource().ResourceName; - } - _restClient = restClient; _resource = resource; _apiKey = apiKey; @@ -31,19 +26,77 @@ namespace NzbDrone.Integration.Test.Client _logger = LogManager.GetLogger("REST"); } + public RestRequest BuildRequest(string command = "") + { + var request = new RestRequest(_resource + "/" + command.Trim('/')) + { + RequestFormat = DataFormat.Json, + }; + + request.AddHeader("Authorization", _apiKey); + request.AddHeader("X-Api-Key", _apiKey); + + return request; + } + + public T Execute(IRestRequest request, HttpStatusCode statusCode) where T : class, new() + { + _logger.Info("{0}: {1}", request.Method, _restClient.BuildUri(request)); + + var response = _restClient.Execute(request); + _logger.Info("Response: {0}", response.Content); + + if (response.ErrorException != null) + { + throw response.ErrorException; + } + + AssertDisableCache(response.Headers); + + response.ErrorMessage.Should().BeNullOrWhiteSpace(); + + response.StatusCode.Should().Be(statusCode); + + return Json.Deserialize(response.Content); + } + + private static void AssertDisableCache(IList headers) + { + headers.Single(c => c.Name == "Cache-Control").Value.Should().Be("no-cache, no-store, must-revalidate"); + headers.Single(c => c.Name == "Pragma").Value.Should().Be("no-cache"); + headers.Single(c => c.Name == "Expires").Value.Should().Be("0"); + } + } + + public class ClientBase : ClientBase + where TResource : RestResource, new() + { + public ClientBase(IRestClient restClient, string apiKey, string resource = null) + : base(restClient, apiKey, resource ?? new TResource().ResourceName) + { + + } + public List All() { var request = BuildRequest(); return Get>(request); } - public PagingResource GetPaged(int pageNumber, int pageSize, string sortKey, string sortDir) + public PagingResource GetPaged(int pageNumber, int pageSize, string sortKey, string sortDir, string filterKey = null, string filterValue = null) { var request = BuildRequest(); request.AddParameter("page", pageNumber); request.AddParameter("pageSize", pageSize); request.AddParameter("sortKey", sortKey); request.AddParameter("sortDir", sortDir); + + if (filterKey != null && filterValue != null) + { + request.AddParameter("filterKey", filterKey); + request.AddParameter("filterValue", filterValue); + } + return Get>(request); } @@ -79,31 +132,24 @@ namespace NzbDrone.Integration.Test.Client Delete(request); } - public List InvalidPost(TResource body) + public object InvalidGet(int id, HttpStatusCode statusCode = HttpStatusCode.NotFound) + { + var request = BuildRequest(id.ToString()); + return Get(request, statusCode); + } + + public object InvalidPost(TResource body, HttpStatusCode statusCode = HttpStatusCode.BadRequest) { var request = BuildRequest(); request.AddBody(body); - return Post>(request, HttpStatusCode.BadRequest); + return Post(request, statusCode); } - public List InvalidPut(TResource body) + public object InvalidPut(TResource body, HttpStatusCode statusCode = HttpStatusCode.BadRequest) { var request = BuildRequest(); request.AddBody(body); - return Put>(request, HttpStatusCode.BadRequest); - } - - public RestRequest BuildRequest(string command = "") - { - var request = new RestRequest(_resource + "/" + command.Trim('/')) - { - RequestFormat = DataFormat.Json, - }; - - request.AddHeader("Authorization", _apiKey); - request.AddHeader("X-Api-Key", _apiKey); - - return request; + return Put(request, statusCode); } public T Get(IRestRequest request, HttpStatusCode statusCode = HttpStatusCode.OK) where T : class, new() @@ -129,33 +175,5 @@ namespace NzbDrone.Integration.Test.Client request.Method = Method.DELETE; Execute(request, statusCode); } - - private T Execute(IRestRequest request, HttpStatusCode statusCode) where T : class, new() - { - _logger.Info("{0}: {1}", request.Method, _restClient.BuildUri(request)); - - var response = _restClient.Execute(request); - _logger.Info("Response: {0}", response.Content); - - if (response.ErrorException != null) - { - throw response.ErrorException; - } - - AssertDisableCache(response.Headers); - - response.ErrorMessage.Should().BeNullOrWhiteSpace(); - - response.StatusCode.Should().Be(statusCode); - - return Json.Deserialize(response.Content); - } - - private static void AssertDisableCache(IList headers) - { - headers.Single(c => c.Name == "Cache-Control").Value.Should().Be("no-cache, no-store, must-revalidate"); - headers.Single(c => c.Name == "Pragma").Value.Should().Be("no-cache"); - headers.Single(c => c.Name == "Expires").Value.Should().Be("0"); - } } } \ No newline at end of file diff --git a/src/NzbDrone.Integration.Test/Client/CommandClient.cs b/src/NzbDrone.Integration.Test/Client/CommandClient.cs index b83033fad..047427a98 100644 --- a/src/NzbDrone.Integration.Test/Client/CommandClient.cs +++ b/src/NzbDrone.Integration.Test/Client/CommandClient.cs @@ -1,5 +1,10 @@ using NzbDrone.Api.Commands; using RestSharp; +using NzbDrone.Core.Messaging.Commands; +using FluentAssertions; +using System.Threading; +using NUnit.Framework; +using System.Linq; namespace NzbDrone.Integration.Test.Client { @@ -9,5 +14,42 @@ namespace NzbDrone.Integration.Test.Client : base(restClient, apiKey) { } + + public CommandResource PostAndWait(CommandResource command) + { + var result = Post(command); + result.Id.Should().NotBe(0); + + for (var i = 0; i < 50; i++) + { + if (result.Status == CommandStatus.Completed) + { + return result; + } + + Thread.Sleep(500); + result = Get(result.Id); + } + + Assert.Fail("Command failed"); + return result; + } + + public void WaitAll() + { + var resources = All(); + for (var i = 0; i < 50; i++) + { + if (!resources.Any(v => v.Status == CommandStatus.Queued || v.Status == CommandStatus.Started)) + { + return; + } + + Thread.Sleep(500); + resources = All(); + } + + Assert.Fail("Commands still processing"); + } } } diff --git a/src/NzbDrone.Integration.Test/IntegrationTestBase.cs b/src/NzbDrone.Integration.Test/IntegrationTestBase.cs index 424cc340b..ad34134ab 100644 --- a/src/NzbDrone.Integration.Test/IntegrationTestBase.cs +++ b/src/NzbDrone.Integration.Test/IntegrationTestBase.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; +using FluentAssertions; using Microsoft.AspNet.SignalR.Client; using Microsoft.AspNet.SignalR.Client.Transports; using NLog; @@ -12,18 +13,24 @@ using NUnit.Framework; using NzbDrone.Api.Blacklist; using NzbDrone.Api.Commands; using NzbDrone.Api.Config; +using NzbDrone.Api.DownloadClient; +using NzbDrone.Api.EpisodeFiles; using NzbDrone.Api.Episodes; using NzbDrone.Api.History; +using NzbDrone.Api.Profiles; using NzbDrone.Api.RootFolders; using NzbDrone.Api.Series; using NzbDrone.Api.Tags; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Serializer; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Tv.Commands; using NzbDrone.Integration.Test.Client; using NzbDrone.SignalR; using NzbDrone.Test.Common; using NzbDrone.Test.Common.Categories; using RestSharp; +using System.Collections; namespace NzbDrone.Integration.Test { @@ -33,17 +40,20 @@ namespace NzbDrone.Integration.Test protected RestClient RestClient { get; private set; } public ClientBase Blacklist; - public ClientBase Commands; + public CommandClient Commands; public DownloadClientClient DownloadClients; public EpisodeClient Episodes; public ClientBase History; public IndexerClient Indexers; public ClientBase NamingConfig; public NotificationClient Notifications; + public ClientBase Profiles; public ReleaseClient Releases; public ClientBase RootFolders; public SeriesClient Series; public ClientBase Tags; + public ClientBase WantedMissing; + public ClientBase WantedCutoffUnmet; private List _signalRReceived; private Connection _signalrConnection; @@ -102,10 +112,13 @@ namespace NzbDrone.Integration.Test Indexers = new IndexerClient(RestClient, ApiKey); NamingConfig = new ClientBase(RestClient, ApiKey, "config/naming"); Notifications = new NotificationClient(RestClient, ApiKey); + Profiles = new ClientBase(RestClient, ApiKey); Releases = new ReleaseClient(RestClient, ApiKey); RootFolders = new ClientBase(RestClient, ApiKey); Series = new SeriesClient(RestClient, ApiKey); Tags = new ClientBase(RestClient, ApiKey); + WantedMissing = new ClientBase(RestClient, ApiKey, "wanted/missing"); + WantedCutoffUnmet = new ClientBase(RestClient, ApiKey, "wanted/cutoff"); } [OneTimeTearDown] @@ -211,7 +224,7 @@ namespace NzbDrone.Integration.Test Directory.CreateDirectory(series.Path); result = Series.Post(series); - + Commands.WaitAll(); WaitForCompletion(() => Episodes.GetEpisodesInSeries(result.Id).Count > 0); } @@ -252,6 +265,41 @@ namespace NzbDrone.Integration.Test } } + public EpisodeFileResource EnsureEpisodeFile(SeriesResource series, int season, int episode, Quality quality) + { + var result = Episodes.GetEpisodesInSeries(series.Id).Single(v => v.SeasonNumber == season && v.EpisodeNumber == episode); + + if (result.EpisodeFile == null) + { + var path = Path.Combine(SeriesRootFolder, series.Title, string.Format("Series.S{0}E{1}.{2}.mkv", season, episode, quality.Name)); + + Directory.CreateDirectory(Path.GetDirectoryName(path)); + File.WriteAllText(path, "Fake Episode"); + + Commands.PostAndWait(new CommandResource { Name = "refreshseries", Body = new RefreshSeriesCommand(series.Id) }); + Commands.WaitAll(); + + result = Episodes.GetEpisodesInSeries(series.Id).Single(v => v.SeasonNumber == season && v.EpisodeNumber == episode); + + result.EpisodeFile.Should().NotBeNull(); + } + + return result.EpisodeFile; + } + + public ProfileResource EnsureProfileCutoff(int profileId, Quality cutoff) + { + var profile = Profiles.Get(profileId); + + if (profile.Cutoff != cutoff) + { + profile.Cutoff = cutoff; + profile = Profiles.Put(profile); + } + + return profile; + } + public TagResource EnsureTag(string tagLabel) { var tag = Tags.All().FirstOrDefault(v => v.Label == tagLabel); @@ -263,5 +311,50 @@ namespace NzbDrone.Integration.Test return tag; } + + public void EnsureNoTag(string tagLabel) + { + var tag = Tags.All().FirstOrDefault(v => v.Label == tagLabel); + + if (tag != null) + { + Tags.Delete(tag.Id); + } + } + + public DownloadClientResource EnsureDownloadClient(bool enabled = true) + { + var client = DownloadClients.All().FirstOrDefault(v => v.Name == "Test UsenetBlackhole"); + + if (client == null) + { + var schema = DownloadClients.Schema().First(v => v.Implementation == "UsenetBlackhole"); + + schema.Enable = enabled; + schema.Name = "Test UsenetBlackhole"; + schema.Fields.First(v => v.Name == "WatchFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Watch"); + schema.Fields.First(v => v.Name == "NzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb"); + + client = DownloadClients.Post(schema); + } + else if (client.Enable != enabled) + { + client.Enable = enabled; + + client = DownloadClients.Put(client); + } + + return client; + } + + public void EnsureNoDownloadClient() + { + var clients = DownloadClients.All(); + + foreach (var client in clients) + { + DownloadClients.Delete(client.Id); + } + } } } diff --git a/src/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj b/src/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj index 7cb48e817..c03fc46f5 100644 --- a/src/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj +++ b/src/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj @@ -103,10 +103,14 @@ + + + + diff --git a/src/NzbDrone.sln b/src/NzbDrone.sln index e6afdcb7a..0bed10417 100644 --- a/src/NzbDrone.sln +++ b/src/NzbDrone.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{57A04B72-8088-4F75-A582-1158CF8291F7}" EndProject