moved seriesmodule to restmodule

pull/4/head
kay.one 12 years ago
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 @@ namespace NzbDrone.Api.ErrorManagement
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" />

@ -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();
}
}
}

@ -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 @@ namespace NzbDrone.Api.REST
protected RestModule(string modulePath)
: base(modulePath)
{
PostValidator = new ResourceValidator<TResource>();
PutValidator = new ResourceValidator<TResource>();
SharedValidator = new ResourceValidator<TResource>();
Get[ROOT_ROUTE] = options =>
{
EnsureImplementation(GetResourceAll);
@ -78,13 +88,21 @@ namespace NzbDrone.Api.REST
{
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.Globalization;
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 @@ namespace NzbDrone.Api.Series
: 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 @@ namespace NzbDrone.Api.Series
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 @@ namespace NzbDrone.Api.Series
//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(request.Id);
var series = _seriesService.GetSeries(seriesResource.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 };
}
}

@ -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 @@ namespace NzbDrone.Integration.Test.Client
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 @@ namespace NzbDrone.Integration.Test.Client
_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 @@ namespace NzbDrone.Integration.Test.Client
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 @@ namespace NzbDrone.Integration.Test
}
[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…
Cancel
Save