2017-10-31 01:28:29 +00:00
using System.Collections.Generic ;
2017-09-04 02:20:56 +00:00
using System.Linq ;
using FluentValidation ;
using FluentValidation.Results ;
2021-08-04 20:42:40 +00:00
using Lidarr.Http.REST ;
using Lidarr.Http.REST.Attributes ;
using Microsoft.AspNetCore.Mvc ;
2024-06-06 09:09:39 +00:00
using NzbDrone.Common.Extensions ;
2018-08-08 00:57:15 +00:00
using NzbDrone.Common.Serializer ;
2017-09-04 02:20:56 +00:00
using NzbDrone.Core.ThingiProvider ;
using NzbDrone.Core.Validation ;
2017-10-31 01:28:29 +00:00
namespace Lidarr.Api.V1
2017-09-04 02:20:56 +00:00
{
2023-07-10 21:42:51 +00:00
public abstract class ProviderControllerBase < TProviderResource , TBulkProviderResource , TProvider , TProviderDefinition > : RestController < TProviderResource >
2017-09-04 02:20:56 +00:00
where TProviderDefinition : ProviderDefinition , new ( )
where TProvider : IProvider
2021-04-26 20:56:39 +00:00
where TProviderResource : ProviderResource < TProviderResource > , new ( )
2023-07-10 21:42:51 +00:00
where TBulkProviderResource : ProviderBulkResource < TBulkProviderResource > , new ( )
2017-09-04 02:20:56 +00:00
{
private readonly IProviderFactory < TProvider , TProviderDefinition > _providerFactory ;
private readonly ProviderResourceMapper < TProviderResource , TProviderDefinition > _resourceMapper ;
2023-07-10 21:42:51 +00:00
private readonly ProviderBulkResourceMapper < TBulkProviderResource , TProviderDefinition > _bulkResourceMapper ;
2017-09-04 02:20:56 +00:00
2023-07-10 21:42:51 +00:00
protected ProviderControllerBase ( IProviderFactory < TProvider ,
TProviderDefinition > providerFactory ,
string resource ,
ProviderResourceMapper < TProviderResource , TProviderDefinition > resourceMapper ,
ProviderBulkResourceMapper < TBulkProviderResource , TProviderDefinition > bulkResourceMapper )
2017-09-04 02:20:56 +00:00
{
_providerFactory = providerFactory ;
_resourceMapper = resourceMapper ;
2023-07-10 21:42:51 +00:00
_bulkResourceMapper = bulkResourceMapper ;
2017-09-04 02:20:56 +00:00
SharedValidator . RuleFor ( c = > c . Name ) . NotEmpty ( ) ;
2024-06-06 09:09:39 +00:00
SharedValidator . RuleFor ( c = > c . Name ) . Must ( ( v , c ) = > ! _providerFactory . All ( ) . Any ( p = > p . Name . EqualsIgnoreCase ( c ) & & p . Id ! = v . Id ) ) . WithMessage ( "Should be unique" ) ;
2017-09-04 02:20:56 +00:00
SharedValidator . RuleFor ( c = > c . Implementation ) . NotEmpty ( ) ;
SharedValidator . RuleFor ( c = > c . ConfigContract ) . NotEmpty ( ) ;
PostValidator . RuleFor ( c = > c . Fields ) . NotNull ( ) ;
}
2021-08-04 20:42:40 +00:00
public override TProviderResource GetResourceById ( int id )
2017-09-04 02:20:56 +00:00
{
var definition = _providerFactory . Get ( id ) ;
_providerFactory . SetProviderCharacteristics ( definition ) ;
return _resourceMapper . ToResource ( definition ) ;
}
2021-08-04 20:42:40 +00:00
[HttpGet]
2023-05-30 02:52:20 +00:00
[Produces("application/json")]
2021-08-04 20:42:40 +00:00
public List < TProviderResource > GetAll ( )
2017-09-04 02:20:56 +00:00
{
2023-03-27 03:40:51 +00:00
var providerDefinitions = _providerFactory . All ( ) ;
2017-09-04 02:20:56 +00:00
2023-03-27 03:40:51 +00:00
var result = new List < TProviderResource > ( providerDefinitions . Count ) ;
2017-09-04 02:20:56 +00:00
2023-03-27 03:40:51 +00:00
foreach ( var definition in providerDefinitions . OrderBy ( p = > p . ImplementationName ) )
2017-09-04 02:20:56 +00:00
{
_providerFactory . SetProviderCharacteristics ( definition ) ;
result . Add ( _resourceMapper . ToResource ( definition ) ) ;
}
return result . OrderBy ( p = > p . Name ) . ToList ( ) ;
}
2021-08-04 20:42:40 +00:00
[RestPostById]
2023-05-30 02:52:20 +00:00
[Consumes("application/json")]
2023-06-21 02:57:27 +00:00
[Produces("application/json")]
2023-05-26 15:35:31 +00:00
public ActionResult < TProviderResource > CreateProvider ( [ FromBody ] TProviderResource providerResource , [ FromQuery ] bool forceSave = false )
2017-09-04 02:20:56 +00:00
{
2023-11-13 05:31:00 +00:00
var providerDefinition = GetDefinition ( providerResource , null , true , ! forceSave , false ) ;
2017-09-04 02:20:56 +00:00
if ( providerDefinition . Enable )
{
2023-11-12 16:01:39 +00:00
Test ( providerDefinition , ! forceSave ) ;
2017-09-04 02:20:56 +00:00
}
providerDefinition = _providerFactory . Create ( providerDefinition ) ;
2021-08-04 20:42:40 +00:00
return Created ( providerDefinition . Id ) ;
2017-09-04 02:20:56 +00:00
}
2021-08-04 20:42:40 +00:00
[RestPutById]
2023-05-30 02:52:20 +00:00
[Consumes("application/json")]
2023-06-21 02:57:27 +00:00
[Produces("application/json")]
2023-05-18 10:17:36 +00:00
public ActionResult < TProviderResource > UpdateProvider ( [ FromBody ] TProviderResource providerResource , [ FromQuery ] bool forceSave = false )
2017-09-04 02:20:56 +00:00
{
2023-11-13 05:31:00 +00:00
var existingDefinition = _providerFactory . Find ( providerResource . Id ) ;
var providerDefinition = GetDefinition ( providerResource , existingDefinition , true , ! forceSave , false ) ;
2017-09-04 02:20:56 +00:00
2023-11-13 05:31:00 +00:00
// Comparing via JSON string to eliminate the need for every provider implementation to implement equality checks.
2023-11-19 19:13:11 +00:00
// Compare settings separately because they are not serialized with the definition.
var hasDefinitionChanged = STJson . ToJson ( existingDefinition ) ! = STJson . ToJson ( providerDefinition ) | |
STJson . ToJson ( existingDefinition . Settings ) ! = STJson . ToJson ( providerDefinition . Settings ) ;
2023-11-13 05:31:00 +00:00
2023-11-20 05:02:28 +00:00
// Only test existing definitions if it is enabled and forceSave isn't set and the definition has changed.
if ( providerDefinition . Enable & & ! forceSave & & hasDefinitionChanged )
2017-09-04 02:20:56 +00:00
{
2023-11-12 16:01:39 +00:00
Test ( providerDefinition , true ) ;
2017-09-04 02:20:56 +00:00
}
2023-11-13 05:31:00 +00:00
if ( hasDefinitionChanged )
{
_providerFactory . Update ( providerDefinition ) ;
}
2021-08-04 20:42:40 +00:00
return Accepted ( providerResource . Id ) ;
2017-09-04 02:20:56 +00:00
}
2023-07-10 21:42:51 +00:00
[HttpPut("bulk")]
[Consumes("application/json")]
[Produces("application/json")]
public virtual ActionResult < TProviderResource > UpdateProvider ( [ FromBody ] TBulkProviderResource providerResource )
{
if ( ! providerResource . Ids . Any ( ) )
{
throw new BadRequestException ( "ids must be provided" ) ;
}
var definitionsToUpdate = _providerFactory . Get ( providerResource . Ids ) . ToList ( ) ;
foreach ( var definition in definitionsToUpdate )
{
_providerFactory . SetProviderCharacteristics ( definition ) ;
if ( providerResource . Tags ! = null )
{
var newTags = providerResource . Tags ;
var applyTags = providerResource . ApplyTags ;
switch ( applyTags )
{
case ApplyTags . Add :
newTags . ForEach ( t = > definition . Tags . Add ( t ) ) ;
break ;
case ApplyTags . Remove :
newTags . ForEach ( t = > definition . Tags . Remove ( t ) ) ;
break ;
case ApplyTags . Replace :
definition . Tags = new HashSet < int > ( newTags ) ;
break ;
}
}
}
_bulkResourceMapper . UpdateModel ( providerResource , definitionsToUpdate ) ;
return Accepted ( _providerFactory . Update ( definitionsToUpdate ) . Select ( x = > _resourceMapper . ToResource ( x ) ) ) ;
}
2023-11-13 05:31:00 +00:00
private TProviderDefinition GetDefinition ( TProviderResource providerResource , TProviderDefinition existingDefinition , bool validate , bool includeWarnings , bool forceValidate )
2017-09-04 02:20:56 +00:00
{
2022-05-24 03:41:50 +00:00
var definition = _resourceMapper . ToModel ( providerResource , existingDefinition ) ;
2017-09-04 02:20:56 +00:00
2022-04-14 02:08:14 +00:00
if ( validate & & ( definition . Enable | | forceValidate ) )
2017-09-04 02:20:56 +00:00
{
Validate ( definition , includeWarnings ) ;
}
return definition ;
}
2021-08-04 20:42:40 +00:00
[RestDeleteById]
2021-08-04 20:42:41 +00:00
public object DeleteProvider ( int id )
2017-09-04 02:20:56 +00:00
{
_providerFactory . Delete ( id ) ;
2021-12-24 18:31:18 +00:00
return new { } ;
2017-09-04 02:20:56 +00:00
}
2023-07-10 21:42:51 +00:00
[HttpDelete("bulk")]
[Consumes("application/json")]
public virtual object DeleteProviders ( [ FromBody ] TBulkProviderResource resource )
{
_providerFactory . Delete ( resource . Ids ) ;
return new { } ;
}
2021-08-04 20:42:40 +00:00
[HttpGet("schema")]
2023-05-30 02:52:20 +00:00
[Produces("application/json")]
2021-08-04 20:42:40 +00:00
public List < TProviderResource > GetTemplates ( )
2017-09-04 02:20:56 +00:00
{
var defaultDefinitions = _providerFactory . GetDefaultDefinitions ( ) . OrderBy ( p = > p . ImplementationName ) . ToList ( ) ;
2022-04-14 02:08:14 +00:00
var result = new List < TProviderResource > ( defaultDefinitions . Count ) ;
2017-09-04 02:20:56 +00:00
foreach ( var providerDefinition in defaultDefinitions )
{
var providerResource = _resourceMapper . ToResource ( providerDefinition ) ;
var presetDefinitions = _providerFactory . GetPresetDefinitions ( providerDefinition ) ;
2021-04-26 20:56:39 +00:00
providerResource . Presets = presetDefinitions
. Select ( v = > _resourceMapper . ToResource ( v ) )
. ToList ( ) ;
2017-09-04 02:20:56 +00:00
result . Add ( providerResource ) ;
}
2019-09-12 20:32:51 +00:00
return result ;
2017-09-04 02:20:56 +00:00
}
2021-08-04 20:42:40 +00:00
[SkipValidation(true, false)]
[HttpPost("test")]
2023-05-30 02:52:20 +00:00
[Consumes("application/json")]
2024-04-15 02:43:52 +00:00
public object Test ( [ FromBody ] TProviderResource providerResource , [ FromQuery ] bool forceTest = false )
2017-09-04 02:20:56 +00:00
{
2023-11-13 05:31:00 +00:00
var existingDefinition = providerResource . Id > 0 ? _providerFactory . Find ( providerResource . Id ) : null ;
2024-04-15 02:43:52 +00:00
var providerDefinition = GetDefinition ( providerResource , existingDefinition , true , ! forceTest , true ) ;
2017-09-04 02:20:56 +00:00
Test ( providerDefinition , true ) ;
return "{}" ;
}
2021-08-04 20:42:40 +00:00
[HttpPost("testall")]
2023-06-21 02:57:27 +00:00
[Produces("application/json")]
2021-08-04 20:42:40 +00:00
public IActionResult TestAll ( )
2018-10-17 02:05:40 +00:00
{
var providerDefinitions = _providerFactory . All ( )
. Where ( c = > c . Settings . Validate ( ) . IsValid & & c . Enable )
. ToList ( ) ;
var result = new List < ProviderTestAllResult > ( ) ;
foreach ( var definition in providerDefinitions )
{
var validationResult = _providerFactory . Test ( definition ) ;
result . Add ( new ProviderTestAllResult
{
Id = definition . Id ,
ValidationFailures = validationResult . Errors . ToList ( )
} ) ;
}
2021-08-04 20:42:40 +00:00
return result . Any ( c = > ! c . IsValid ) ? BadRequest ( result ) : Ok ( result ) ;
2018-10-17 02:05:40 +00:00
}
2021-08-04 20:42:40 +00:00
[SkipValidation]
[HttpPost("action/{name}")]
2023-05-30 02:52:20 +00:00
[Consumes("application/json")]
2023-06-21 02:57:27 +00:00
[Produces("application/json")]
2023-11-13 05:31:00 +00:00
public IActionResult RequestAction ( string name , [ FromBody ] TProviderResource providerResource )
2017-09-04 02:20:56 +00:00
{
2023-11-13 05:31:00 +00:00
var existingDefinition = providerResource . Id > 0 ? _providerFactory . Find ( providerResource . Id ) : null ;
var providerDefinition = GetDefinition ( providerResource , existingDefinition , false , false , false ) ;
2021-08-04 20:42:40 +00:00
var query = Request . Query . ToDictionary ( x = > x . Key , x = > x . Value . ToString ( ) ) ;
2017-09-04 02:20:56 +00:00
2021-08-04 20:42:40 +00:00
var data = _providerFactory . RequestAction ( providerDefinition , name , query ) ;
2017-09-04 02:20:56 +00:00
2021-08-04 20:42:40 +00:00
return Content ( data . ToJson ( ) , "application/json" ) ;
2017-09-04 02:20:56 +00:00
}
protected virtual void Validate ( TProviderDefinition definition , bool includeWarnings )
{
var validationResult = definition . Settings . Validate ( ) ;
VerifyValidationResult ( validationResult , includeWarnings ) ;
}
protected virtual void Test ( TProviderDefinition definition , bool includeWarnings )
{
var validationResult = _providerFactory . Test ( definition ) ;
VerifyValidationResult ( validationResult , includeWarnings ) ;
}
protected void VerifyValidationResult ( ValidationResult validationResult , bool includeWarnings )
{
2023-05-26 15:35:31 +00:00
var result = validationResult as NzbDroneValidationResult ? ? new NzbDroneValidationResult ( validationResult . Errors ) ;
2017-09-04 02:20:56 +00:00
if ( includeWarnings & & ( ! result . IsValid | | result . HasWarnings ) )
{
throw new ValidationException ( result . Failures ) ;
}
if ( ! result . IsValid )
{
throw new ValidationException ( result . Errors ) ;
}
}
}
}