Provider testing improvements

New: Test button for indexers in UI

Fixed: Testing download clients shows error messages in UI
Fixed: Testing notifications shows error messages in UI
pull/4/head
Mark McDowall 11 years ago
parent c5bd8b27fb
commit 7af782d353

@ -23,7 +23,10 @@ namespace NzbDrone.Api
: base(resource) : base(resource)
{ {
_providerFactory = providerFactory; _providerFactory = providerFactory;
Get["schema"] = x => GetTemplates(); Get["schema"] = x => GetTemplates();
Post["test"] = x => Test(ReadResourceFromRequest());
GetResourceAll = GetAll; GetResourceAll = GetAll;
GetResourceById = GetProviderById; GetResourceById = GetProviderById;
CreateResource = CreateProvider; CreateResource = CreateProvider;
@ -63,16 +66,26 @@ namespace NzbDrone.Api
private int CreateProvider(TProviderResource providerResource) private int CreateProvider(TProviderResource providerResource)
{ {
var provider = GetDefinition(providerResource); var providerDefinition = GetDefinition(providerResource);
provider = _providerFactory.Create(provider);
return provider.Id; if (providerDefinition.Enable)
{
Test(providerDefinition);
}
providerDefinition = _providerFactory.Create(providerDefinition);
return providerDefinition.Id;
} }
private void UpdateProvider(TProviderResource providerResource) private void UpdateProvider(TProviderResource providerResource)
{ {
var providerDefinition = GetDefinition(providerResource); var providerDefinition = GetDefinition(providerResource);
Validate(providerDefinition); if (providerDefinition.Enable)
{
Test(providerDefinition);
}
_providerFactory.Update(providerDefinition); _providerFactory.Update(providerDefinition);
} }
@ -133,6 +146,25 @@ namespace NzbDrone.Api
return result.AsResponse(); return result.AsResponse();
} }
private Response Test(TProviderResource providerResource)
{
var providerDefinition = GetDefinition(providerResource);
Test(providerDefinition);
return "{}";
}
private void Test(TProviderDefinition providerDefinition)
{
var result = _providerFactory.Test(providerDefinition);
if (!result.IsValid)
{
throw new ValidationException(result.Errors);
}
}
protected virtual void Validate(TProviderDefinition definition) protected virtual void Validate(TProviderDefinition definition)
{ {
var validationResult = definition.Settings.Validate(); var validationResult = definition.Settings.Validate();
@ -143,4 +175,4 @@ namespace NzbDrone.Api
} }
} }
} }
} }

@ -194,7 +194,7 @@ namespace NzbDrone.Api.REST
var errors = SharedValidator.Validate(resource).Errors.ToList(); var errors = SharedValidator.Validate(resource).Errors.ToList();
if (Request.Method.Equals("POST", StringComparison.InvariantCultureIgnoreCase)) if (Request.Method.Equals("POST", StringComparison.InvariantCultureIgnoreCase) && !Request.Url.Path.EndsWith("/test", StringComparison.InvariantCultureIgnoreCase))
{ {
errors.AddRange(PostValidator.Validate(resource).Errors); errors.AddRange(PostValidator.Validate(resource).Errors);
} }

@ -1,8 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Indexers.Newznab;

@ -2,19 +2,18 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using Omu.ValueInjecter;
namespace NzbDrone.Core.Download.Clients.Nzbget namespace NzbDrone.Core.Download.Clients.Nzbget
{ {
public class Nzbget : DownloadClientBase<NzbgetSettings>, IExecute<TestNzbgetCommand> public class Nzbget : DownloadClientBase<NzbgetSettings>
{ {
private readonly INzbgetProxy _proxy; private readonly INzbgetProxy _proxy;
private readonly IHttpProvider _httpProvider; private readonly IHttpProvider _httpProvider;
@ -265,25 +264,42 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
return _proxy.GetVersion(Settings); return _proxy.GetVersion(Settings);
} }
public override void Test(NzbgetSettings settings) public override ValidationResult Test()
{ {
_proxy.GetVersion(settings); var failures = new List<ValidationFailure>();
var config = _proxy.GetConfig(settings); failures.AddIfNotNull(TestConnection());
var categories = GetCategories(config); failures.AddIfNotNull(TestCategory());
return new ValidationResult(failures);
}
if (!settings.TvCategory.IsNullOrWhiteSpace() && !categories.Any(v => v.Name == settings.TvCategory)) private ValidationFailure TestConnection()
{
try
{
_proxy.GetVersion(Settings);
}
catch (Exception ex)
{ {
throw new ApplicationException("Category does not exist"); _logger.ErrorException(ex.Message, ex);
return new ValidationFailure("Host", "Unable to connect to NZBGet");
} }
return null;
} }
public void Execute(TestNzbgetCommand message) private ValidationFailure TestCategory()
{ {
var settings = new NzbgetSettings(); var config = _proxy.GetConfig(Settings);
settings.InjectFrom(message); var categories = GetCategories(config);
if (!Settings.TvCategory.IsNullOrWhiteSpace() && !categories.Any(v => v.Name == Settings.TvCategory))
{
return new ValidationFailure("TvCategory", "Category does not exist");
}
Test(settings); return null;
} }
// Javascript doesn't support 64 bit integers natively so json officially doesn't either. // Javascript doesn't support 64 bit integers natively so json officially doesn't either.

@ -1,26 +0,0 @@
using System;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
public class TestNzbgetCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public String Host { get; set; }
public Int32 Port { get; set; }
public String Username { get; set; }
public String Password { get; set; }
public String TvCategory { get; set; }
public Int32 RecentTvPriority { get; set; }
public Int32 OlderTvPriority { get; set; }
public Boolean UseSsl { get; set; }
}
}

@ -1,28 +1,24 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using Omu.ValueInjecter;
namespace NzbDrone.Core.Download.Clients.Pneumatic namespace NzbDrone.Core.Download.Clients.Pneumatic
{ {
public class Pneumatic : DownloadClientBase<PneumaticSettings>, IExecute<TestPneumaticCommand> public class Pneumatic : DownloadClientBase<PneumaticSettings>
{ {
private readonly IHttpProvider _httpProvider; private readonly IHttpProvider _httpProvider;
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private static readonly Logger logger = NzbDroneLogger.GetLogger();
public Pneumatic(IHttpProvider httpProvider, public Pneumatic(IHttpProvider httpProvider,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IConfigService configService, IConfigService configService,
@ -57,10 +53,10 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
//Save to the Pneumatic directory (The user will need to ensure its accessible by XBMC) //Save to the Pneumatic directory (The user will need to ensure its accessible by XBMC)
var filename = Path.Combine(Settings.NzbFolder, title + ".nzb"); var filename = Path.Combine(Settings.NzbFolder, title + ".nzb");
logger.Debug("Downloading NZB from: {0} to: {1}", url, filename); _logger.Debug("Downloading NZB from: {0} to: {1}", url, filename);
_httpProvider.DownloadFile(url, filename); _httpProvider.DownloadFile(url, filename);
logger.Debug("NZB Download succeeded, saved to: {0}", filename); _logger.Debug("NZB Download succeeded, saved to: {0}", filename);
var contents = String.Format("plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb={0}&nzbname={1}", filename, title); var contents = String.Format("plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb={0}&nzbname={1}", filename, title);
_diskProvider.WriteAllText(Path.Combine(_configService.DownloadedEpisodesFolder, title + ".strm"), contents); _diskProvider.WriteAllText(Path.Combine(_configService.DownloadedEpisodesFolder, title + ".strm"), contents);
@ -101,24 +97,35 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
return status; return status;
} }
public override void Test(PneumaticSettings settings) public override ValidationResult Test()
{ {
PerformWriteTest(settings.NzbFolder); var failures = new List<ValidationFailure>();
}
private void PerformWriteTest(string folder) failures.AddIfNotNull(TestWrite(Settings.NzbFolder, "NzbFolder"));
{
var testPath = Path.Combine(folder, "drone_test.txt"); return new ValidationResult(failures);
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString());
_diskProvider.DeleteFile(testPath);
} }
public void Execute(TestPneumaticCommand message) private ValidationFailure TestWrite(String folder, String propertyName)
{ {
var settings = new PneumaticSettings(); if (!_diskProvider.FolderExists(folder))
settings.InjectFrom(message); {
return new ValidationFailure(propertyName, "Folder does not exist");
}
Test(settings); try
{
var testPath = Path.Combine(folder, "drone_test.txt");
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString());
_diskProvider.DeleteFile(testPath);
}
catch (Exception ex)
{
_logger.ErrorException(ex.Message, ex);
return new ValidationFailure(propertyName, "Unable to write to folder");
}
return null;
} }
} }
} }

@ -1,18 +0,0 @@
using System;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Download.Clients.Pneumatic
{
public class TestPneumaticCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public String Folder { get; set; }
}
}

@ -2,19 +2,18 @@
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using Omu.ValueInjecter;
namespace NzbDrone.Core.Download.Clients.Sabnzbd namespace NzbDrone.Core.Download.Clients.Sabnzbd
{ {
public class Sabnzbd : DownloadClientBase<SabnzbdSettings>, IExecute<TestSabnzbdCommand> public class Sabnzbd : DownloadClientBase<SabnzbdSettings>
{ {
private readonly IHttpProvider _httpProvider; private readonly IHttpProvider _httpProvider;
private readonly ISabnzbdProxy _proxy; private readonly ISabnzbdProxy _proxy;
@ -218,22 +217,41 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return status; return status;
} }
public override void Test(SabnzbdSettings settings) public override ValidationResult Test()
{ {
var categories = _proxy.GetCategories(settings); var failures = new List<ValidationFailure>();
if (!settings.TvCategory.IsNullOrWhiteSpace() && !categories.Any(v => v == settings.TvCategory)) failures.AddIfNotNull(TestConnection());
failures.AddIfNotNull(TestCategory());
return new ValidationResult(failures);
}
private ValidationFailure TestConnection()
{
try
{
_proxy.GetCategories(Settings);
}
catch (Exception ex)
{ {
throw new ApplicationException("Category does not exist"); _logger.ErrorException(ex.Message, ex);
return new ValidationFailure("Host", "Unable to connect to SABnzbd");
} }
return null;
} }
public void Execute(TestSabnzbdCommand message) private ValidationFailure TestCategory()
{ {
var settings = new SabnzbdSettings(); var categories = _proxy.GetCategories(Settings);
settings.InjectFrom(message);
if (!Settings.TvCategory.IsNullOrWhiteSpace() && !categories.Any(v => v == Settings.TvCategory))
{
return new ValidationFailure("TvCategory", "Category does not exist");
}
Test(settings); return null;
} }
} }
} }

@ -1,27 +0,0 @@
using System;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Download.Clients.Sabnzbd
{
public class TestSabnzbdCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public String Host { get; set; }
public Int32 Port { get; set; }
public String ApiKey { get; set; }
public String Username { get; set; }
public String Password { get; set; }
public String TvCategory { get; set; }
public Int32 RecentTvPriority { get; set; }
public Int32 OlderTvPriority { get; set; }
public Boolean UseSsl { get; set; }
}
}

@ -1,19 +0,0 @@
using System;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Download.Clients.UsenetBlackhole
{
public class TestUsenetBlackholeCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public String NzbFolder { get; set; }
public String WatchFolder { get; set; }
}
}

@ -2,22 +2,21 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using Omu.ValueInjecter;
namespace NzbDrone.Core.Download.Clients.UsenetBlackhole namespace NzbDrone.Core.Download.Clients.UsenetBlackhole
{ {
public class UsenetBlackhole : DownloadClientBase<UsenetBlackholeSettings>, IExecute<TestUsenetBlackholeCommand> public class UsenetBlackhole : DownloadClientBase<UsenetBlackholeSettings>
{ {
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly IDiskScanService _diskScanService; private readonly IDiskScanService _diskScanService;
@ -146,25 +145,36 @@ namespace NzbDrone.Core.Download.Clients.UsenetBlackhole
}; };
} }
public override void Test(UsenetBlackholeSettings settings) public override ValidationResult Test()
{ {
PerformWriteTest(settings.NzbFolder); var failures = new List<ValidationFailure>();
PerformWriteTest(settings.WatchFolder);
}
private void PerformWriteTest(string folder) failures.AddIfNotNull(TestWrite(Settings.NzbFolder, "NzbFolder"));
{ failures.AddIfNotNull(TestWrite(Settings.WatchFolder, "WatchFolder"));
var testPath = Path.Combine(folder, "drone_test.txt");
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString()); return new ValidationResult(failures);
_diskProvider.DeleteFile(testPath);
} }
public void Execute(TestUsenetBlackholeCommand message) private ValidationFailure TestWrite(String folder, String propertyName)
{ {
var settings = new UsenetBlackholeSettings(); if (!_diskProvider.FolderExists(folder))
settings.InjectFrom(message); {
return new ValidationFailure(propertyName, "Folder does not exist");
}
Test(settings); try
{
var testPath = Path.Combine(folder, "drone_test.txt");
_diskProvider.WriteAllText(testPath, DateTime.Now.ToString());
_diskProvider.DeleteFile(testPath);
}
catch (Exception ex)
{
_logger.ErrorException(ex.Message, ex);
return new ValidationFailure(propertyName, "Unable to write to folder");
}
return null;
} }
} }
} }

@ -1,7 +1,6 @@
using System; using System;
using FluentValidation; using FluentValidation;
using FluentValidation.Results; using FluentValidation.Results;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation.Paths; using NzbDrone.Core.Validation.Paths;
@ -12,7 +11,6 @@ namespace NzbDrone.Core.Download.Clients.UsenetBlackhole
{ {
public UsenetBlackholeSettingsValidator() public UsenetBlackholeSettingsValidator()
{ {
//Todo: Validate that the path actually exists
RuleFor(c => c.NzbFolder).IsValidPath(); RuleFor(c => c.NzbFolder).IsValidPath();
RuleFor(c => c.WatchFolder).IsValidPath(); RuleFor(c => c.WatchFolder).IsValidPath();
} }

@ -1,6 +1,6 @@
using System; using System;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -34,6 +34,7 @@ namespace NzbDrone.Core.Download
} }
public ProviderDefinition Definition { get; set; } public ProviderDefinition Definition { get; set; }
public abstract ValidationResult Test();
protected TSettings Settings protected TSettings Settings
{ {
@ -55,8 +56,6 @@ namespace NzbDrone.Core.Download
return GetType().Name; return GetType().Name;
} }
public abstract DownloadProtocol Protocol public abstract DownloadProtocol Protocol
{ {
get; get;
@ -68,8 +67,6 @@ namespace NzbDrone.Core.Download
public abstract void RetryDownload(string id); public abstract void RetryDownload(string id);
public abstract DownloadClientStatus GetStatus(); public abstract DownloadClientStatus GetStatus();
public abstract void Test(TSettings settings);
protected RemoteEpisode GetRemoteEpisode(String title) protected RemoteEpisode GetRemoteEpisode(String title)
{ {
var parsedEpisodeInfo = Parser.Parser.ParseTitle(title); var parsedEpisodeInfo = Parser.Parser.ParseTitle(title);

@ -1,12 +1,10 @@
using System; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Download namespace NzbDrone.Core.Download
{ {
public class DownloadClientDefinition : ProviderDefinition public class DownloadClientDefinition : ProviderDefinition
{ {
public Boolean Enable { get; set; }
public DownloadProtocol Protocol { get; set; } public DownloadProtocol Protocol { get; set; }
} }
} }

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using FluentValidation.Results;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers.Animezb namespace NzbDrone.Core.Indexers.Animezb
@ -72,6 +73,11 @@ namespace NzbDrone.Core.Indexers.Animezb
return new List<string>(); return new List<string>();
} }
public override ValidationResult Test()
{
return new ValidationResult();
}
private String GetSearchQuery(string title, int absoluteEpisodeNumber) private String GetSearchQuery(string title, int absoluteEpisodeNumber)
{ {
var match = RemoveSingleCharacterRegex.Match(title); var match = RemoveSingleCharacterRegex.Match(title);

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using FluentValidation.Results;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers.Fanzub namespace NzbDrone.Core.Indexers.Fanzub
@ -69,6 +70,11 @@ namespace NzbDrone.Core.Indexers.Fanzub
return new List<string>(); return new List<string>();
} }
public override ValidationResult Test()
{
return new ValidationResult();
}
private IEnumerable<String> GetTitleSearchStrings(string title, int absoluteEpisodeNumber) private IEnumerable<String> GetTitleSearchStrings(string title, int absoluteEpisodeNumber)
{ {
var formats = new[] { "{0}%20{1:00}", "{0}%20-%20{1:00}" }; var formats = new[] { "{0}%20{1:00}", "{0}%20-%20{1:00}" };

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers namespace NzbDrone.Core.Indexers
@ -32,6 +33,7 @@ namespace NzbDrone.Core.Indexers
public virtual ProviderDefinition Definition { get; set; } public virtual ProviderDefinition Definition { get; set; }
public abstract ValidationResult Test();
public abstract DownloadProtocol Protocol { get; } public abstract DownloadProtocol Protocol { get; }
public virtual Boolean SupportsFeed { get { return true; } } public virtual Boolean SupportsFeed { get { return true; } }

@ -4,8 +4,6 @@ namespace NzbDrone.Core.Indexers
{ {
public class IndexerDefinition : ProviderDefinition public class IndexerDefinition : ProviderDefinition
{ {
public bool Enable { get; set; }
public DownloadProtocol Protocol { get; set; } public DownloadProtocol Protocol { get; set; }
} }
} }

@ -14,7 +14,6 @@ namespace NzbDrone.Core.Indexers
public class IndexerFactory : ProviderFactory<IIndexer, IndexerDefinition>, IIndexerFactory public class IndexerFactory : ProviderFactory<IIndexer, IndexerDefinition>, IIndexerFactory
{ {
private readonly IIndexerRepository _providerRepository;
private readonly INewznabTestService _newznabTestService; private readonly INewznabTestService _newznabTestService;
public IndexerFactory(IIndexerRepository providerRepository, public IndexerFactory(IIndexerRepository providerRepository,
@ -25,7 +24,6 @@ namespace NzbDrone.Core.Indexers
Logger logger) Logger logger)
: base(providerRepository, providers, container, eventAggregator, logger) : base(providerRepository, providers, container, eventAggregator, logger)
{ {
_providerRepository = providerRepository;
_newznabTestService = newznabTestService; _newznabTestService = newznabTestService;
} }
@ -39,17 +37,6 @@ namespace NzbDrone.Core.Indexers
return base.Active().Where(c => c.Enable).ToList(); return base.Active().Where(c => c.Enable).ToList();
} }
public override IndexerDefinition Create(IndexerDefinition definition)
{
if (definition.Implementation == typeof(Newznab.Newznab).Name)
{
var indexer = GetInstance(definition);
_newznabTestService.Test(indexer);
}
return base.Create(definition);
}
protected override IndexerDefinition GetProviderCharacteristics(IIndexer provider, IndexerDefinition definition) protected override IndexerDefinition GetProviderCharacteristics(IIndexer provider, IndexerDefinition definition)
{ {
definition = base.GetProviderCharacteristics(provider, definition); definition = base.GetProviderCharacteristics(provider, definition);

@ -1,14 +1,35 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Linq; using System.Linq;
using FluentValidation;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers.Newznab namespace NzbDrone.Core.Indexers.Newznab
{ {
public class Newznab : IndexerBase<NewznabSettings> public class Newznab : IndexerBase<NewznabSettings>
{ {
private readonly IFetchFeedFromIndexers _feedFetcher;
private readonly HttpProvider _httpProvider;
private readonly Logger _logger;
public Newznab(IFetchFeedFromIndexers feedFetcher, HttpProvider httpProvider, Logger logger)
{
_feedFetcher = feedFetcher;
_httpProvider = httpProvider;
_logger = logger;
}
public Newznab()
{
}
public override DownloadProtocol Protocol { get { return DownloadProtocol.Usenet; } } public override DownloadProtocol Protocol { get { return DownloadProtocol.Usenet; } }
public override Int32 SupportedPageSize { get { return 100; } } public override Int32 SupportedPageSize { get { return 100; } }
@ -169,6 +190,41 @@ namespace NzbDrone.Core.Indexers.Newznab
return RecentFeed.Select(url => String.Format("{0}&offset={1}&limit=100&q={2}", url.Replace("t=tvsearch", "t=search"), offset, query)); return RecentFeed.Select(url => String.Format("{0}&offset={1}&limit=100&q={2}", url.Replace("t=tvsearch", "t=search"), offset, query));
} }
public override ValidationResult Test()
{
var releases = _feedFetcher.FetchRss(this);
if (releases.Any()) return new ValidationResult();
try
{
var url = RecentFeed.First();
var xml = _httpProvider.DownloadString(url);
NewznabPreProcessor.Process(xml, url);
}
catch (ApiKeyException)
{
_logger.Warn("Indexer returned result for Newznab RSS URL, API Key appears to be invalid");
var apiKeyFailure = new ValidationFailure("ApiKey", "Invalid API Key");
return new ValidationResult(new List<ValidationFailure> { apiKeyFailure });
}
catch (RequestLimitReachedException)
{
_logger.Warn("Request limit reached");
}
catch (Exception ex)
{
_logger.WarnException("Unable to connect to indexer: " + ex.Message, ex);
var failure = new ValidationFailure("Url", "Unable to connect to indexer, check the log for more details");
return new ValidationResult(new List<ValidationFailure> { failure });
}
return new ValidationResult();
}
private static string NewsnabifyTitle(string title) private static string NewsnabifyTitle(string title)
{ {
return title.Replace("+", "%20"); return title.Replace("+", "%20");

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
namespace NzbDrone.Core.Indexers.Omgwtfnzbs namespace NzbDrone.Core.Indexers.Omgwtfnzbs
{ {
@ -79,5 +81,10 @@ namespace NzbDrone.Core.Indexers.Omgwtfnzbs
{ {
return new List<string>(); return new List<string>();
} }
public override ValidationResult Test()
{
return new ValidationResult();
}
} }
} }

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers.Wombles namespace NzbDrone.Core.Indexers.Wombles
@ -46,5 +48,10 @@ namespace NzbDrone.Core.Indexers.Wombles
{ {
return new List<string>(); return new List<string>();
} }
public override ValidationResult Test()
{
return new ValidationResult();
}
} }
} }

@ -1,10 +1,8 @@
using System; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Metadata namespace NzbDrone.Core.Metadata
{ {
public class MetadataDefinition : ProviderDefinition public class MetadataDefinition : ProviderDefinition
{ {
public Boolean Enable { get; set; }
} }
} }

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Metadata.Files; using NzbDrone.Core.Metadata.Files;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
@ -27,6 +29,11 @@ namespace NzbDrone.Core.Metadata
public ProviderDefinition Definition { get; set; } public ProviderDefinition Definition { get; set; }
public ValidationResult Test()
{
return new ValidationResult();
}
public abstract List<MetadataFile> AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles); public abstract List<MetadataFile> AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles);
public abstract MetadataFile FindMetadataFile(Series series, string path); public abstract MetadataFile FindMetadataFile(Series series, string path);

@ -1,15 +1,18 @@
using System; using System;
using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Email namespace NzbDrone.Core.Notifications.Email
{ {
public class Email : NotificationBase<EmailSettings> public class Email : NotificationBase<EmailSettings>
{ {
private readonly IEmailService _smtpProvider; private readonly IEmailService _emailService;
public Email(IEmailService smtpProvider) public Email(IEmailService emailService)
{ {
_smtpProvider = smtpProvider; _emailService = emailService;
} }
public override string Link public override string Link
@ -20,9 +23,9 @@ namespace NzbDrone.Core.Notifications.Email
public override void OnGrab(string message) public override void OnGrab(string message)
{ {
const string subject = "NzbDrone [TV] - Grabbed"; const string subject = "NzbDrone [TV] - Grabbed";
var body = String.Format("{0} sent to SABnzbd queue.", message); var body = String.Format("{0} sent to queue.", message);
_smtpProvider.SendEmail(Settings, subject, body); _emailService.SendEmail(Settings, subject, body);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
@ -30,11 +33,20 @@ namespace NzbDrone.Core.Notifications.Email
const string subject = "NzbDrone [TV] - Downloaded"; const string subject = "NzbDrone [TV] - Downloaded";
var body = String.Format("{0} Downloaded and sorted.", message.Message); var body = String.Format("{0} Downloaded and sorted.", message.Message);
_smtpProvider.SendEmail(Settings, subject, body); _emailService.SendEmail(Settings, subject, body);
} }
public override void AfterRename(Series series) public override void AfterRename(Series series)
{ {
} }
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_emailService.Test(Settings));
return new ValidationResult(failures);
}
} }
} }

@ -1,18 +1,18 @@
using System; using System;
using System.Net; using System.Net;
using System.Net.Mail; using System.Net.Mail;
using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Core.Messaging.Commands;
using Omu.ValueInjecter;
namespace NzbDrone.Core.Notifications.Email namespace NzbDrone.Core.Notifications.Email
{ {
public interface IEmailService public interface IEmailService
{ {
void SendEmail(EmailSettings settings, string subject, string body, bool htmlBody = false); void SendEmail(EmailSettings settings, string subject, string body, bool htmlBody = false);
ValidationFailure Test(EmailSettings settings);
} }
public class EmailService : IEmailService, IExecute<TestEmailCommand> public class EmailService : IEmailService
{ {
private readonly Logger _logger; private readonly Logger _logger;
@ -66,14 +66,21 @@ namespace NzbDrone.Core.Notifications.Email
} }
} }
public void Execute(TestEmailCommand message) public ValidationFailure Test(EmailSettings settings)
{ {
var settings = new EmailSettings();
settings.InjectFrom(message);
const string body = "Success! You have properly configured your email notification settings"; const string body = "Success! You have properly configured your email notification settings";
SendEmail(settings, "NzbDrone - Test Notification", body); try
{
SendEmail(settings, "NzbDrone - Test Notification", body);
}
catch (Exception ex)
{
_logger.ErrorException("Unable to send test email: " + ex.Message, ex);
return new ValidationFailure("Server", "Unable to send test email");
}
return null;
} }
} }
} }

@ -1,23 +0,0 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Notifications.Email
{
public class TestEmailCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public string Server { get; set; }
public int Port { get; set; }
public bool Ssl { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string From { get; set; }
public string To { get; set; }
}
}

@ -1,14 +1,17 @@
using NzbDrone.Core.Tv; using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Growl namespace NzbDrone.Core.Notifications.Growl
{ {
public class Growl : NotificationBase<GrowlSettings> public class Growl : NotificationBase<GrowlSettings>
{ {
private readonly IGrowlService _growlProvider; private readonly IGrowlService _growlService;
public Growl(IGrowlService growlProvider) public Growl(IGrowlService growlService)
{ {
_growlProvider = growlProvider; _growlService = growlService;
} }
public override string Link public override string Link
@ -20,18 +23,27 @@ namespace NzbDrone.Core.Notifications.Growl
{ {
const string title = "Episode Grabbed"; const string title = "Episode Grabbed";
_growlProvider.SendNotification(title, message, "GRAB", Settings.Host, Settings.Port, Settings.Password); _growlService.SendNotification(title, message, "GRAB", Settings.Host, Settings.Port, Settings.Password);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string title = "Episode Downloaded"; const string title = "Episode Downloaded";
_growlProvider.SendNotification(title, message.Message, "DOWNLOAD", Settings.Host, Settings.Port, Settings.Password); _growlService.SendNotification(title, message.Message, "DOWNLOAD", Settings.Host, Settings.Port, Settings.Password);
} }
public override void AfterRename(Series series) public override void AfterRename(Series series)
{ {
} }
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_growlService.Test(Settings));
return new ValidationResult(failures);
}
} }
} }

@ -2,10 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using FluentValidation.Results;
using Growl.Connector; using Growl.Connector;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Messaging.Commands;
using GrowlNotification = Growl.Connector.Notification; using GrowlNotification = Growl.Connector.Notification;
namespace NzbDrone.Core.Notifications.Growl namespace NzbDrone.Core.Notifications.Growl
@ -13,18 +13,20 @@ namespace NzbDrone.Core.Notifications.Growl
public interface IGrowlService public interface IGrowlService
{ {
void SendNotification(string title, string message, string notificationTypeName, string hostname, int port, string password); void SendNotification(string title, string message, string notificationTypeName, string hostname, int port, string password);
ValidationFailure Test(GrowlSettings settings);
} }
public class GrowlService : IGrowlService, IExecute<TestGrowlCommand> public class GrowlService : IGrowlService
{ {
private static readonly Logger Logger = NzbDroneLogger.GetLogger(); private readonly Logger _logger;
private readonly Application _growlApplication = new Application("NzbDrone"); private readonly Application _growlApplication = new Application("NzbDrone");
private GrowlConnector _growlConnector; private GrowlConnector _growlConnector;
private readonly List<NotificationType> _notificationTypes; private readonly List<NotificationType> _notificationTypes;
public GrowlService() public GrowlService(Logger logger)
{ {
_logger = logger;
_notificationTypes = GetNotificationTypes(); _notificationTypes = GetNotificationTypes();
_growlApplication.Icon = "https://raw.github.com/NzbDrone/NzbDrone/master/Logo/64.png"; _growlApplication.Icon = "https://raw.github.com/NzbDrone/NzbDrone/master/Logo/64.png";
} }
@ -36,13 +38,13 @@ namespace NzbDrone.Core.Notifications.Growl
_growlConnector = new GrowlConnector(password, hostname, port); _growlConnector = new GrowlConnector(password, hostname, port);
Logger.Debug("Sending Notification to: {0}:{1}", hostname, port); _logger.Debug("Sending Notification to: {0}:{1}", hostname, port);
_growlConnector.Notify(notification); _growlConnector.Notify(notification);
} }
private void Register(string host, int port, string password) private void Register(string host, int port, string password)
{ {
Logger.Debug("Registering NzbDrone with Growl host: {0}:{1}", host, port); _logger.Debug("Registering NzbDrone with Growl host: {0}:{1}", host, port);
_growlConnector = new GrowlConnector(password, host, port); _growlConnector = new GrowlConnector(password, host, port);
_growlConnector.Register(_growlApplication, _notificationTypes.ToArray()); _growlConnector.Register(_growlApplication, _notificationTypes.ToArray());
} }
@ -57,16 +59,26 @@ namespace NzbDrone.Core.Notifications.Growl
return notificationTypes; return notificationTypes;
} }
public void Execute(TestGrowlCommand message) public ValidationFailure Test(GrowlSettings settings)
{ {
Register(message.Host, message.Port, message.Password); try
{
Register(settings.Host, settings.Port, settings.Password);
const string title = "Test Notification"; const string title = "Test Notification";
const string body = "This is a test message from NzbDrone"; const string body = "This is a test message from NzbDrone";
Thread.Sleep(5000); Thread.Sleep(5000);
SendNotification(title, body, "TEST", message.Host, message.Port, message.Password); SendNotification(title, body, "TEST", settings.Host, settings.Port, settings.Password);
}
catch (Exception ex)
{
_logger.ErrorException("Unable to send test message: " + ex.Message, ex);
return new ValidationFailure("Host", "Unable to send test message");
}
return null;
} }
} }
} }

@ -1,18 +0,0 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Notifications.Growl
{
public class TestGrowlCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public string Host { get; set; }
public int Port { get; set; }
public string Password { get; set; }
}
}

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -24,6 +26,7 @@ namespace NzbDrone.Core.Notifications
} }
public ProviderDefinition Definition { get; set; } public ProviderDefinition Definition { get; set; }
public abstract ValidationResult Test();
public abstract string Link { get; } public abstract string Link { get; }

@ -8,5 +8,13 @@ namespace NzbDrone.Core.Notifications
public Boolean OnGrab { get; set; } public Boolean OnGrab { get; set; }
public Boolean OnDownload { get; set; } public Boolean OnDownload { get; set; }
public Boolean OnUpgrade { get; set; } public Boolean OnUpgrade { get; set; }
public override Boolean Enable
{
get
{
return OnGrab || OnDownload || OnUpgrade;
}
}
} }
} }

@ -1,14 +1,19 @@
using NzbDrone.Core.Tv; 
using System.Collections.Generic;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.NotifyMyAndroid namespace NzbDrone.Core.Notifications.NotifyMyAndroid
{ {
public class NotifyMyAndroid : NotificationBase<NotifyMyAndroidSettings> public class NotifyMyAndroid : NotificationBase<NotifyMyAndroidSettings>
{ {
private readonly INotifyMyAndroidProxy _notifyMyAndroidProxy; private readonly INotifyMyAndroidProxy _proxy;
public NotifyMyAndroid(INotifyMyAndroidProxy notifyMyAndroidProxy) public NotifyMyAndroid(INotifyMyAndroidProxy proxy)
{ {
_notifyMyAndroidProxy = notifyMyAndroidProxy; _proxy = proxy;
} }
public override string Link public override string Link
@ -20,18 +25,27 @@ namespace NzbDrone.Core.Notifications.NotifyMyAndroid
{ {
const string title = "Episode Grabbed"; const string title = "Episode Grabbed";
_notifyMyAndroidProxy.SendNotification(title, message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority); _proxy.SendNotification(title, message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string title = "Episode Downloaded"; const string title = "Episode Downloaded";
_notifyMyAndroidProxy.SendNotification(title, message.Message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority); _proxy.SendNotification(title, message.Message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority);
} }
public override void AfterRename(Series series) public override void AfterRename(Series series)
{ {
} }
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_proxy.Test(Settings));
return new ValidationResult(failures);
}
} }
} }

@ -2,8 +2,9 @@
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Xml.Linq; using System.Xml.Linq;
using FluentValidation.Results;
using NLog;
using NzbDrone.Core.Exceptions; using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Messaging.Commands;
using RestSharp; using RestSharp;
using NzbDrone.Core.Rest; using NzbDrone.Core.Rest;
@ -12,12 +13,19 @@ namespace NzbDrone.Core.Notifications.NotifyMyAndroid
public interface INotifyMyAndroidProxy public interface INotifyMyAndroidProxy
{ {
void SendNotification(string title, string message, string apiKye, NotifyMyAndroidPriority priority); void SendNotification(string title, string message, string apiKye, NotifyMyAndroidPriority priority);
ValidationFailure Test(NotifyMyAndroidSettings settings);
} }
public class NotifyMyAndroidProxy : INotifyMyAndroidProxy, IExecute<TestNotifyMyAndroidCommand> public class NotifyMyAndroidProxy : INotifyMyAndroidProxy
{ {
private readonly Logger _logger;
private const string URL = "https://www.notifymyandroid.com/publicapi"; private const string URL = "https://www.notifymyandroid.com/publicapi";
public NotifyMyAndroidProxy(Logger logger)
{
_logger = logger;
}
public void SendNotification(string title, string message, string apiKey, NotifyMyAndroidPriority priority) public void SendNotification(string title, string message, string apiKey, NotifyMyAndroidPriority priority)
{ {
var client = new RestClient(URL); var client = new RestClient(URL);
@ -56,12 +64,22 @@ namespace NzbDrone.Core.Notifications.NotifyMyAndroid
} }
} }
public void Execute(TestNotifyMyAndroidCommand message) public ValidationFailure Test(NotifyMyAndroidSettings settings)
{ {
const string title = "Test Notification"; try
const string body = "This is a test message from NzbDrone"; {
Verify(message.ApiKey); const string title = "Test Notification";
SendNotification(title, body, message.ApiKey, (NotifyMyAndroidPriority)message.Priority); const string body = "This is a test message from NzbDrone";
Verify(settings.ApiKey);
SendNotification(title, body, settings.ApiKey, (NotifyMyAndroidPriority)settings.Priority);
}
catch (Exception ex)
{
_logger.ErrorException("Unable to send test message: " + ex.Message, ex);
return new ValidationFailure("ApiKey", "Unable to send test message");
}
return null;
} }
} }
} }

@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications.NotifyMyAndroid
{ {
get get
{ {
return !String.IsNullOrWhiteSpace(ApiKey) && Priority != null & Priority >= -1 && Priority <= 2; return !String.IsNullOrWhiteSpace(ApiKey) && Priority >= -1 && Priority <= 2;
} }
} }

@ -1,18 +0,0 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Notifications.NotifyMyAndroid
{
public class TestNotifyMyAndroidCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public string ApiKey { get; set; }
public int Priority { get; set; }
}
}

@ -1,14 +1,17 @@
using NzbDrone.Core.Tv; using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Plex namespace NzbDrone.Core.Notifications.Plex
{ {
public class PlexClient : NotificationBase<PlexClientSettings> public class PlexClient : NotificationBase<PlexClientSettings>
{ {
private readonly IPlexService _plexProvider; private readonly IPlexService _plexService;
public PlexClient(IPlexService plexProvider) public PlexClient(IPlexService plexService)
{ {
_plexProvider = plexProvider; _plexService = plexService;
} }
public override string Link public override string Link
@ -19,17 +22,26 @@ namespace NzbDrone.Core.Notifications.Plex
public override void OnGrab(string message) public override void OnGrab(string message)
{ {
const string header = "NzbDrone [TV] - Grabbed"; const string header = "NzbDrone [TV] - Grabbed";
_plexProvider.Notify(Settings, header, message); _plexService.Notify(Settings, header, message);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string header = "NzbDrone [TV] - Downloaded"; const string header = "NzbDrone [TV] - Downloaded";
_plexProvider.Notify(Settings, header, message.Message); _plexService.Notify(Settings, header, message.Message);
} }
public override void AfterRename(Series series) public override void AfterRename(Series series)
{ {
} }
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_plexService.Test(Settings));
return new ValidationResult(failures);
}
} }
} }

@ -1,14 +1,17 @@
using NzbDrone.Core.Tv; using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Plex namespace NzbDrone.Core.Notifications.Plex
{ {
public class PlexServer : NotificationBase<PlexServerSettings> public class PlexServer : NotificationBase<PlexServerSettings>
{ {
private readonly IPlexService _plexProvider; private readonly IPlexService _plexService;
public PlexServer(IPlexService plexProvider) public PlexServer(IPlexService plexService)
{ {
_plexProvider = plexProvider; _plexService = plexService;
} }
public override string Link public override string Link
@ -34,8 +37,17 @@ namespace NzbDrone.Core.Notifications.Plex
{ {
if (Settings.UpdateLibrary) if (Settings.UpdateLibrary)
{ {
_plexProvider.UpdateLibrary(Settings); _plexService.UpdateLibrary(Settings);
} }
} }
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_plexService.Test(Settings));
return new ValidationResult(failures);
}
} }
} }

@ -1,12 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using FluentValidation.Results;
using System.Xml.Linq;
using NLog; using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Notifications.Plex namespace NzbDrone.Core.Notifications.Plex
{ {
@ -14,9 +11,11 @@ namespace NzbDrone.Core.Notifications.Plex
{ {
void Notify(PlexClientSettings settings, string header, string message); void Notify(PlexClientSettings settings, string header, string message);
void UpdateLibrary(PlexServerSettings settings); void UpdateLibrary(PlexServerSettings settings);
ValidationFailure Test(PlexClientSettings settings);
ValidationFailure Test(PlexServerSettings settings);
} }
public class PlexService : IPlexService, IExecute<TestPlexClientCommand>, IExecute<TestPlexServerCommand> public class PlexService : IPlexService
{ {
private readonly IHttpProvider _httpProvider; private readonly IHttpProvider _httpProvider;
private readonly IPlexServerProxy _plexServerProxy; private readonly IPlexServerProxy _plexServerProxy;
@ -84,31 +83,51 @@ namespace NzbDrone.Core.Notifications.Plex
return _httpProvider.DownloadString(url); return _httpProvider.DownloadString(url);
} }
public void Execute(TestPlexClientCommand message) public ValidationFailure Test(PlexClientSettings settings)
{ {
_logger.Debug("Sending Test Notifcation to Plex Client: {0}", message.Host); try
var command = String.Format("ExecBuiltIn(Notification({0}, {1}))", "Test Notification", "Success! Notifications are setup correctly");
var result = SendCommand(message.Host, message.Port, command, message.Username, message.Password);
if (String.IsNullOrWhiteSpace(result) ||
result.IndexOf("error", StringComparison.InvariantCultureIgnoreCase) > -1)
{ {
throw new Exception("Unable to connect to Plex Client"); _logger.Debug("Sending Test Notifcation to Plex Client: {0}", settings.Host);
var command = String.Format("ExecBuiltIn(Notification({0}, {1}))", "Test Notification", "Success! Notifications are setup correctly");
var result = SendCommand(settings.Host, settings.Port, command, settings.Username, settings.Password);
if (String.IsNullOrWhiteSpace(result) ||
result.IndexOf("error", StringComparison.InvariantCultureIgnoreCase) > -1)
{
throw new Exception("Unable to connect to Plex Client");
}
} }
catch (Exception ex)
{
_logger.ErrorException("Unable to send test message: " + ex.Message, ex);
return new ValidationFailure("Host", "Unable to send test message");
}
return null;
} }
public void Execute(TestPlexServerCommand message) public ValidationFailure Test(PlexServerSettings settings)
{ {
if (!GetSectionKeys(new PlexServerSettings try
{ {
Host = message.Host, if (!GetSectionKeys(new PlexServerSettings
Port = message.Port, {
Username = message.Username, Host = settings.Host,
Password = message.Password Port = settings.Port,
}).Any()) Username = settings.Username,
Password = settings.Password
}).Any())
{
throw new Exception("Unable to connect to Plex Server");
}
}
catch (Exception ex)
{ {
throw new Exception("Unable to connect to Plex Server"); _logger.ErrorException("Unable to connect to Plex Server: " + ex.Message, ex);
return new ValidationFailure("Host", "Unable to connect to Plex Server");
} }
return null;
} }
} }
} }

@ -1,19 +0,0 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Notifications.Plex
{
public class TestPlexClientCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public string Host { get; set; }
public int Port { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
}

@ -1,20 +0,0 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Notifications.Plex
{
public class TestPlexServerCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public string Host { get; set; }
public int Port { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
}

@ -1,15 +1,18 @@
using NzbDrone.Core.Tv; using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common;
using NzbDrone.Core.Tv;
using Prowlin; using Prowlin;
namespace NzbDrone.Core.Notifications.Prowl namespace NzbDrone.Core.Notifications.Prowl
{ {
public class Prowl : NotificationBase<ProwlSettings> public class Prowl : NotificationBase<ProwlSettings>
{ {
private readonly IProwlService _prowlProvider; private readonly IProwlService _prowlService;
public Prowl(IProwlService prowlProvider) public Prowl(IProwlService prowlService)
{ {
_prowlProvider = prowlProvider; _prowlService = prowlService;
} }
public override string Link public override string Link
@ -21,18 +24,27 @@ namespace NzbDrone.Core.Notifications.Prowl
{ {
const string title = "Episode Grabbed"; const string title = "Episode Grabbed";
_prowlProvider.SendNotification(title, message, Settings.ApiKey, (NotificationPriority)Settings.Priority); _prowlService.SendNotification(title, message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string title = "Episode Downloaded"; const string title = "Episode Downloaded";
_prowlProvider.SendNotification(title, message.Message, Settings.ApiKey, (NotificationPriority)Settings.Priority); _prowlService.SendNotification(title, message.Message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
} }
public override void AfterRename(Series series) public override void AfterRename(Series series)
{ {
} }
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_prowlService.Test(Settings));
return new ValidationResult(failures);
}
} }
} }

@ -1,6 +1,6 @@
using System; using System;
using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Core.Messaging.Commands;
using Prowlin; using Prowlin;
namespace NzbDrone.Core.Notifications.Prowl namespace NzbDrone.Core.Notifications.Prowl
@ -8,9 +8,10 @@ namespace NzbDrone.Core.Notifications.Prowl
public interface IProwlService public interface IProwlService
{ {
void SendNotification(string title, string message, string apiKey, NotificationPriority priority = NotificationPriority.Normal, string url = null); void SendNotification(string title, string message, string apiKey, NotificationPriority priority = NotificationPriority.Normal, string url = null);
ValidationFailure Test(ProwlSettings settings);
} }
public class ProwlService : IProwlService, IExecute<TestProwlCommand> public class ProwlService : IProwlService
{ {
private readonly Logger _logger; private readonly Logger _logger;
@ -80,14 +81,24 @@ namespace NzbDrone.Core.Notifications.Prowl
} }
} }
public void Execute(TestProwlCommand message) public ValidationFailure Test(ProwlSettings settings)
{ {
Verify(message.ApiKey); try
{
Verify(settings.ApiKey);
const string title = "Test Notification"; const string title = "Test Notification";
const string body = "This is a test message from NzbDrone"; const string body = "This is a test message from NzbDrone";
SendNotification(title, body, settings.ApiKey);
}
catch (Exception ex)
{
_logger.ErrorException("Unable to send test message: " + ex.Message, ex);
return new ValidationFailure("ApiKey", "Unable to send test message");
}
SendNotification(title, body, message.ApiKey); return null;
} }
} }
} }

@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications.Prowl
{ {
get get
{ {
return !string.IsNullOrWhiteSpace(ApiKey) && Priority != null & Priority >= -2 && Priority <= 2; return !string.IsNullOrWhiteSpace(ApiKey) && Priority >= -2 && Priority <= 2;
} }
} }

@ -1,17 +0,0 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Notifications.Prowl
{
public class TestProwlCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public string ApiKey { get; set; }
public int Priority { get; set; }
}
}

@ -1,14 +1,17 @@
using NzbDrone.Core.Tv; using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.PushBullet namespace NzbDrone.Core.Notifications.PushBullet
{ {
public class PushBullet : NotificationBase<PushBulletSettings> public class PushBullet : NotificationBase<PushBulletSettings>
{ {
private readonly IPushBulletProxy _pushBulletProxy; private readonly IPushBulletProxy _proxy;
public PushBullet(IPushBulletProxy pushBulletProxy) public PushBullet(IPushBulletProxy proxy)
{ {
_pushBulletProxy = pushBulletProxy; _proxy = proxy;
} }
public override string Link public override string Link
@ -20,18 +23,27 @@ namespace NzbDrone.Core.Notifications.PushBullet
{ {
const string title = "Episode Grabbed"; const string title = "Episode Grabbed";
_pushBulletProxy.SendNotification(title, message, Settings.ApiKey, Settings.DeviceId); _proxy.SendNotification(title, message, Settings.ApiKey, Settings.DeviceId);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string title = "Episode Downloaded"; const string title = "Episode Downloaded";
_pushBulletProxy.SendNotification(title, message.Message, Settings.ApiKey, Settings.DeviceId); _proxy.SendNotification(title, message.Message, Settings.ApiKey, Settings.DeviceId);
} }
public override void AfterRename(Series series) public override void AfterRename(Series series)
{ {
} }
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_proxy.Test(Settings));
return new ValidationResult(failures);
}
} }
} }

@ -1,5 +1,7 @@
using System; using System;
using NzbDrone.Core.Messaging.Commands; using System.Net;
using FluentValidation.Results;
using NLog;
using RestSharp; using RestSharp;
using NzbDrone.Core.Rest; using NzbDrone.Core.Rest;
@ -8,12 +10,19 @@ namespace NzbDrone.Core.Notifications.PushBullet
public interface IPushBulletProxy public interface IPushBulletProxy
{ {
void SendNotification(string title, string message, string apiKey, string deviceId); void SendNotification(string title, string message, string apiKey, string deviceId);
ValidationFailure Test(PushBulletSettings settings);
} }
public class PushBulletProxy : IPushBulletProxy, IExecute<TestPushBulletCommand> public class PushBulletProxy : IPushBulletProxy
{ {
private readonly Logger _logger;
private const string URL = "https://api.pushbullet.com/api/pushes"; private const string URL = "https://api.pushbullet.com/api/pushes";
public PushBulletProxy(Logger logger)
{
_logger = logger;
}
public void SendNotification(string title, string message, string apiKey, string deviceId) public void SendNotification(string title, string message, string apiKey, string deviceId)
{ {
var client = new RestClient(URL); var client = new RestClient(URL);
@ -45,12 +54,33 @@ namespace NzbDrone.Core.Notifications.PushBullet
return request; return request;
} }
public void Execute(TestPushBulletCommand message) public ValidationFailure Test(PushBulletSettings settings)
{ {
const string title = "Test Notification"; try
const string body = "This is a test message from NzbDrone"; {
const string title = "Test Notification";
const string body = "This is a test message from NzbDrone";
SendNotification(title, body, settings.ApiKey, settings.DeviceId);
}
catch (RestException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
{
_logger.ErrorException("API Key is invalid: " + ex.Message, ex);
return new ValidationFailure("ApiKey", "API Key is invalid");
}
_logger.ErrorException("Unable to send test message: " + ex.Message, ex);
return new ValidationFailure("ApiKey", "Unable to send test message");
}
catch (Exception ex)
{
_logger.ErrorException("Unable to send test message: " + ex.Message, ex);
return new ValidationFailure("", "Unable to send test message");
}
SendNotification(title, body, message.ApiKey, message.DeviceId); return null;
} }
} }
} }

@ -1,18 +0,0 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Notifications.PushBullet
{
public class TestPushBulletCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public string ApiKey { get; set; }
public string DeviceId { get; set; }
}
}

@ -1,14 +1,17 @@
using NzbDrone.Core.Tv; using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Pushover namespace NzbDrone.Core.Notifications.Pushover
{ {
public class Pushover : NotificationBase<PushoverSettings> public class Pushover : NotificationBase<PushoverSettings>
{ {
private readonly IPushoverProxy _pushoverProxy; private readonly IPushoverProxy _proxy;
public Pushover(IPushoverProxy pushoverProxy) public Pushover(IPushoverProxy proxy)
{ {
_pushoverProxy = pushoverProxy; _proxy = proxy;
} }
public override string Link public override string Link
@ -20,18 +23,27 @@ namespace NzbDrone.Core.Notifications.Pushover
{ {
const string title = "Episode Grabbed"; const string title = "Episode Grabbed";
_pushoverProxy.SendNotification(title, message, Settings.ApiKey, Settings.UserKey, (PushoverPriority)Settings.Priority, Settings.Sound); _proxy.SendNotification(title, message, Settings.ApiKey, Settings.UserKey, (PushoverPriority)Settings.Priority, Settings.Sound);
} }
public override void OnDownload(DownloadMessage message) public override void OnDownload(DownloadMessage message)
{ {
const string title = "Episode Downloaded"; const string title = "Episode Downloaded";
_pushoverProxy.SendNotification(title, message.Message, Settings.ApiKey, Settings.UserKey, (PushoverPriority)Settings.Priority, Settings.Sound); _proxy.SendNotification(title, message.Message, Settings.ApiKey, Settings.UserKey, (PushoverPriority)Settings.Priority, Settings.Sound);
} }
public override void AfterRename(Series series) public override void AfterRename(Series series)
{ {
} }
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_proxy.Test(Settings));
return new ValidationResult(failures);
}
} }
} }

@ -1,5 +1,7 @@
using NzbDrone.Common; using System;
using NzbDrone.Core.Messaging.Commands; using FluentValidation.Results;
using NLog;
using NzbDrone.Common;
using RestSharp; using RestSharp;
using NzbDrone.Core.Rest; using NzbDrone.Core.Rest;
@ -8,12 +10,19 @@ namespace NzbDrone.Core.Notifications.Pushover
public interface IPushoverProxy public interface IPushoverProxy
{ {
void SendNotification(string title, string message, string apiKey, string userKey, PushoverPriority priority, string sound); void SendNotification(string title, string message, string apiKey, string userKey, PushoverPriority priority, string sound);
ValidationFailure Test(PushoverSettings settings);
} }
public class PushoverProxy : IPushoverProxy, IExecute<TestPushoverCommand> public class PushoverProxy : IPushoverProxy
{ {
private readonly Logger _logger;
private const string URL = "https://api.pushover.net/1/messages.json"; private const string URL = "https://api.pushover.net/1/messages.json";
public PushoverProxy(Logger logger)
{
_logger = logger;
}
public void SendNotification(string title, string message, string apiKey, string userKey, PushoverPriority priority, string sound) public void SendNotification(string title, string message, string apiKey, string userKey, PushoverPriority priority, string sound)
{ {
var client = new RestClient(URL); var client = new RestClient(URL);
@ -30,12 +39,22 @@ namespace NzbDrone.Core.Notifications.Pushover
client.ExecuteAndValidate(request); client.ExecuteAndValidate(request);
} }
public void Execute(TestPushoverCommand message) public ValidationFailure Test(PushoverSettings settings)
{ {
const string title = "Test Notification"; try
const string body = "This is a test message from NzbDrone"; {
const string title = "Test Notification";
SendNotification(title, body, message.ApiKey, message.UserKey, (PushoverPriority)message.Priority, message.Sound); const string body = "This is a test message from NzbDrone";
SendNotification(title, body, settings.ApiKey, settings.UserKey, (PushoverPriority)settings.Priority, settings.Sound);
}
catch (Exception ex)
{
_logger.ErrorException("Unable to send test message: " + ex.Message, ex);
return new ValidationFailure("ApiKey", "Unable to send test message");
}
return null;
} }
} }
} }

@ -34,7 +34,7 @@ namespace NzbDrone.Core.Notifications.Pushover
{ {
get get
{ {
return !string.IsNullOrWhiteSpace(UserKey) && Priority != null & Priority >= -1 && Priority <= 2; return !string.IsNullOrWhiteSpace(UserKey) && Priority >= -1 && Priority <= 2;
} }
} }

@ -1,21 +0,0 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Notifications.Pushover
{
public class TestPushoverCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public string ApiKey { get; set; }
public string UserKey { get; set; }
public int Priority { get; set; }
public string Sound { get; set; }
}
}

@ -1,21 +0,0 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Notifications.Xbmc
{
public class TestXbmcCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
public string Host { get; set; }
public int Port { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public int DisplayTime { get; set; }
}
}

@ -1,15 +1,18 @@
using System.Linq; using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NzbDrone.Common;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Xbmc namespace NzbDrone.Core.Notifications.Xbmc
{ {
public class Xbmc : NotificationBase<XbmcSettings> public class Xbmc : NotificationBase<XbmcSettings>
{ {
private readonly IXbmcService _xbmcProvider; private readonly IXbmcService _xbmcService;
public Xbmc(IXbmcService xbmcProvider) public Xbmc(IXbmcService xbmcService)
{ {
_xbmcProvider = xbmcProvider; _xbmcService = xbmcService;
} }
public override string Link public override string Link
@ -23,7 +26,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
if (Settings.Notify) if (Settings.Notify)
{ {
_xbmcProvider.Notify(Settings, header, message); _xbmcService.Notify(Settings, header, message);
} }
} }
@ -33,7 +36,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
if (Settings.Notify) if (Settings.Notify)
{ {
_xbmcProvider.Notify(Settings, header, message.Message); _xbmcService.Notify(Settings, header, message.Message);
} }
UpdateAndClean(message.Series, message.OldFiles.Any()); UpdateAndClean(message.Series, message.OldFiles.Any());
@ -44,16 +47,25 @@ namespace NzbDrone.Core.Notifications.Xbmc
UpdateAndClean(series); UpdateAndClean(series);
} }
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_xbmcService.Test(Settings));
return new ValidationResult(failures);
}
private void UpdateAndClean(Series series, bool clean = true) private void UpdateAndClean(Series series, bool clean = true)
{ {
if (Settings.UpdateLibrary) if (Settings.UpdateLibrary)
{ {
_xbmcProvider.Update(Settings, series); _xbmcService.Update(Settings, series);
} }
if (clean && Settings.CleanLibrary) if (clean && Settings.CleanLibrary)
{ {
_xbmcProvider.Clean(Settings); _xbmcService.Clean(Settings);
} }
} }
} }

@ -1,13 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation.Results;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog; using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Notifications.Xbmc.Model; using NzbDrone.Core.Notifications.Xbmc.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -19,18 +18,20 @@ namespace NzbDrone.Core.Notifications.Xbmc
void Update(XbmcSettings settings, Series series); void Update(XbmcSettings settings, Series series);
void Clean(XbmcSettings settings); void Clean(XbmcSettings settings);
XbmcVersion GetJsonVersion(XbmcSettings settings); XbmcVersion GetJsonVersion(XbmcSettings settings);
ValidationFailure Test(XbmcSettings settings);
} }
public class XbmcService : IXbmcService, IExecute<TestXbmcCommand> public class XbmcService : IXbmcService
{ {
private static readonly Logger Logger = NzbDroneLogger.GetLogger();
private readonly IHttpProvider _httpProvider; private readonly IHttpProvider _httpProvider;
private readonly IEnumerable<IApiProvider> _apiProviders; private readonly IEnumerable<IApiProvider> _apiProviders;
private readonly Logger _logger;
public XbmcService(IHttpProvider httpProvider, IEnumerable<IApiProvider> apiProviders) public XbmcService(IHttpProvider httpProvider, IEnumerable<IApiProvider> apiProviders, Logger logger)
{ {
_httpProvider = httpProvider; _httpProvider = httpProvider;
_apiProviders = apiProviders; _apiProviders = apiProviders;
_logger = logger;
} }
public void Notify(XbmcSettings settings, string title, string message) public void Notify(XbmcSettings settings, string title, string message)
@ -62,7 +63,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
var response = _httpProvider.PostCommand(settings.Address, settings.Username, settings.Password, postJson.ToString()); var response = _httpProvider.PostCommand(settings.Address, settings.Username, settings.Password, postJson.ToString());
Logger.Debug("Getting version from response: " + response); _logger.Debug("Getting version from response: " + response);
var result = Json.Deserialize<XbmcJsonResult<JObject>>(response); var result = Json.Deserialize<XbmcJsonResult<JObject>>(response);
var versionObject = result.Result.Property("version"); var versionObject = result.Result.Property("version");
@ -78,7 +79,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
catch (Exception ex) catch (Exception ex)
{ {
Logger.DebugException(ex.Message, ex); _logger.DebugException(ex.Message, ex);
} }
return new XbmcVersion(); return new XbmcVersion();
@ -98,27 +99,28 @@ namespace NzbDrone.Core.Notifications.Xbmc
return apiProvider; return apiProvider;
} }
public void Execute(TestXbmcCommand message) public ValidationFailure Test(XbmcSettings settings)
{ {
var settings = new XbmcSettings try
{ {
Host = message.Host, _logger.Debug("Determining version of XBMC Host: {0}", settings.Address);
Port = message.Port, var version = GetJsonVersion(settings);
Username = message.Username, _logger.Debug("Version is: {0}", version);
Password = message.Password,
DisplayTime = message.DisplayTime
};
Logger.Debug("Determining version of XBMC Host: {0}", settings.Address);
var version = GetJsonVersion(settings);
Logger.Debug("Version is: {0}", version);
if (version == new XbmcVersion(0)) if (version == new XbmcVersion(0))
{
throw new InvalidXbmcVersionException("Verion received from XBMC is invalid, please correct your settings.");
}
Notify(settings, "Test Notification", "Success! XBMC has been successfully configured!");
}
catch (Exception ex)
{ {
throw new InvalidXbmcVersionException("Verion received from XBMC is invalid, please correct your settings."); _logger.ErrorException("Unable to send test message: " + ex.Message, ex);
return new ValidationFailure("Host", "Unable to send test message");
} }
Notify(settings, "Test Notification", "Success! XBMC has been successfully configured!"); return null;
} }
} }
} }

@ -253,21 +253,17 @@
<Compile Include="Download\Clients\Nzbget\NzbgetPostQueueItem.cs" /> <Compile Include="Download\Clients\Nzbget\NzbgetPostQueueItem.cs" />
<Compile Include="Download\Clients\Sabnzbd\SabnzbdDownloadStatus.cs" /> <Compile Include="Download\Clients\Sabnzbd\SabnzbdDownloadStatus.cs" />
<Compile Include="Download\Clients\UsenetBlackhole\UsenetBlackhole.cs" /> <Compile Include="Download\Clients\UsenetBlackhole\UsenetBlackhole.cs" />
<Compile Include="Download\Clients\UsenetBlackhole\TestUsenetBlackholeCommand.cs" />
<Compile Include="Download\Clients\UsenetBlackhole\UsenetBlackholeSettings.cs" /> <Compile Include="Download\Clients\UsenetBlackhole\UsenetBlackholeSettings.cs" />
<Compile Include="Download\Clients\DownloadClientException.cs" /> <Compile Include="Download\Clients\DownloadClientException.cs" />
<Compile Include="Download\Clients\Pneumatic\PneumaticSettings.cs" /> <Compile Include="Download\Clients\Pneumatic\PneumaticSettings.cs" />
<Compile Include="Download\Clients\Nzbget\NzbgetHistoryItem.cs" /> <Compile Include="Download\Clients\Nzbget\NzbgetHistoryItem.cs" />
<Compile Include="Download\Clients\Nzbget\NzbgetParameter.cs" /> <Compile Include="Download\Clients\Nzbget\NzbgetParameter.cs" />
<Compile Include="Download\Clients\Nzbget\NzbgetSettings.cs" /> <Compile Include="Download\Clients\Nzbget\NzbgetSettings.cs" />
<Compile Include="Download\Clients\Nzbget\TestNzbgetCommand.cs" />
<Compile Include="Download\Clients\Pneumatic\Pneumatic.cs" /> <Compile Include="Download\Clients\Pneumatic\Pneumatic.cs" />
<Compile Include="Download\Clients\Pneumatic\TestPneumaticCommand.cs" />
<Compile Include="Download\Clients\Sabnzbd\Responses\SabnzbdAddResponse.cs" /> <Compile Include="Download\Clients\Sabnzbd\Responses\SabnzbdAddResponse.cs" />
<Compile Include="Download\Clients\Sabnzbd\Responses\SabnzbdCategoryResponse.cs" /> <Compile Include="Download\Clients\Sabnzbd\Responses\SabnzbdCategoryResponse.cs" />
<Compile Include="Download\Clients\Sabnzbd\Responses\SabnzbdVersionResponse.cs" /> <Compile Include="Download\Clients\Sabnzbd\Responses\SabnzbdVersionResponse.cs" />
<Compile Include="Download\Clients\Sabnzbd\SabnzbdSettings.cs" /> <Compile Include="Download\Clients\Sabnzbd\SabnzbdSettings.cs" />
<Compile Include="Download\Clients\Sabnzbd\TestSabnzbdCommand.cs" />
<Compile Include="Download\CompletedDownloadService.cs" /> <Compile Include="Download\CompletedDownloadService.cs" />
<Compile Include="Download\DownloadClientBase.cs" /> <Compile Include="Download\DownloadClientBase.cs" />
<Compile Include="Download\DownloadClientDefinition.cs" /> <Compile Include="Download\DownloadClientDefinition.cs" />
@ -419,12 +415,10 @@
<Compile Include="Notifications\PushBullet\PushBullet.cs" /> <Compile Include="Notifications\PushBullet\PushBullet.cs" />
<Compile Include="Notifications\PushBullet\PushBulletProxy.cs" /> <Compile Include="Notifications\PushBullet\PushBulletProxy.cs" />
<Compile Include="Notifications\PushBullet\PushBulletSettings.cs" /> <Compile Include="Notifications\PushBullet\PushBulletSettings.cs" />
<Compile Include="Notifications\PushBullet\TestPushBulletCommand.cs" />
<Compile Include="Notifications\NotifyMyAndroid\NotifyMyAndroid.cs" /> <Compile Include="Notifications\NotifyMyAndroid\NotifyMyAndroid.cs" />
<Compile Include="Notifications\NotifyMyAndroid\NotifyMyAndroidPriority.cs" /> <Compile Include="Notifications\NotifyMyAndroid\NotifyMyAndroidPriority.cs" />
<Compile Include="Notifications\NotifyMyAndroid\NotifyMyAndroidProxy.cs" /> <Compile Include="Notifications\NotifyMyAndroid\NotifyMyAndroidProxy.cs" />
<Compile Include="Notifications\NotifyMyAndroid\NotifyMyAndroidSettings.cs" /> <Compile Include="Notifications\NotifyMyAndroid\NotifyMyAndroidSettings.cs" />
<Compile Include="Notifications\NotifyMyAndroid\TestNotifyMyAndroidCommand.cs" />
<Compile Include="MediaFiles\Commands\BackendCommandAttribute.cs" /> <Compile Include="MediaFiles\Commands\BackendCommandAttribute.cs" />
<Compile Include="Messaging\Commands\BackendCommandAttribute.cs" /> <Compile Include="Messaging\Commands\BackendCommandAttribute.cs" />
<Compile Include="Messaging\Commands\Command.cs" /> <Compile Include="Messaging\Commands\Command.cs" />
@ -480,9 +474,7 @@
<Compile Include="MediaFiles\RenameEpisodeFileService.cs" /> <Compile Include="MediaFiles\RenameEpisodeFileService.cs" />
<Compile Include="MediaFiles\SameFilenameException.cs" /> <Compile Include="MediaFiles\SameFilenameException.cs" />
<Compile Include="MediaFiles\UpgradeMediaFileService.cs" /> <Compile Include="MediaFiles\UpgradeMediaFileService.cs" />
<Compile Include="Notifications\Email\TestEmailCommand.cs" />
<Compile Include="Notifications\Growl\GrowlSettings.cs" /> <Compile Include="Notifications\Growl\GrowlSettings.cs" />
<Compile Include="Notifications\Growl\TestGrowlCommand.cs" />
<Compile Include="Notifications\INotification.cs" /> <Compile Include="Notifications\INotification.cs" />
<Compile Include="Notifications\NotificationRepository.cs" /> <Compile Include="Notifications\NotificationRepository.cs" />
<Compile Include="Fluent.cs" /> <Compile Include="Fluent.cs" />
@ -523,27 +515,22 @@
<Compile Include="MetadataSource\Trakt\Images.cs" /> <Compile Include="MetadataSource\Trakt\Images.cs" />
<Compile Include="MetadataSource\Trakt\Season.cs" /> <Compile Include="MetadataSource\Trakt\Season.cs" />
<Compile Include="MetadataSource\Trakt\FullShow.cs" /> <Compile Include="MetadataSource\Trakt\FullShow.cs" />
<Compile Include="Notifications\Plex\TestPlexServerCommand.cs" />
<Compile Include="Notifications\Plex\PlexServer.cs" /> <Compile Include="Notifications\Plex\PlexServer.cs" />
<Compile Include="Notifications\Plex\PlexClientSettings.cs" /> <Compile Include="Notifications\Plex\PlexClientSettings.cs" />
<Compile Include="Notifications\Plex\PlexServerSettings.cs" /> <Compile Include="Notifications\Plex\PlexServerSettings.cs" />
<Compile Include="Notifications\Plex\TestPlexClientCommand.cs" />
<Compile Include="Notifications\Prowl\InvalidApiKeyException.cs" /> <Compile Include="Notifications\Prowl\InvalidApiKeyException.cs" />
<Compile Include="Notifications\Prowl\ProwlPriority.cs" /> <Compile Include="Notifications\Prowl\ProwlPriority.cs" />
<Compile Include="Notifications\Prowl\ProwlSettings.cs" /> <Compile Include="Notifications\Prowl\ProwlSettings.cs" />
<Compile Include="Notifications\Email\EmailSettings.cs" /> <Compile Include="Notifications\Email\EmailSettings.cs" />
<Compile Include="Notifications\Prowl\TestProwlCommand.cs" />
<Compile Include="Notifications\Pushover\InvalidResponseException.cs" /> <Compile Include="Notifications\Pushover\InvalidResponseException.cs" />
<Compile Include="Notifications\Pushover\Pushover.cs" /> <Compile Include="Notifications\Pushover\Pushover.cs" />
<Compile Include="Notifications\Pushover\PushoverPriority.cs" /> <Compile Include="Notifications\Pushover\PushoverPriority.cs" />
<Compile Include="Notifications\Pushover\PushoverService.cs" /> <Compile Include="Notifications\Pushover\PushoverService.cs" />
<Compile Include="Notifications\Pushover\PushoverSettings.cs" /> <Compile Include="Notifications\Pushover\PushoverSettings.cs" />
<Compile Include="Notifications\Pushover\TestPushoverCommand.cs" />
<Compile Include="Notifications\Xbmc\HttpApiProvider.cs" /> <Compile Include="Notifications\Xbmc\HttpApiProvider.cs" />
<Compile Include="Notifications\Xbmc\IApiProvider.cs" /> <Compile Include="Notifications\Xbmc\IApiProvider.cs" />
<Compile Include="Notifications\Xbmc\InvalidXbmcVersionException.cs" /> <Compile Include="Notifications\Xbmc\InvalidXbmcVersionException.cs" />
<Compile Include="Notifications\Xbmc\JsonApiProvider.cs" /> <Compile Include="Notifications\Xbmc\JsonApiProvider.cs" />
<Compile Include="Notifications\Xbmc\TestXbmcCommand.cs" />
<Compile Include="Notifications\Xbmc\XbmcSettings.cs" /> <Compile Include="Notifications\Xbmc\XbmcSettings.cs" />
<Compile Include="Organizer\EpisodeSortingType.cs" /> <Compile Include="Organizer\EpisodeSortingType.cs" />
<Compile Include="Organizer\FileNameBuilder.cs" /> <Compile Include="Organizer\FileNameBuilder.cs" />

@ -1,6 +1,6 @@
 using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation.Results;
namespace NzbDrone.Core.ThingiProvider namespace NzbDrone.Core.ThingiProvider
{ {
@ -10,5 +10,6 @@ namespace NzbDrone.Core.ThingiProvider
IEnumerable<ProviderDefinition> DefaultDefinitions { get; } IEnumerable<ProviderDefinition> DefaultDefinitions { get; }
ProviderDefinition Definition { get; set; } ProviderDefinition Definition { get; set; }
ValidationResult Test();
} }
} }

@ -1,5 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic; using FluentValidation.Results;
namespace NzbDrone.Core.ThingiProvider namespace NzbDrone.Core.ThingiProvider
{ {
@ -10,10 +10,11 @@ namespace NzbDrone.Core.ThingiProvider
List<TProviderDefinition> All(); List<TProviderDefinition> All();
List<TProvider> GetAvailableProviders(); List<TProvider> GetAvailableProviders();
TProviderDefinition Get(int id); TProviderDefinition Get(int id);
TProviderDefinition Create(TProviderDefinition indexer); TProviderDefinition Create(TProviderDefinition definition);
void Update(TProviderDefinition indexer); void Update(TProviderDefinition definition);
void Delete(int id); void Delete(int id);
IEnumerable<TProviderDefinition> GetDefaultDefinitions(); IEnumerable<TProviderDefinition> GetDefaultDefinitions();
IEnumerable<TProviderDefinition> GetPresetDefinitions(TProviderDefinition providerDefinition); IEnumerable<TProviderDefinition> GetPresetDefinitions(TProviderDefinition providerDefinition);
ValidationResult Test(TProviderDefinition definition);
} }
} }

@ -1,14 +1,16 @@
using NzbDrone.Core.Datastore; using System;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.ThingiProvider namespace NzbDrone.Core.ThingiProvider
{ {
public abstract class ProviderDefinition : ModelBase public abstract class ProviderDefinition : ModelBase
{ {
private IProviderConfig _settings; private IProviderConfig _settings;
public string Name { get; set; }
public string Implementation { get; set; }
public string ConfigContract { get; set; } public String Name { get; set; }
public String Implementation { get; set; }
public String ConfigContract { get; set; }
public virtual Boolean Enable { get; set; }
public IProviderConfig Settings public IProviderConfig Settings
{ {
@ -26,4 +28,4 @@ namespace NzbDrone.Core.ThingiProvider
} }
} }
} }
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation.Results;
using NLog; using NLog;
using NzbDrone.Common.Composition; using NzbDrone.Common.Composition;
using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Lifecycle;
@ -75,6 +76,11 @@ namespace NzbDrone.Core.ThingiProvider
return definitions; return definitions;
} }
public ValidationResult Test(TProviderDefinition definition)
{
return GetInstance(definition).Test();
}
public List<TProvider> GetAvailableProviders() public List<TProvider> GetAvailableProviders()
{ {
return Active().Select(GetInstance).ToList(); return Active().Select(GetInstance).ToList();

@ -1,9 +1,9 @@
'use strict'; 'use strict';
define([ define([
'backbone.deepmodel' 'Settings/ProviderSettingsModelBase'
], function (DeepModel) { ], function (ProviderSettingsModelBase) {
return DeepModel.DeepModel.extend({ return ProviderSettingsModelBase.extend({
}); });
}); });

@ -78,14 +78,7 @@ define([
}, },
_test: function () { _test: function () {
var testCommand = 'test{0}'.format(this.model.get('implementation')); this.model.test();
var properties = {};
_.each(this.model.get('fields'), function (field) {
properties[field.name] = field.value;
});
CommandController.Execute(testCommand, properties);
} }
}); });

@ -82,14 +82,7 @@ define([
}, },
_test: function () { _test: function () {
var testCommand = 'test{0}'.format(this.model.get('implementation')); this.model.test();
var properties = {};
_.each(this.model.get('fields'), function (field) {
properties[field.name] = field.value;
});
CommandController.Execute(testCommand, properties);
} }
}); });

@ -46,8 +46,7 @@
<button class="btn pull-left x-back">back</button> <button class="btn pull-left x-back">back</button>
{{/if}} {{/if}}
<!-- Testing is currently not yet supported for indexers, but leaving the infrastructure for later --> <button class="btn x-test">test <i class="x-test-icon icon-nd-test"/></button>
<!-- <button class="btn x-test">test <i class="x-test-icon icon-nd-test"/></button> -->
<button class="btn x-close">cancel</button> <button class="btn x-close">cancel</button>
<div class="btn-group"> <div class="btn-group">

@ -1,9 +1,9 @@
'use strict'; 'use strict';
define([ define([
'backbone.deepmodel' 'Settings/ProviderSettingsModelBase'
], function (DeepModel) { ], function (ProviderSettingsModelBase) {
return DeepModel.DeepModel.extend({ return ProviderSettingsModelBase.extend({
}); });
}); });

@ -1,10 +1,10 @@
'use strict'; 'use strict';
define(
[
'backbone.deepmodel'
], function (DeepModel) {
return DeepModel.DeepModel.extend({
}); define([
'Settings/ProviderSettingsModelBase'
], function (ProviderSettingsModelBase) {
return ProviderSettingsModelBase.extend({
}); });
});

@ -83,14 +83,7 @@ define([
}, },
_test: function () { _test: function () {
var testCommand = 'test{0}'.format(this.model.get('implementation')); this.model.test();
var properties = {};
_.each(this.model.get('fields'), function (field) {
properties[field.name] = field.value;
});
CommandController.Execute(testCommand, properties);
}, },
_onDownloadChanged: function () { _onDownloadChanged: function () {

@ -1,10 +1,9 @@
'use strict'; 'use strict';
define([ define([
'Settings/SettingsModelBase' 'Settings/ProviderSettingsModelBase'
], function (ModelBase) { ], function (ProviderSettingsModelBase) {
return ModelBase.extend({ return ProviderSettingsModelBase.extend({
successMessage: 'Notification Saved',
errorMessage : 'Couldn\'t save notification'
}); });
}); });

@ -0,0 +1,36 @@
'use strict';
define([
'jquery',
'backbone.deepmodel',
'Shared/Messenger'
], function ($, DeepModel, Messenger) {
return DeepModel.DeepModel.extend({
test: function () {
var self = this;
this.trigger('validation:sync');
var params = {};
params.url = this.collection.url + '/test';
params.contentType = 'application/json';
params.data = JSON.stringify(this.toJSON());
params.type = 'POST';
params.isValidatedCall = true;
var promise = $.ajax(params);
Messenger.monitor({
promise : promise,
successMessage : 'Testing \'{0}\' completed'.format(this.get('name')),
errorMessage : 'Testing \'{0}\' failed'.format(this.get('name'))
});
promise.fail(function (response) {
self.trigger('validation:failed', response);
});
}
});
});

@ -57,13 +57,17 @@ define(
}; };
$.fn.addFormError = function (error) { $.fn.addFormError = function (error) {
var t1 = this.find('.form-horizontal'); if (this.find('.modal-body')) {
var t2 = this.find('.form-horizontal').parent(); this.find('.modal-body').prepend('<div class="alert alert-danger validation-error">' + error.errorMessage + '</div>');
}
this.prepend('<div class="alert alert-danger validation-error">' + error.errorMessage + '</div>');
else {
this.prepend('<div class="alert alert-danger validation-error">' + error.errorMessage + '</div>');
}
}; };
$.fn.removeAllErrors = function () { $.fn.removeAllErrors = function () {
this.find('.has-error').removeClass('has-error');
this.find('.error').removeClass('error'); this.find('.error').removeClass('error');
this.find('.validation-errors').removeClass('alert').removeClass('alert-danger').html(''); this.find('.validation-errors').removeClass('alert').removeClass('alert-danger').html('');
this.find('.validation-error').remove(); this.find('.validation-error').remove();

Loading…
Cancel
Save