mirror of
https://github.com/Sonarr/Sonarr
synced 2026-05-07 12:30:56 +02:00
Add v5 Auto Tagging endpoints
This commit is contained in:
parent
510cbe54e8
commit
3f019e67f2
3 changed files with 217 additions and 0 deletions
114
src/Sonarr.Api.V5/AutoTagging/AutoTaggingController.cs
Normal file
114
src/Sonarr.Api.V5/AutoTagging/AutoTaggingController.cs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.AutoTagging;
|
||||
using NzbDrone.Core.AutoTagging.Specifications;
|
||||
using NzbDrone.Core.Validation;
|
||||
using Sonarr.Http;
|
||||
using Sonarr.Http.REST;
|
||||
using Sonarr.Http.REST.Attributes;
|
||||
|
||||
namespace Sonarr.Api.V5.AutoTagging;
|
||||
|
||||
[V5ApiController]
|
||||
public class AutoTaggingController : RestController<AutoTaggingResource>
|
||||
{
|
||||
private readonly IAutoTaggingService _autoTaggingService;
|
||||
private readonly List<IAutoTaggingSpecification> _specifications;
|
||||
|
||||
public AutoTaggingController(IAutoTaggingService autoTaggingService,
|
||||
List<IAutoTaggingSpecification> specifications)
|
||||
{
|
||||
_autoTaggingService = autoTaggingService;
|
||||
_specifications = specifications;
|
||||
|
||||
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
||||
SharedValidator.RuleFor(c => c.Name)
|
||||
.Must((v, c) => !_autoTaggingService.All().Any(f => f.Name == c && f.Id != v.Id)).WithMessage("Must be unique.");
|
||||
SharedValidator.RuleFor(c => c.Tags).NotEmpty();
|
||||
SharedValidator.RuleFor(c => c).Custom((autoTag, context) =>
|
||||
{
|
||||
if (!autoTag.Specifications.Any())
|
||||
{
|
||||
context.AddFailure("Must contain at least one Condition");
|
||||
}
|
||||
|
||||
if (autoTag.Specifications.Any(s => s.Name.IsNullOrWhiteSpace()))
|
||||
{
|
||||
context.AddFailure("Condition name(s) cannot be empty or consist of only spaces");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override AutoTaggingResource GetResourceById(int id)
|
||||
{
|
||||
return _autoTaggingService.GetById(id).ToResource();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Produces("application/json")]
|
||||
public List<AutoTaggingResource> GetAll()
|
||||
{
|
||||
return _autoTaggingService.All().ToResource();
|
||||
}
|
||||
|
||||
[RestPostById]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<AutoTaggingResource> Create([FromBody] AutoTaggingResource autoTagResource)
|
||||
{
|
||||
var model = autoTagResource.ToModel(_specifications);
|
||||
|
||||
Validate(model);
|
||||
|
||||
return Created(_autoTaggingService.Insert(model).Id);
|
||||
}
|
||||
|
||||
[RestPutById]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<AutoTaggingResource> Update([FromBody] AutoTaggingResource resource)
|
||||
{
|
||||
var model = resource.ToModel(_specifications);
|
||||
|
||||
Validate(model);
|
||||
|
||||
_autoTaggingService.Update(model);
|
||||
|
||||
return Accepted(model.Id);
|
||||
}
|
||||
|
||||
[RestDeleteById]
|
||||
public NoContent DeleteAutoTagging(int id)
|
||||
{
|
||||
_autoTaggingService.Delete(id);
|
||||
|
||||
return TypedResults.NoContent();
|
||||
}
|
||||
|
||||
[HttpGet("schema")]
|
||||
[Produces("application/json")]
|
||||
public List<AutoTaggingSpecificationSchema> GetTemplates()
|
||||
{
|
||||
return _specifications.OrderBy(x => x.Order).Select(x => x.ToSchema()).ToList();
|
||||
}
|
||||
|
||||
private void Validate(AutoTag definition)
|
||||
{
|
||||
foreach (var validationResult in definition.Specifications.Select(spec => spec.Validate()))
|
||||
{
|
||||
VerifyValidationResult(validationResult);
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyValidationResult(ValidationResult validationResult)
|
||||
{
|
||||
var result = new NzbDroneValidationResult(validationResult.Errors);
|
||||
|
||||
if (!result.IsValid)
|
||||
{
|
||||
throw new ValidationException(result.Errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
72
src/Sonarr.Api.V5/AutoTagging/AutoTaggingResource.cs
Normal file
72
src/Sonarr.Api.V5/AutoTagging/AutoTaggingResource.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using NzbDrone.Core.AutoTagging;
|
||||
using NzbDrone.Core.AutoTagging.Specifications;
|
||||
using Sonarr.Http.ClientSchema;
|
||||
using Sonarr.Http.REST;
|
||||
|
||||
namespace Sonarr.Api.V5.AutoTagging;
|
||||
|
||||
public class AutoTaggingResource : RestResource
|
||||
{
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public bool RemoveTagsAutomatically { get; set; }
|
||||
public HashSet<int> Tags { get; set; } = [];
|
||||
public List<AutoTaggingSpecificationSchema> Specifications { get; set; } = [];
|
||||
}
|
||||
|
||||
public static class AutoTaggingResourceMapper
|
||||
{
|
||||
public static AutoTaggingResource ToResource(this AutoTag model)
|
||||
{
|
||||
return new AutoTaggingResource
|
||||
{
|
||||
Id = model.Id,
|
||||
Name = model.Name,
|
||||
RemoveTagsAutomatically = model.RemoveTagsAutomatically,
|
||||
Tags = model.Tags,
|
||||
Specifications = model.Specifications.Select(x => x.ToSchema()).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public static List<AutoTaggingResource> ToResource(this IEnumerable<AutoTag> models)
|
||||
{
|
||||
return models.Select(m => m.ToResource()).ToList();
|
||||
}
|
||||
|
||||
public static AutoTag ToModel(this AutoTaggingResource resource, List<IAutoTaggingSpecification> specifications)
|
||||
{
|
||||
return new AutoTag
|
||||
{
|
||||
Id = resource.Id,
|
||||
Name = resource.Name,
|
||||
RemoveTagsAutomatically = resource.RemoveTagsAutomatically,
|
||||
Tags = resource.Tags,
|
||||
Specifications = resource.Specifications.Select(x => MapSpecification(x, specifications)).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private static IAutoTaggingSpecification MapSpecification(AutoTaggingSpecificationSchema resource, List<IAutoTaggingSpecification> specifications)
|
||||
{
|
||||
var matchingSpec =
|
||||
specifications.SingleOrDefault(x => x.GetType().Name == resource.Implementation);
|
||||
|
||||
if (matchingSpec is null)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"{resource.Implementation} is not a valid specification implementation");
|
||||
}
|
||||
|
||||
var type = matchingSpec.GetType();
|
||||
|
||||
// Finding the exact current specification isn't possible given the dynamic nature of them and the possibility that multiple
|
||||
// of the same type exist within the same format. Passing in null is safe as long as there never exists a specification that
|
||||
// relies on additional privacy.
|
||||
var spec = (IAutoTaggingSpecification)SchemaBuilder.ReadFromSchema(resource.Fields, type, null);
|
||||
spec.Name = resource.Name;
|
||||
spec.Negate = resource.Negate;
|
||||
spec.Required = resource.Required;
|
||||
return spec;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using NzbDrone.Core.AutoTagging.Specifications;
|
||||
using Sonarr.Http.ClientSchema;
|
||||
using Sonarr.Http.REST;
|
||||
|
||||
namespace Sonarr.Api.V5.AutoTagging;
|
||||
|
||||
public class AutoTaggingSpecificationSchema : RestResource
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Implementation { get; set; }
|
||||
public string? ImplementationName { get; set; }
|
||||
public bool Negate { get; set; }
|
||||
public bool Required { get; set; }
|
||||
public List<Field> Fields { get; set; } = [];
|
||||
}
|
||||
|
||||
public static class AutoTaggingSpecificationSchemaMapper
|
||||
{
|
||||
public static AutoTaggingSpecificationSchema ToSchema(this IAutoTaggingSpecification model)
|
||||
{
|
||||
return new AutoTaggingSpecificationSchema
|
||||
{
|
||||
Name = model.Name,
|
||||
Implementation = model.GetType().Name,
|
||||
ImplementationName = model.ImplementationName,
|
||||
Negate = model.Negate,
|
||||
Required = model.Required,
|
||||
Fields = SchemaBuilder.ToSchema(model)
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue