Add support for Applications

This commit is contained in:
nitsua
2020-10-20 14:15:05 -04:00
committed by Qstick
parent 191d06deca
commit 47fbab02c5
37 changed files with 1607 additions and 3 deletions

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Applications
{
public abstract class ApplicationBase<TSettings> : IApplications
where TSettings : IProviderConfig, new()
{
public abstract string Name { get; }
public Type ConfigContract => typeof(TSettings);
public virtual ProviderMessage Message => null;
public ProviderDefinition Definition { get; set; }
public abstract ValidationResult Test();
protected TSettings Settings => (TSettings)Definition.Settings;
public override string ToString()
{
return GetType().Name;
}
public virtual IEnumerable<ProviderDefinition> DefaultDefinitions
{
get
{
var config = (IProviderConfig)new TSettings();
yield return new ApplicationDefinition
{
Name = GetType().Name,
SyncLevel = ApplicationSyncLevel.AddOnly,
Implementation = GetType().Name,
Settings = config
};
}
}
public virtual object RequestAction(string action, IDictionary<string, string> query)
{
return null;
}
}
}

View File

@@ -0,0 +1,11 @@
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Applications
{
public class ApplicationDefinition : ProviderDefinition
{
public ApplicationSyncLevel SyncLevel { get; set; }
public override bool Enable => SyncLevel == ApplicationSyncLevel.AddOnly || SyncLevel == ApplicationSyncLevel.FullSync;
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Composition;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Applications
{
public interface IApplicationsFactory : IProviderFactory<IApplications, ApplicationDefinition>
{
}
public class ApplicationFactory : ProviderFactory<IApplications, ApplicationDefinition>, IApplicationsFactory
{
public ApplicationFactory(IApplicationsRepository providerRepository, IEnumerable<IApplications> providers, IContainer container, IEventAggregator eventAggregator, Logger logger)
: base(providerRepository, providers, container, eventAggregator, logger)
{
}
}
}

View File

@@ -0,0 +1,24 @@
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Applications
{
public interface IApplicationsRepository : IProviderRepository<ApplicationDefinition>
{
void UpdateSettings(ApplicationDefinition model);
}
public class ApplicationRepository : ProviderRepository<ApplicationDefinition>, IApplicationsRepository
{
public ApplicationRepository(IMainDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator)
{
}
public void UpdateSettings(ApplicationDefinition model)
{
SetFields(model, m => m.Settings);
}
}
}

View File

@@ -0,0 +1,16 @@
using NLog;
namespace NzbDrone.Core.Applications
{
public class ApplicationService
{
private readonly IApplicationsFactory _applicationsFactory;
private readonly Logger _logger;
public ApplicationService(IApplicationsFactory applicationsFactory, Logger logger)
{
_applicationsFactory = applicationsFactory;
_logger = logger;
}
}
}

View File

@@ -0,0 +1,9 @@
namespace NzbDrone.Core.Applications
{
public enum ApplicationSyncLevel
{
Disabled,
AddOnly,
FullSync
}
}

View File

@@ -0,0 +1,8 @@
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Applications
{
public interface IApplications : IProvider
{
}
}

View File

@@ -0,0 +1,27 @@
using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Applications.Radarr
{
public class Radarr : ApplicationBase<RadarrSettings>
{
public override string Name => "Radarr";
private readonly IRadarrV3Proxy _radarrV3Proxy;
public Radarr(IRadarrV3Proxy radarrV3Proxy)
{
_radarrV3Proxy = radarrV3Proxy;
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_radarrV3Proxy.Test(Settings));
return new ValidationResult(failures);
}
}
}

View File

@@ -0,0 +1,36 @@
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Applications.Radarr
{
public class RadarrSettingsValidator : AbstractValidator<RadarrSettings>
{
public RadarrSettingsValidator()
{
RuleFor(c => c.BaseUrl).IsValidUrl();
RuleFor(c => c.ApiKey).NotEmpty();
}
}
public class RadarrSettings : IProviderConfig
{
private static readonly RadarrSettingsValidator Validator = new RadarrSettingsValidator();
public RadarrSettings()
{
}
[FieldDefinition(0, Label = "Radarr Server", HelpText = "Radarr server URL, including http(s):// and port if needed")]
public string BaseUrl { get; set; }
[FieldDefinition(1, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Radarr in Settings/General")]
public string ApiKey { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
}

View File

@@ -0,0 +1,7 @@
namespace NzbDrone.Core.Applications.Radarr
{
public class RadarrStatus
{
public string Version { get; set; }
}
}

View File

@@ -0,0 +1,78 @@
using System;
using System.Net;
using FluentValidation.Results;
using Newtonsoft.Json;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
namespace NzbDrone.Core.Applications.Radarr
{
public interface IRadarrV3Proxy
{
ValidationFailure Test(RadarrSettings settings);
}
public class RadarrV3Proxy : IRadarrV3Proxy
{
private readonly IHttpClient _httpClient;
private readonly Logger _logger;
public RadarrV3Proxy(IHttpClient httpClient, Logger logger)
{
_httpClient = httpClient;
_logger = logger;
}
public RadarrStatus GetStatus(RadarrSettings settings)
{
return Execute<RadarrStatus>("/api/v3/system/status", settings);
}
public ValidationFailure Test(RadarrSettings settings)
{
try
{
GetStatus(settings);
}
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
{
_logger.Error(ex, "API Key is invalid");
return new ValidationFailure("ApiKey", "API Key is invalid");
}
_logger.Error(ex, "Unable to send test message");
return new ValidationFailure("ApiKey", "Unable to send test message");
}
catch (Exception ex)
{
_logger.Error(ex, "Unable to send test message");
return new ValidationFailure("", "Unable to send test message");
}
return null;
}
private TResource Execute<TResource>(string resource, RadarrSettings settings)
where TResource : new()
{
if (settings.BaseUrl.IsNullOrWhiteSpace() || settings.ApiKey.IsNullOrWhiteSpace())
{
return new TResource();
}
var baseUrl = settings.BaseUrl.TrimEnd('/');
var request = new HttpRequestBuilder(baseUrl).Resource(resource).Accept(HttpAccept.Json)
.SetHeader("X-Api-Key", settings.ApiKey).Build();
var response = _httpClient.Get(request);
var results = JsonConvert.DeserializeObject<TResource>(response.Content);
return results;
}
}
}

View File

@@ -52,7 +52,9 @@ namespace NzbDrone.Core.Datastore.Migration
.WithColumn("Name").AsString().Unique()
.WithColumn("Implementation").AsString()
.WithColumn("Settings").AsString().Nullable()
.WithColumn("ConfigContract").AsString().Nullable();
.WithColumn("ConfigContract").AsString().Nullable()
.WithColumn("SyncLevel").AsInt32()
.WithColumn("Tags").AsString().Nullable();
Create.TableForModel("Tags")
.WithColumn("Label").AsString().Unique();

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using Dapper;
using NzbDrone.Common.Reflection;
using NzbDrone.Core.Applications;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.CustomFilters;
@@ -51,6 +52,9 @@ namespace NzbDrone.Core.Datastore
.Ignore(x => x.ImplementationName)
.Ignore(i => i.SupportsOnHealthIssue);
Mapper.Entity<ApplicationDefinition>("Applications").RegisterModel()
.Ignore(x => x.ImplementationName);
Mapper.Entity<History.History>("History").RegisterModel();
Mapper.Entity<Log>("Logs").RegisterModel();

View File

@@ -0,0 +1,14 @@
using NzbDrone.Core.Applications;
namespace Prowlarr.Api.V1.Application
{
public class ApplicationModule : ProviderModuleBase<ApplicationResource, IApplications, ApplicationDefinition>
{
public static readonly ApplicationResourceMapper ResourceMapper = new ApplicationResourceMapper();
public ApplicationModule(ApplicationFactory applicationsFactory)
: base(applicationsFactory, "applications", ResourceMapper)
{
}
}
}

View File

@@ -0,0 +1,41 @@
using NzbDrone.Core.Applications;
namespace Prowlarr.Api.V1.Application
{
public class ApplicationResource : ProviderResource
{
public ApplicationSyncLevel SyncLevel { get; set; }
public string TestCommand { get; set; }
}
public class ApplicationResourceMapper : ProviderResourceMapper<ApplicationResource, ApplicationDefinition>
{
public override ApplicationResource ToResource(ApplicationDefinition definition)
{
if (definition == null)
{
return default;
}
var resource = base.ToResource(definition);
resource.SyncLevel = definition.SyncLevel;
return resource;
}
public override ApplicationDefinition ToModel(ApplicationResource resource)
{
if (resource == null)
{
return default;
}
var definition = base.ToModel(resource);
definition.SyncLevel = resource.SyncLevel;
return definition;
}
}
}