mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-19 21:46:43 -04:00
Add v5 Connection endpoints
This commit is contained in:
@@ -36,7 +36,7 @@ namespace NzbDrone.Core.Notifications.Plex.Server
|
|||||||
[FieldDefinition(1, Label = "Host")]
|
[FieldDefinition(1, Label = "Host")]
|
||||||
public string Host { get; set; }
|
public string Host { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(2, Label = "Port")]
|
[FieldDefinition(2, Label = "Port", Type = FieldType.Number)]
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(3, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "NotificationsSettingsUseSslHelpText")]
|
[FieldDefinition(3, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "NotificationsSettingsUseSslHelpText")]
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using NzbDrone.Core.Notifications;
|
||||||
|
using Sonarr.Api.V5.Provider;
|
||||||
|
|
||||||
|
namespace Sonarr.Api.V5.Connections;
|
||||||
|
|
||||||
|
public class ConnectionBulkResource : ProviderBulkResource<ConnectionBulkResource>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConnectionBulkResourceMapper : ProviderBulkResourceMapper<ConnectionBulkResource, NotificationDefinition>
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using NzbDrone.Core.Notifications;
|
||||||
|
using NzbDrone.SignalR;
|
||||||
|
using Sonarr.Api.V5.Provider;
|
||||||
|
using Sonarr.Http;
|
||||||
|
|
||||||
|
namespace Sonarr.Api.V5.Connections;
|
||||||
|
|
||||||
|
[V5ApiController]
|
||||||
|
public class ConnectionController : ProviderControllerBase<ConnectionResource, ConnectionBulkResource, INotification, NotificationDefinition>
|
||||||
|
{
|
||||||
|
public static readonly ConnectionResourceMapper ResourceMapper = new();
|
||||||
|
public static readonly ConnectionBulkResourceMapper BulkResourceMapper = new();
|
||||||
|
|
||||||
|
public ConnectionController(IBroadcastSignalRMessage signalRBroadcaster, NotificationFactory notificationFactory)
|
||||||
|
: base(signalRBroadcaster, notificationFactory, "connection", ResourceMapper, BulkResourceMapper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
public override ActionResult<ConnectionResource> UpdateProvider([FromBody] ConnectionBulkResource providerResource)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
public override ActionResult DeleteProviders([FromBody] ConnectionBulkResource resource)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
using NzbDrone.Core.Notifications;
|
||||||
|
using Sonarr.Api.V5.Provider;
|
||||||
|
|
||||||
|
namespace Sonarr.Api.V5.Connections;
|
||||||
|
|
||||||
|
public class ConnectionResource : ProviderResource<ConnectionResource>
|
||||||
|
{
|
||||||
|
public string? Link { get; set; }
|
||||||
|
public bool OnGrab { get; set; }
|
||||||
|
public bool OnDownload { get; set; }
|
||||||
|
public bool OnUpgrade { get; set; }
|
||||||
|
public bool OnImportComplete { get; set; }
|
||||||
|
public bool OnRename { get; set; }
|
||||||
|
public bool OnSeriesAdd { get; set; }
|
||||||
|
public bool OnSeriesDelete { get; set; }
|
||||||
|
public bool OnEpisodeFileDelete { get; set; }
|
||||||
|
public bool OnEpisodeFileDeleteForUpgrade { get; set; }
|
||||||
|
public bool OnHealthIssue { get; set; }
|
||||||
|
public bool IncludeHealthWarnings { get; set; }
|
||||||
|
public bool OnHealthRestored { get; set; }
|
||||||
|
public bool OnApplicationUpdate { get; set; }
|
||||||
|
public bool OnManualInteractionRequired { get; set; }
|
||||||
|
public bool SupportsOnGrab { get; set; }
|
||||||
|
public bool SupportsOnDownload { get; set; }
|
||||||
|
public bool SupportsOnUpgrade { get; set; }
|
||||||
|
public bool SupportsOnImportComplete { get; set; }
|
||||||
|
public bool SupportsOnRename { get; set; }
|
||||||
|
public bool SupportsOnSeriesAdd { get; set; }
|
||||||
|
public bool SupportsOnSeriesDelete { get; set; }
|
||||||
|
public bool SupportsOnEpisodeFileDelete { get; set; }
|
||||||
|
public bool SupportsOnEpisodeFileDeleteForUpgrade { get; set; }
|
||||||
|
public bool SupportsOnHealthIssue { get; set; }
|
||||||
|
public bool SupportsOnHealthRestored { get; set; }
|
||||||
|
public bool SupportsOnApplicationUpdate { get; set; }
|
||||||
|
public bool SupportsOnManualInteractionRequired { get; set; }
|
||||||
|
public string? TestCommand { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConnectionResourceMapper : ProviderResourceMapper<ConnectionResource, NotificationDefinition>
|
||||||
|
{
|
||||||
|
public override ConnectionResource ToResource(NotificationDefinition definition)
|
||||||
|
{
|
||||||
|
var resource = base.ToResource(definition);
|
||||||
|
|
||||||
|
resource.OnGrab = definition.OnGrab;
|
||||||
|
resource.OnDownload = definition.OnDownload;
|
||||||
|
resource.OnUpgrade = definition.OnUpgrade;
|
||||||
|
resource.OnImportComplete = definition.OnImportComplete;
|
||||||
|
resource.OnRename = definition.OnRename;
|
||||||
|
resource.OnSeriesAdd = definition.OnSeriesAdd;
|
||||||
|
resource.OnSeriesDelete = definition.OnSeriesDelete;
|
||||||
|
resource.OnEpisodeFileDelete = definition.OnEpisodeFileDelete;
|
||||||
|
resource.OnEpisodeFileDeleteForUpgrade = definition.OnEpisodeFileDeleteForUpgrade;
|
||||||
|
resource.OnHealthIssue = definition.OnHealthIssue;
|
||||||
|
resource.IncludeHealthWarnings = definition.IncludeHealthWarnings;
|
||||||
|
resource.OnHealthRestored = definition.OnHealthRestored;
|
||||||
|
resource.OnApplicationUpdate = definition.OnApplicationUpdate;
|
||||||
|
resource.OnManualInteractionRequired = definition.OnManualInteractionRequired;
|
||||||
|
resource.SupportsOnGrab = definition.SupportsOnGrab;
|
||||||
|
resource.SupportsOnDownload = definition.SupportsOnDownload;
|
||||||
|
resource.SupportsOnUpgrade = definition.SupportsOnUpgrade;
|
||||||
|
resource.SupportsOnImportComplete = definition.SupportsOnImportComplete;
|
||||||
|
resource.SupportsOnRename = definition.SupportsOnRename;
|
||||||
|
resource.SupportsOnSeriesAdd = definition.SupportsOnSeriesAdd;
|
||||||
|
resource.SupportsOnSeriesDelete = definition.SupportsOnSeriesDelete;
|
||||||
|
resource.SupportsOnEpisodeFileDelete = definition.SupportsOnEpisodeFileDelete;
|
||||||
|
resource.SupportsOnEpisodeFileDeleteForUpgrade = definition.SupportsOnEpisodeFileDeleteForUpgrade;
|
||||||
|
resource.SupportsOnHealthIssue = definition.SupportsOnHealthIssue;
|
||||||
|
resource.SupportsOnHealthRestored = definition.SupportsOnHealthRestored;
|
||||||
|
resource.SupportsOnApplicationUpdate = definition.SupportsOnApplicationUpdate;
|
||||||
|
resource.SupportsOnManualInteractionRequired = definition.SupportsOnManualInteractionRequired;
|
||||||
|
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override NotificationDefinition ToModel(ConnectionResource resource, NotificationDefinition? existingDefinition)
|
||||||
|
{
|
||||||
|
var definition = base.ToModel(resource, existingDefinition);
|
||||||
|
|
||||||
|
definition.OnGrab = resource.OnGrab;
|
||||||
|
definition.OnDownload = resource.OnDownload;
|
||||||
|
definition.OnUpgrade = resource.OnUpgrade;
|
||||||
|
definition.OnImportComplete = resource.OnImportComplete;
|
||||||
|
definition.OnRename = resource.OnRename;
|
||||||
|
definition.OnSeriesAdd = resource.OnSeriesAdd;
|
||||||
|
definition.OnSeriesDelete = resource.OnSeriesDelete;
|
||||||
|
definition.OnEpisodeFileDelete = resource.OnEpisodeFileDelete;
|
||||||
|
definition.OnEpisodeFileDeleteForUpgrade = resource.OnEpisodeFileDeleteForUpgrade;
|
||||||
|
definition.OnHealthIssue = resource.OnHealthIssue;
|
||||||
|
definition.IncludeHealthWarnings = resource.IncludeHealthWarnings;
|
||||||
|
definition.OnHealthRestored = resource.OnHealthRestored;
|
||||||
|
definition.OnApplicationUpdate = resource.OnApplicationUpdate;
|
||||||
|
definition.OnManualInteractionRequired = resource.OnManualInteractionRequired;
|
||||||
|
definition.SupportsOnGrab = resource.SupportsOnGrab;
|
||||||
|
definition.SupportsOnDownload = resource.SupportsOnDownload;
|
||||||
|
definition.SupportsOnUpgrade = resource.SupportsOnUpgrade;
|
||||||
|
definition.SupportsOnImportComplete = resource.SupportsOnImportComplete;
|
||||||
|
definition.SupportsOnRename = resource.SupportsOnRename;
|
||||||
|
definition.SupportsOnSeriesAdd = resource.SupportsOnSeriesAdd;
|
||||||
|
definition.SupportsOnSeriesDelete = resource.SupportsOnSeriesDelete;
|
||||||
|
definition.SupportsOnEpisodeFileDelete = resource.SupportsOnEpisodeFileDelete;
|
||||||
|
definition.SupportsOnEpisodeFileDeleteForUpgrade = resource.SupportsOnEpisodeFileDeleteForUpgrade;
|
||||||
|
definition.SupportsOnHealthIssue = resource.SupportsOnHealthIssue;
|
||||||
|
definition.SupportsOnHealthRestored = resource.SupportsOnHealthRestored;
|
||||||
|
definition.SupportsOnApplicationUpdate = resource.SupportsOnApplicationUpdate;
|
||||||
|
definition.SupportsOnManualInteractionRequired = resource.SupportsOnManualInteractionRequired;
|
||||||
|
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
|
namespace Sonarr.Api.V5.Provider
|
||||||
|
{
|
||||||
|
public class ProviderBulkResource<T>
|
||||||
|
{
|
||||||
|
public List<int> Ids { get; set; } = [];
|
||||||
|
public List<int>? Tags { get; set; }
|
||||||
|
public ApplyTags ApplyTags { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProviderBulkResourceMapper<TProviderBulkResource, TProviderDefinition>
|
||||||
|
where TProviderBulkResource : ProviderBulkResource<TProviderBulkResource>, new()
|
||||||
|
where TProviderDefinition : ProviderDefinition, new()
|
||||||
|
{
|
||||||
|
public virtual List<TProviderDefinition> UpdateModel(TProviderBulkResource resource, List<TProviderDefinition> existingDefinitions)
|
||||||
|
{
|
||||||
|
if (resource == null)
|
||||||
|
{
|
||||||
|
return new List<TProviderDefinition>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return existingDefinitions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,320 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Serializer;
|
||||||
|
using NzbDrone.Core.Datastore.Events;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.ThingiProvider.Events;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
using NzbDrone.SignalR;
|
||||||
|
using Sonarr.Http.REST;
|
||||||
|
using Sonarr.Http.REST.Attributes;
|
||||||
|
|
||||||
|
namespace Sonarr.Api.V5.Provider
|
||||||
|
{
|
||||||
|
public abstract class ProviderControllerBase<TProviderResource, TBulkProviderResource, TProvider, TProviderDefinition> : RestControllerWithSignalR<TProviderResource, TProviderDefinition>,
|
||||||
|
IHandle<ProviderAddedEvent<TProvider>>,
|
||||||
|
IHandle<ProviderUpdatedEvent<TProvider>>,
|
||||||
|
IHandle<ProviderDeletedEvent<TProvider>>
|
||||||
|
where TProviderDefinition : ProviderDefinition, new()
|
||||||
|
where TProvider : IProvider
|
||||||
|
where TProviderResource : ProviderResource<TProviderResource>, new()
|
||||||
|
where TBulkProviderResource : ProviderBulkResource<TBulkProviderResource>, new()
|
||||||
|
{
|
||||||
|
private readonly IProviderFactory<TProvider, TProviderDefinition> _providerFactory;
|
||||||
|
private readonly ProviderResourceMapper<TProviderResource, TProviderDefinition> _resourceMapper;
|
||||||
|
private readonly ProviderBulkResourceMapper<TBulkProviderResource, TProviderDefinition> _bulkResourceMapper;
|
||||||
|
|
||||||
|
protected ProviderControllerBase(IBroadcastSignalRMessage signalRBroadcaster,
|
||||||
|
IProviderFactory<TProvider,
|
||||||
|
TProviderDefinition> providerFactory,
|
||||||
|
string resource,
|
||||||
|
ProviderResourceMapper<TProviderResource, TProviderDefinition> resourceMapper,
|
||||||
|
ProviderBulkResourceMapper<TBulkProviderResource, TProviderDefinition> bulkResourceMapper)
|
||||||
|
: base(signalRBroadcaster)
|
||||||
|
{
|
||||||
|
_providerFactory = providerFactory;
|
||||||
|
_resourceMapper = resourceMapper;
|
||||||
|
_bulkResourceMapper = bulkResourceMapper;
|
||||||
|
|
||||||
|
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
||||||
|
SharedValidator.RuleFor(c => c.Name).Must((v, c) => !_providerFactory.All().Any(p => p.Name.EqualsIgnoreCase(c) && p.Id != v.Id)).WithMessage("Should be unique");
|
||||||
|
SharedValidator.RuleFor(c => c.Implementation).NotEmpty();
|
||||||
|
SharedValidator.RuleFor(c => c.ConfigContract).NotEmpty();
|
||||||
|
|
||||||
|
PostValidator.RuleFor(c => c.Fields).NotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TProviderResource GetResourceById(int id)
|
||||||
|
{
|
||||||
|
var definition = _providerFactory.Get(id);
|
||||||
|
_providerFactory.SetProviderCharacteristics(definition);
|
||||||
|
|
||||||
|
return _resourceMapper.ToResource(definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public List<TProviderResource> GetAll()
|
||||||
|
{
|
||||||
|
var providerDefinitions = _providerFactory.All();
|
||||||
|
|
||||||
|
var result = new List<TProviderResource>(providerDefinitions.Count);
|
||||||
|
|
||||||
|
foreach (var definition in providerDefinitions.OrderBy(p => p.ImplementationName))
|
||||||
|
{
|
||||||
|
_providerFactory.SetProviderCharacteristics(definition);
|
||||||
|
|
||||||
|
result.Add(_resourceMapper.ToResource(definition));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.OrderBy(p => p.Name).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
[RestPostById]
|
||||||
|
[Consumes("application/json")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public ActionResult<TProviderResource> CreateProvider([FromBody] TProviderResource providerResource, [FromQuery] bool forceSave = false)
|
||||||
|
{
|
||||||
|
var providerDefinition = GetDefinition(providerResource, null, true, !forceSave, false);
|
||||||
|
|
||||||
|
if (providerDefinition.Enable)
|
||||||
|
{
|
||||||
|
Test(providerDefinition, !forceSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
providerDefinition = _providerFactory.Create(providerDefinition);
|
||||||
|
|
||||||
|
return Created(providerDefinition.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[RestPutById]
|
||||||
|
[Consumes("application/json")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public ActionResult<TProviderResource> UpdateProvider([FromRoute] int id, [FromBody] TProviderResource providerResource, [FromQuery] bool forceSave = false)
|
||||||
|
{
|
||||||
|
// TODO: Remove fallback to Id from body in next API version bump
|
||||||
|
var existingDefinition = _providerFactory.Find(id) ?? _providerFactory.Find(providerResource.Id);
|
||||||
|
|
||||||
|
if (existingDefinition == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var providerDefinition = GetDefinition(providerResource, existingDefinition, true, !forceSave, false);
|
||||||
|
|
||||||
|
// Compare settings separately because they are not serialized with the definition.
|
||||||
|
var hasDefinitionChanged = !existingDefinition.Equals(providerDefinition) || !existingDefinition.Settings.Equals(providerDefinition.Settings);
|
||||||
|
|
||||||
|
// Only test existing definitions if it is enabled and forceSave isn't set and the definition has changed.
|
||||||
|
if (providerDefinition.Enable && !forceSave && hasDefinitionChanged)
|
||||||
|
{
|
||||||
|
Test(providerDefinition, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDefinitionChanged)
|
||||||
|
{
|
||||||
|
_providerFactory.Update(providerDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Accepted(existingDefinition.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private TProviderDefinition GetDefinition(TProviderResource providerResource, TProviderDefinition? existingDefinition, bool validate, bool includeWarnings, bool forceValidate)
|
||||||
|
{
|
||||||
|
var definition = _resourceMapper.ToModel(providerResource, existingDefinition);
|
||||||
|
|
||||||
|
if (validate && (definition.Enable || forceValidate))
|
||||||
|
{
|
||||||
|
Validate(definition, includeWarnings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RestDeleteById]
|
||||||
|
public ActionResult DeleteProvider(int id)
|
||||||
|
{
|
||||||
|
_providerFactory.Delete(id);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("bulk")]
|
||||||
|
[Consumes("application/json")]
|
||||||
|
public virtual ActionResult DeleteProviders([FromBody] TBulkProviderResource resource)
|
||||||
|
{
|
||||||
|
_providerFactory.Delete(resource.Ids);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("schema")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public List<TProviderResource> GetTemplates()
|
||||||
|
{
|
||||||
|
var defaultDefinitions = _providerFactory.GetDefaultDefinitions().OrderBy(p => p.ImplementationName).ToList();
|
||||||
|
|
||||||
|
var result = new List<TProviderResource>(defaultDefinitions.Count);
|
||||||
|
|
||||||
|
foreach (var providerDefinition in defaultDefinitions)
|
||||||
|
{
|
||||||
|
var providerResource = _resourceMapper.ToResource(providerDefinition);
|
||||||
|
var presetDefinitions = _providerFactory.GetPresetDefinitions(providerDefinition);
|
||||||
|
|
||||||
|
providerResource.Presets = presetDefinitions
|
||||||
|
.Select(v => _resourceMapper.ToResource(v))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
result.Add(providerResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SkipValidation(true, false)]
|
||||||
|
[HttpPost("test")]
|
||||||
|
[Consumes("application/json")]
|
||||||
|
public ActionResult Test([FromBody] TProviderResource providerResource, [FromQuery] bool forceTest = false)
|
||||||
|
{
|
||||||
|
var existingDefinition = providerResource.Id > 0 ? _providerFactory.Find(providerResource.Id) : null;
|
||||||
|
var providerDefinition = GetDefinition(providerResource, existingDefinition, true, !forceTest, true);
|
||||||
|
|
||||||
|
Test(providerDefinition, true);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("testall")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public IActionResult TestAll()
|
||||||
|
{
|
||||||
|
var providerDefinitions = _providerFactory.All()
|
||||||
|
.Where(c => c.Settings.Validate().IsValid && c.Enable)
|
||||||
|
.ToList();
|
||||||
|
var result = new List<ProviderTestAllResult>();
|
||||||
|
|
||||||
|
foreach (var definition in providerDefinitions)
|
||||||
|
{
|
||||||
|
var validationFailures = new List<ValidationFailure>();
|
||||||
|
|
||||||
|
validationFailures.AddRange(definition.Settings.Validate().Errors);
|
||||||
|
validationFailures.AddRange(_providerFactory.Test(definition).Errors);
|
||||||
|
|
||||||
|
result.Add(new ProviderTestAllResult
|
||||||
|
{
|
||||||
|
Id = definition.Id,
|
||||||
|
ValidationFailures = validationFailures
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Any(c => !c.IsValid) ? BadRequest(result) : Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SkipValidation]
|
||||||
|
[HttpPost("action/{name}")]
|
||||||
|
[Consumes("application/json")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public IActionResult RequestAction([FromRoute] string name, [FromBody] TProviderResource providerResource)
|
||||||
|
{
|
||||||
|
var existingDefinition = providerResource.Id > 0 ? _providerFactory.Find(providerResource.Id) : null;
|
||||||
|
var providerDefinition = GetDefinition(providerResource, existingDefinition, false, false, false);
|
||||||
|
|
||||||
|
var query = Request.Query.ToDictionary(x => x.Key, x => x.Value.ToString());
|
||||||
|
|
||||||
|
var data = _providerFactory.RequestAction(providerDefinition, name, query);
|
||||||
|
|
||||||
|
return Content(data.ToJson(), "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
public virtual void Handle(ProviderAddedEvent<TProvider> message)
|
||||||
|
{
|
||||||
|
BroadcastResourceChange(ModelAction.Created, message.Definition.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
public virtual void Handle(ProviderUpdatedEvent<TProvider> message)
|
||||||
|
{
|
||||||
|
BroadcastResourceChange(ModelAction.Updated, message.Definition.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
public virtual void Handle(ProviderDeletedEvent<TProvider> message)
|
||||||
|
{
|
||||||
|
BroadcastResourceChange(ModelAction.Deleted, message.ProviderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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)
|
||||||
|
{
|
||||||
|
var result = validationResult as NzbDroneValidationResult ?? new NzbDroneValidationResult(validationResult.Errors);
|
||||||
|
|
||||||
|
if (includeWarnings && (!result.IsValid || result.HasWarnings))
|
||||||
|
{
|
||||||
|
throw new ValidationException(result.Failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.IsValid)
|
||||||
|
{
|
||||||
|
throw new ValidationException(result.Errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using NzbDrone.Common.Reflection;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using Sonarr.Http.ClientSchema;
|
||||||
|
using Sonarr.Http.REST;
|
||||||
|
|
||||||
|
namespace Sonarr.Api.V5.Provider
|
||||||
|
{
|
||||||
|
public class ProviderResource<T> : RestResource
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public List<Field> Fields { get; set; } = [];
|
||||||
|
public string? ImplementationName { get; set; }
|
||||||
|
public string? Implementation { get; set; }
|
||||||
|
public string? ConfigContract { get; set; }
|
||||||
|
public string? InfoLink { get; set; }
|
||||||
|
public ProviderMessage? Message { get; set; }
|
||||||
|
public HashSet<int> Tags { get; set; } = [];
|
||||||
|
public List<T> Presets { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProviderResourceMapper<TProviderResource, TProviderDefinition>
|
||||||
|
where TProviderResource : ProviderResource<TProviderResource>, new()
|
||||||
|
where TProviderDefinition : ProviderDefinition, new()
|
||||||
|
{
|
||||||
|
public virtual TProviderResource ToResource(TProviderDefinition definition)
|
||||||
|
{
|
||||||
|
return new TProviderResource
|
||||||
|
{
|
||||||
|
Id = definition.Id,
|
||||||
|
|
||||||
|
Name = definition.Name,
|
||||||
|
ImplementationName = definition.ImplementationName,
|
||||||
|
Implementation = definition.Implementation,
|
||||||
|
ConfigContract = definition.ConfigContract,
|
||||||
|
Message = definition.Message,
|
||||||
|
Tags = definition.Tags,
|
||||||
|
Fields = SchemaBuilder.ToSchema(definition.Settings),
|
||||||
|
|
||||||
|
InfoLink = $"https://wiki.servarr.com/sonarr/supported#{definition.Implementation.ToLower()}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual TProviderDefinition ToModel(TProviderResource resource, TProviderDefinition? existingDefinition)
|
||||||
|
{
|
||||||
|
var definition = new TProviderDefinition
|
||||||
|
{
|
||||||
|
Id = resource.Id,
|
||||||
|
Name = resource.Name,
|
||||||
|
ImplementationName = resource.ImplementationName,
|
||||||
|
Implementation = resource.Implementation,
|
||||||
|
ConfigContract = resource.ConfigContract,
|
||||||
|
Message = resource.Message,
|
||||||
|
Tags = resource.Tags
|
||||||
|
};
|
||||||
|
|
||||||
|
var configContract = ReflectionExtensions.CoreAssembly.FindTypeByName(definition.ConfigContract);
|
||||||
|
definition.Settings = (IProviderConfig)SchemaBuilder.ReadFromSchema(resource.Fields, configContract, existingDefinition?.Settings);
|
||||||
|
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using FluentValidation.Results;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
|
||||||
|
namespace Sonarr.Api.V5.Provider
|
||||||
|
{
|
||||||
|
public class ProviderTestAllResult
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public bool IsValid => ValidationFailures.Empty();
|
||||||
|
public List<ValidationFailure> ValidationFailures { get; set; }
|
||||||
|
|
||||||
|
public ProviderTestAllResult()
|
||||||
|
{
|
||||||
|
ValidationFailures = new List<ValidationFailure>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user