mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-11 12:02:35 +02:00
moved seriesmodule to restmodule
This commit is contained in:
parent
4afec69c79
commit
d85b825e06
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using NLog;
|
||||
using Nancy;
|
||||
using NzbDrone.Api.Extensions;
|
||||
@ -25,6 +26,17 @@ public Response HandleException(NancyContext context, Exception exception)
|
||||
return apiException.ToErrorResponse();
|
||||
}
|
||||
|
||||
var validationException = exception as ValidationException;
|
||||
|
||||
if (validationException != null)
|
||||
{
|
||||
_logger.Warn("Invalid request {0}", validationException.Message);
|
||||
|
||||
|
||||
return validationException.Errors.AsResponse(HttpStatusCode.BadRequest);
|
||||
|
||||
}
|
||||
|
||||
_logger.ErrorException("Unexpected error", exception);
|
||||
|
||||
|
||||
|
@ -90,9 +90,11 @@
|
||||
<Compile Include="Frontend\StaticResourceMapper.cs" />
|
||||
<Compile Include="Missing\MissingResource.cs" />
|
||||
<Compile Include="Missing\MissingModule.cs" />
|
||||
<Compile Include="NzbDroneRestModule.cs" />
|
||||
<Compile Include="Resolvers\EndTimeResolver.cs" />
|
||||
<Compile Include="Resolvers\NextAiringResolver.cs" />
|
||||
<Compile Include="Resolvers\NullableDatetimeToString.cs" />
|
||||
<Compile Include="REST\ResourceValidator.cs" />
|
||||
<Compile Include="REST\RestModule.cs" />
|
||||
<Compile Include="REST\RestResource.cs" />
|
||||
<Compile Include="RootFolders\RootFolderModule.cs" />
|
||||
@ -118,6 +120,7 @@
|
||||
<Compile Include="Resolvers\QualityTypesToIntResolver.cs" />
|
||||
<Compile Include="Settings\SettingsModule.cs" />
|
||||
<Compile Include="TinyIoCNancyBootstrapper.cs" />
|
||||
<Compile Include="Validation\IdValidationRule.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
16
NzbDrone.Api/NzbDroneRestModule.cs
Normal file
16
NzbDrone.Api/NzbDroneRestModule.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Api.Validation;
|
||||
|
||||
namespace NzbDrone.Api
|
||||
{
|
||||
public abstract class NzbDroneRestModule<TResource> : RestModule<TResource> where TResource : RestResource, new()
|
||||
{
|
||||
protected NzbDroneRestModule(string resource)
|
||||
: base("/api/" + resource.Trim('/'))
|
||||
{
|
||||
PostValidator.RuleFor(r => r.Id).IsZero();
|
||||
PutValidator.RuleFor(r => r.Id).ValidId();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
9
NzbDrone.Api/REST/ResourceValidator.cs
Normal file
9
NzbDrone.Api/REST/ResourceValidator.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace NzbDrone.Api.REST
|
||||
{
|
||||
public class ResourceValidator<TResource> : AbstractValidator<TResource>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -1,13 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using Nancy;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Api.REST
|
||||
{
|
||||
public abstract class RestModule<TResource> : NancyModule
|
||||
where TResource : RestResource, new()
|
||||
{
|
||||
protected ResourceValidator<TResource> PostValidator { get; private set; }
|
||||
protected ResourceValidator<TResource> PutValidator { get; private set; }
|
||||
protected ResourceValidator<TResource> SharedValidator { get; private set; }
|
||||
private const string ROOT_ROUTE = "/";
|
||||
private const string ID_ROUTE = "/{id}";
|
||||
|
||||
@ -20,6 +25,11 @@ protected RestModule()
|
||||
protected RestModule(string modulePath)
|
||||
: base(modulePath)
|
||||
{
|
||||
|
||||
PostValidator = new ResourceValidator<TResource>();
|
||||
PutValidator = new ResourceValidator<TResource>();
|
||||
SharedValidator = new ResourceValidator<TResource>();
|
||||
|
||||
Get[ROOT_ROUTE] = options =>
|
||||
{
|
||||
EnsureImplementation(GetResourceAll);
|
||||
@ -55,7 +65,7 @@ protected RestModule(string modulePath)
|
||||
return new Response { StatusCode = HttpStatusCode.OK };
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -78,13 +88,21 @@ private TResource ReadFromRequest()
|
||||
{
|
||||
var resource = Request.Body.FromJson<TResource>();
|
||||
|
||||
var errors = SharedValidator.Validate(resource).Errors.ToList();
|
||||
|
||||
|
||||
if (Request.Method.Equals("POST", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
//resource.ValidateForPost();
|
||||
errors.AddRange(PostValidator.Validate(resource).Errors);
|
||||
}
|
||||
else if (Request.Method.Equals("PUT", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
//resource.ValidateForPut();
|
||||
errors.AddRange(PutValidator.Validate(resource).Errors);
|
||||
}
|
||||
|
||||
if (errors.Any())
|
||||
{
|
||||
throw new ValidationException(errors);
|
||||
}
|
||||
|
||||
return resource;
|
||||
|
@ -4,15 +4,15 @@
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using FluentValidation;
|
||||
using Nancy;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Api.Validation;
|
||||
|
||||
namespace NzbDrone.Api.Series
|
||||
{
|
||||
public class SeriesModule : NzbDroneApiModule//: RestModule<SeriesResource>
|
||||
public class SeriesModule : NzbDroneRestModule<SeriesResource>
|
||||
{
|
||||
private readonly ISeriesService _seriesService;
|
||||
|
||||
@ -20,15 +20,23 @@ public SeriesModule(ISeriesService seriesService)
|
||||
: base("/Series")
|
||||
{
|
||||
_seriesService = seriesService;
|
||||
Get["/"] = x => AllSeries();
|
||||
Get["/{id}"] = x => GetSeries((int)x.id);
|
||||
Post["/"] = x => AddSeries();
|
||||
Put["/"] = x => UpdateSeries();
|
||||
|
||||
Delete["/{id}"] = x => DeleteSeries((int)x.id);
|
||||
GetResourceAll = AllSeries;
|
||||
GetResourceById = GetSeries;
|
||||
CreateResource = AddSeries;
|
||||
UpdateResource = UpdateSeries;
|
||||
DeleteResource = DeleteSeries;
|
||||
|
||||
|
||||
SharedValidator.RuleFor(s => s.RootFolderId).ValidId();
|
||||
SharedValidator.RuleFor(s => s.QualityProfileId).ValidId();
|
||||
|
||||
|
||||
PostValidator.RuleFor(s => s.Title).NotEmpty();
|
||||
|
||||
}
|
||||
|
||||
private Response AllSeries()
|
||||
private List<SeriesResource> AllSeries()
|
||||
{
|
||||
var series = _seriesService.GetAllSeries().ToList();
|
||||
var seriesStats = _seriesService.SeriesStatistics();
|
||||
@ -45,18 +53,17 @@ private Response AllSeries()
|
||||
s.NextAiring = stats.NextAiring;
|
||||
}
|
||||
|
||||
return seriesModels.AsResponse();
|
||||
return seriesModels;
|
||||
}
|
||||
|
||||
private Response GetSeries(int id)
|
||||
private SeriesResource GetSeries(int id)
|
||||
{
|
||||
var series = _seriesService.GetSeries(id);
|
||||
var seriesModels = Mapper.Map<Core.Tv.Series, SeriesResource>(series);
|
||||
|
||||
return seriesModels.AsResponse();
|
||||
return seriesModels;
|
||||
}
|
||||
|
||||
private Response AddSeries()
|
||||
private SeriesResource AddSeries(SeriesResource seriesResource)
|
||||
{
|
||||
var newSeries = Request.Body.FromJson<Core.Tv.Series>();
|
||||
|
||||
@ -64,44 +71,40 @@ private Response AddSeries()
|
||||
//Todo: We need to create the folder if the user is adding a new series
|
||||
//(we can just create the folder and it won't blow up if it already exists)
|
||||
//We also need to remove any special characters from the filename before attempting to create it
|
||||
|
||||
_seriesService.AddSeries(newSeries);
|
||||
|
||||
return new Response { StatusCode = HttpStatusCode.Created };
|
||||
var series = Mapper.Map<SeriesResource, Core.Tv.Series>(seriesResource);
|
||||
_seriesService.AddSeries(series);
|
||||
return Mapper.Map<Core.Tv.Series, SeriesResource>(series);
|
||||
}
|
||||
|
||||
private Response UpdateSeries()
|
||||
private SeriesResource UpdateSeries(SeriesResource seriesResource)
|
||||
{
|
||||
var request = Request.Body.FromJson<SeriesResource>();
|
||||
var series = _seriesService.GetSeries(seriesResource.Id);
|
||||
|
||||
var series = _seriesService.GetSeries(request.Id);
|
||||
|
||||
series.Monitored = request.Monitored;
|
||||
series.SeasonFolder = request.SeasonFolder;
|
||||
series.QualityProfileId = request.QualityProfileId;
|
||||
series.Monitored = seriesResource.Monitored;
|
||||
series.SeasonFolder = seriesResource.SeasonFolder;
|
||||
series.QualityProfileId = seriesResource.QualityProfileId;
|
||||
|
||||
//Todo: Do we want to force a scan when this path changes? Can we use events instead?
|
||||
series.RootFolderId = request.RootFolderId;
|
||||
series.FolderName = request.FolderName;
|
||||
series.RootFolderId = seriesResource.RootFolderId;
|
||||
series.FolderName = seriesResource.FolderName;
|
||||
|
||||
series.BacklogSetting = (BacklogSettingType)request.BacklogSetting;
|
||||
series.BacklogSetting = (BacklogSettingType)seriesResource.BacklogSetting;
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(request.CustomStartDate))
|
||||
series.CustomStartDate = DateTime.Parse(request.CustomStartDate, null, DateTimeStyles.RoundtripKind);
|
||||
if (!String.IsNullOrWhiteSpace(seriesResource.CustomStartDate))
|
||||
series.CustomStartDate = DateTime.Parse(seriesResource.CustomStartDate, null, DateTimeStyles.RoundtripKind);
|
||||
|
||||
else
|
||||
series.CustomStartDate = null;
|
||||
|
||||
_seriesService.UpdateSeries(series);
|
||||
|
||||
return request.AsResponse();
|
||||
return Mapper.Map<Core.Tv.Series, SeriesResource>(series);
|
||||
}
|
||||
|
||||
private Response DeleteSeries(int id)
|
||||
private void DeleteSeries(int id)
|
||||
{
|
||||
var deleteFiles = Convert.ToBoolean(Request.Headers["deleteFiles"].FirstOrDefault());
|
||||
_seriesService.DeleteSeries(id, deleteFiles);
|
||||
return new Response { StatusCode = HttpStatusCode.OK };
|
||||
}
|
||||
}
|
||||
|
||||
|
19
NzbDrone.Api/Validation/IdValidationRule.cs
Normal file
19
NzbDrone.Api/Validation/IdValidationRule.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Validators;
|
||||
|
||||
namespace NzbDrone.Api.Validation
|
||||
{
|
||||
public static class RuleBuilderExtensions
|
||||
{
|
||||
public static IRuleBuilderOptions<T, int> ValidId<T>(this IRuleBuilder<T, int> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.SetValidator(new GreaterThanValidator(0));
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, int> IsZero<T>(this IRuleBuilder<T, int> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.SetValidator(new EqualValidator(0));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using FluentAssertions;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
|
||||
namespace NzbDrone.Integration.Test.Client
|
||||
@ -33,6 +36,13 @@ public TResource Post(TResource body)
|
||||
return Post<TResource>(request);
|
||||
}
|
||||
|
||||
public List<string> InvalidPost(TResource body)
|
||||
{
|
||||
var request = BuildRequest();
|
||||
request.AddBody(body);
|
||||
return Post<List<string>>(request, HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
protected RestRequest BuildRequest(string command = "")
|
||||
{
|
||||
return new RestRequest(_resource + "/" + command.Trim('/'))
|
||||
@ -58,9 +68,10 @@ protected RestRequest BuildRequest(string command = "")
|
||||
_logger.Info("{0}: {1}", request.Method, _restClient.BuildUri(request));
|
||||
|
||||
var response = _restClient.Execute<T>(request);
|
||||
|
||||
_logger.Info("Response: {0}", response.Content);
|
||||
|
||||
response.StatusCode.Should().Be(statusCode);
|
||||
|
||||
if (response.ErrorException != null)
|
||||
{
|
||||
throw response.ErrorException;
|
||||
@ -68,9 +79,6 @@ protected RestRequest BuildRequest(string command = "")
|
||||
|
||||
response.ErrorMessage.Should().BeBlank();
|
||||
|
||||
|
||||
response.StatusCode.Should().Be(statusCode);
|
||||
|
||||
return response.Data;
|
||||
}
|
||||
|
||||
|
@ -42,12 +42,20 @@
|
||||
<Reference Include="FluentAssertions">
|
||||
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FluentValidation, Version=3.4.6.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\FluentValidation.3.4.6.0\lib\Net40\FluentValidation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Nancy">
|
||||
<HintPath>..\packages\Nancy.0.16.1\lib\net40\Nancy.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Nancy.Hosting.Self">
|
||||
<HintPath>..\packages\Nancy.Hosting.Self.0.16.1\lib\net40\Nancy.Hosting.Self.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Newtonsoft.Json.5.0.3\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog">
|
||||
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using System.Net;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Api.Series;
|
||||
|
||||
@ -23,10 +24,10 @@ public void series_lookup_on_trakt()
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore]
|
||||
public void add_series_without_required_fields_should_return_400()
|
||||
public void add_series_without_required_fields_should_return_badrequest()
|
||||
{
|
||||
Series.Post(new SeriesResource());
|
||||
var errors = Series.InvalidPost(new SeriesResource());
|
||||
errors.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,8 +3,10 @@
|
||||
<package id="Exceptron.Client" version="1.0.20" targetFramework="net40" />
|
||||
<package id="Exceptron.NLog" version="1.0.11" targetFramework="net40" />
|
||||
<package id="FluentAssertions" version="2.0.1" targetFramework="net40" />
|
||||
<package id="FluentValidation" version="3.4.6.0" targetFramework="net40" />
|
||||
<package id="Nancy" version="0.16.1" targetFramework="net40" />
|
||||
<package id="Nancy.Hosting.Self" version="0.16.1" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="5.0.3" targetFramework="net40" />
|
||||
<package id="NLog" version="2.0.1.2" targetFramework="net40" />
|
||||
<package id="NUnit" version="2.6.2" targetFramework="net40" />
|
||||
<package id="RestSharp" version="104.1" targetFramework="net40" />
|
||||
|
Loading…
Reference in New Issue
Block a user