mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-17 15:02:34 +02:00
moved seriesmodule to restmodule
This commit is contained in:
parent
4afec69c79
commit
d85b825e06
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using FluentValidation;
|
||||||
using NLog;
|
using NLog;
|
||||||
using Nancy;
|
using Nancy;
|
||||||
using NzbDrone.Api.Extensions;
|
using NzbDrone.Api.Extensions;
|
||||||
@ -25,6 +26,17 @@ public Response HandleException(NancyContext context, Exception exception)
|
|||||||
return apiException.ToErrorResponse();
|
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);
|
_logger.ErrorException("Unexpected error", exception);
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,9 +90,11 @@
|
|||||||
<Compile Include="Frontend\StaticResourceMapper.cs" />
|
<Compile Include="Frontend\StaticResourceMapper.cs" />
|
||||||
<Compile Include="Missing\MissingResource.cs" />
|
<Compile Include="Missing\MissingResource.cs" />
|
||||||
<Compile Include="Missing\MissingModule.cs" />
|
<Compile Include="Missing\MissingModule.cs" />
|
||||||
|
<Compile Include="NzbDroneRestModule.cs" />
|
||||||
<Compile Include="Resolvers\EndTimeResolver.cs" />
|
<Compile Include="Resolvers\EndTimeResolver.cs" />
|
||||||
<Compile Include="Resolvers\NextAiringResolver.cs" />
|
<Compile Include="Resolvers\NextAiringResolver.cs" />
|
||||||
<Compile Include="Resolvers\NullableDatetimeToString.cs" />
|
<Compile Include="Resolvers\NullableDatetimeToString.cs" />
|
||||||
|
<Compile Include="REST\ResourceValidator.cs" />
|
||||||
<Compile Include="REST\RestModule.cs" />
|
<Compile Include="REST\RestModule.cs" />
|
||||||
<Compile Include="REST\RestResource.cs" />
|
<Compile Include="REST\RestResource.cs" />
|
||||||
<Compile Include="RootFolders\RootFolderModule.cs" />
|
<Compile Include="RootFolders\RootFolderModule.cs" />
|
||||||
@ -118,6 +120,7 @@
|
|||||||
<Compile Include="Resolvers\QualityTypesToIntResolver.cs" />
|
<Compile Include="Resolvers\QualityTypesToIntResolver.cs" />
|
||||||
<Compile Include="Settings\SettingsModule.cs" />
|
<Compile Include="Settings\SettingsModule.cs" />
|
||||||
<Compile Include="TinyIoCNancyBootstrapper.cs" />
|
<Compile Include="TinyIoCNancyBootstrapper.cs" />
|
||||||
|
<Compile Include="Validation\IdValidationRule.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using FluentValidation;
|
||||||
using Nancy;
|
using Nancy;
|
||||||
using NzbDrone.Api.Extensions;
|
using NzbDrone.Api.Extensions;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace NzbDrone.Api.REST
|
namespace NzbDrone.Api.REST
|
||||||
{
|
{
|
||||||
public abstract class RestModule<TResource> : NancyModule
|
public abstract class RestModule<TResource> : NancyModule
|
||||||
where TResource : RestResource, new()
|
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 ROOT_ROUTE = "/";
|
||||||
private const string ID_ROUTE = "/{id}";
|
private const string ID_ROUTE = "/{id}";
|
||||||
|
|
||||||
@ -20,6 +25,11 @@ protected RestModule()
|
|||||||
protected RestModule(string modulePath)
|
protected RestModule(string modulePath)
|
||||||
: base(modulePath)
|
: base(modulePath)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
PostValidator = new ResourceValidator<TResource>();
|
||||||
|
PutValidator = new ResourceValidator<TResource>();
|
||||||
|
SharedValidator = new ResourceValidator<TResource>();
|
||||||
|
|
||||||
Get[ROOT_ROUTE] = options =>
|
Get[ROOT_ROUTE] = options =>
|
||||||
{
|
{
|
||||||
EnsureImplementation(GetResourceAll);
|
EnsureImplementation(GetResourceAll);
|
||||||
@ -55,7 +65,7 @@ protected RestModule(string modulePath)
|
|||||||
return new Response { StatusCode = HttpStatusCode.OK };
|
return new Response { StatusCode = HttpStatusCode.OK };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -78,13 +88,21 @@ private TResource ReadFromRequest()
|
|||||||
{
|
{
|
||||||
var resource = Request.Body.FromJson<TResource>();
|
var resource = Request.Body.FromJson<TResource>();
|
||||||
|
|
||||||
|
var errors = SharedValidator.Validate(resource).Errors.ToList();
|
||||||
|
|
||||||
|
|
||||||
if (Request.Method.Equals("POST", StringComparison.InvariantCultureIgnoreCase))
|
if (Request.Method.Equals("POST", StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
//resource.ValidateForPost();
|
errors.AddRange(PostValidator.Validate(resource).Errors);
|
||||||
}
|
}
|
||||||
else if (Request.Method.Equals("PUT", StringComparison.InvariantCultureIgnoreCase))
|
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;
|
return resource;
|
||||||
|
@ -4,15 +4,15 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Nancy;
|
|
||||||
using NzbDrone.Api.Extensions;
|
using NzbDrone.Api.Extensions;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using NzbDrone.Core.Model;
|
using NzbDrone.Core.Model;
|
||||||
|
using NzbDrone.Api.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Series
|
namespace NzbDrone.Api.Series
|
||||||
{
|
{
|
||||||
public class SeriesModule : NzbDroneApiModule//: RestModule<SeriesResource>
|
public class SeriesModule : NzbDroneRestModule<SeriesResource>
|
||||||
{
|
{
|
||||||
private readonly ISeriesService _seriesService;
|
private readonly ISeriesService _seriesService;
|
||||||
|
|
||||||
@ -20,15 +20,23 @@ public SeriesModule(ISeriesService seriesService)
|
|||||||
: base("/Series")
|
: base("/Series")
|
||||||
{
|
{
|
||||||
_seriesService = seriesService;
|
_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 series = _seriesService.GetAllSeries().ToList();
|
||||||
var seriesStats = _seriesService.SeriesStatistics();
|
var seriesStats = _seriesService.SeriesStatistics();
|
||||||
@ -45,18 +53,17 @@ private Response AllSeries()
|
|||||||
s.NextAiring = stats.NextAiring;
|
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 series = _seriesService.GetSeries(id);
|
||||||
var seriesModels = Mapper.Map<Core.Tv.Series, SeriesResource>(series);
|
var seriesModels = Mapper.Map<Core.Tv.Series, SeriesResource>(series);
|
||||||
|
return seriesModels;
|
||||||
return seriesModels.AsResponse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response AddSeries()
|
private SeriesResource AddSeries(SeriesResource seriesResource)
|
||||||
{
|
{
|
||||||
var newSeries = Request.Body.FromJson<Core.Tv.Series>();
|
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
|
//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 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
|
//We also need to remove any special characters from the filename before attempting to create it
|
||||||
|
var series = Mapper.Map<SeriesResource, Core.Tv.Series>(seriesResource);
|
||||||
_seriesService.AddSeries(newSeries);
|
_seriesService.AddSeries(series);
|
||||||
|
return Mapper.Map<Core.Tv.Series, SeriesResource>(series);
|
||||||
return new Response { StatusCode = HttpStatusCode.Created };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = seriesResource.Monitored;
|
||||||
|
series.SeasonFolder = seriesResource.SeasonFolder;
|
||||||
series.Monitored = request.Monitored;
|
series.QualityProfileId = seriesResource.QualityProfileId;
|
||||||
series.SeasonFolder = request.SeasonFolder;
|
|
||||||
series.QualityProfileId = request.QualityProfileId;
|
|
||||||
|
|
||||||
//Todo: Do we want to force a scan when this path changes? Can we use events instead?
|
//Todo: Do we want to force a scan when this path changes? Can we use events instead?
|
||||||
series.RootFolderId = request.RootFolderId;
|
series.RootFolderId = seriesResource.RootFolderId;
|
||||||
series.FolderName = request.FolderName;
|
series.FolderName = seriesResource.FolderName;
|
||||||
|
|
||||||
series.BacklogSetting = (BacklogSettingType)request.BacklogSetting;
|
series.BacklogSetting = (BacklogSettingType)seriesResource.BacklogSetting;
|
||||||
|
|
||||||
if (!String.IsNullOrWhiteSpace(request.CustomStartDate))
|
if (!String.IsNullOrWhiteSpace(seriesResource.CustomStartDate))
|
||||||
series.CustomStartDate = DateTime.Parse(request.CustomStartDate, null, DateTimeStyles.RoundtripKind);
|
series.CustomStartDate = DateTime.Parse(seriesResource.CustomStartDate, null, DateTimeStyles.RoundtripKind);
|
||||||
|
|
||||||
else
|
else
|
||||||
series.CustomStartDate = null;
|
series.CustomStartDate = null;
|
||||||
|
|
||||||
_seriesService.UpdateSeries(series);
|
_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());
|
var deleteFiles = Convert.ToBoolean(Request.Headers["deleteFiles"].FirstOrDefault());
|
||||||
_seriesService.DeleteSeries(id, deleteFiles);
|
_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.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using FluentValidation;
|
||||||
|
using FluentValidation.Results;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.Client
|
namespace NzbDrone.Integration.Test.Client
|
||||||
@ -33,6 +36,13 @@ public TResource Post(TResource body)
|
|||||||
return Post<TResource>(request);
|
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 = "")
|
protected RestRequest BuildRequest(string command = "")
|
||||||
{
|
{
|
||||||
return new RestRequest(_resource + "/" + command.Trim('/'))
|
return new RestRequest(_resource + "/" + command.Trim('/'))
|
||||||
@ -58,9 +68,10 @@ protected RestRequest BuildRequest(string command = "")
|
|||||||
_logger.Info("{0}: {1}", request.Method, _restClient.BuildUri(request));
|
_logger.Info("{0}: {1}", request.Method, _restClient.BuildUri(request));
|
||||||
|
|
||||||
var response = _restClient.Execute<T>(request);
|
var response = _restClient.Execute<T>(request);
|
||||||
|
|
||||||
_logger.Info("Response: {0}", response.Content);
|
_logger.Info("Response: {0}", response.Content);
|
||||||
|
|
||||||
|
response.StatusCode.Should().Be(statusCode);
|
||||||
|
|
||||||
if (response.ErrorException != null)
|
if (response.ErrorException != null)
|
||||||
{
|
{
|
||||||
throw response.ErrorException;
|
throw response.ErrorException;
|
||||||
@ -68,9 +79,6 @@ protected RestRequest BuildRequest(string command = "")
|
|||||||
|
|
||||||
response.ErrorMessage.Should().BeBlank();
|
response.ErrorMessage.Should().BeBlank();
|
||||||
|
|
||||||
|
|
||||||
response.StatusCode.Should().Be(statusCode);
|
|
||||||
|
|
||||||
return response.Data;
|
return response.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,12 +42,20 @@
|
|||||||
<Reference Include="FluentAssertions">
|
<Reference Include="FluentAssertions">
|
||||||
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath>
|
<HintPath>..\packages\FluentAssertions.2.0.1\lib\net40\FluentAssertions.dll</HintPath>
|
||||||
</Reference>
|
</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">
|
<Reference Include="Nancy">
|
||||||
<HintPath>..\packages\Nancy.0.16.1\lib\net40\Nancy.dll</HintPath>
|
<HintPath>..\packages\Nancy.0.16.1\lib\net40\Nancy.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Nancy.Hosting.Self">
|
<Reference Include="Nancy.Hosting.Self">
|
||||||
<HintPath>..\packages\Nancy.Hosting.Self.0.16.1\lib\net40\Nancy.Hosting.Self.dll</HintPath>
|
<HintPath>..\packages\Nancy.Hosting.Self.0.16.1\lib\net40\Nancy.Hosting.Self.dll</HintPath>
|
||||||
</Reference>
|
</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">
|
<Reference Include="NLog">
|
||||||
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.2.0.1.2\lib\net40\NLog.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using FluentAssertions;
|
using System.Net;
|
||||||
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Api.Series;
|
using NzbDrone.Api.Series;
|
||||||
|
|
||||||
@ -23,10 +24,10 @@ public void series_lookup_on_trakt()
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[Ignore]
|
public void add_series_without_required_fields_should_return_badrequest()
|
||||||
public void add_series_without_required_fields_should_return_400()
|
|
||||||
{
|
{
|
||||||
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.Client" version="1.0.20" targetFramework="net40" />
|
||||||
<package id="Exceptron.NLog" version="1.0.11" targetFramework="net40" />
|
<package id="Exceptron.NLog" version="1.0.11" targetFramework="net40" />
|
||||||
<package id="FluentAssertions" version="2.0.1" 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" version="0.16.1" targetFramework="net40" />
|
||||||
<package id="Nancy.Hosting.Self" 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="NLog" version="2.0.1.2" targetFramework="net40" />
|
||||||
<package id="NUnit" version="2.6.2" targetFramework="net40" />
|
<package id="NUnit" version="2.6.2" targetFramework="net40" />
|
||||||
<package id="RestSharp" version="104.1" targetFramework="net40" />
|
<package id="RestSharp" version="104.1" targetFramework="net40" />
|
||||||
|
Loading…
Reference in New Issue
Block a user