new: smarter validation for newznab indexer settings

This commit is contained in:
kayone 2013-12-01 13:33:53 -08:00
parent 53cebdee17
commit be3ec7ddb8
8 changed files with 98 additions and 7 deletions

View File

@ -1 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" /> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/Environment/ExternalSources/Decompiler/DecompileMethodBodies/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -0,0 +1,41 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
{
public class NewznabSettingFixture : CoreTest
{
[TestCase("http://nzbs.org")]
[TestCase("http:///www.nzbplanet.net")]
public void requires_apikey(string url)
{
var setting = new NewznabSettings()
{
ApiKey = "",
Url = url
};
setting.Validate().IsValid.Should().BeFalse();
setting.Validate().Errors.Should().Contain(c => c.PropertyName == "ApiKey");
}
[TestCase("http://nzbs2.org")]
public void doesnt_requires_apikey(string url)
{
var setting = new NewznabSettings()
{
ApiKey = "",
Url = url
};
setting.Validate().IsValid.Should().BeTrue();
}
}
}

View File

@ -138,6 +138,7 @@
<Compile Include="IndexerTests\BasicRssParserFixture.cs" /> <Compile Include="IndexerTests\BasicRssParserFixture.cs" />
<Compile Include="IndexerTests\IndexerServiceFixture.cs" /> <Compile Include="IndexerTests\IndexerServiceFixture.cs" />
<Compile Include="IndexerTests\IntegrationTests\IndexerIntegrationTests.cs" /> <Compile Include="IndexerTests\IntegrationTests\IndexerIntegrationTests.cs" />
<Compile Include="IndexerTests\NewznabTests\NewznabSettingFixture.cs" />
<Compile Include="IndexerTests\XElementExtensionsFixture.cs" /> <Compile Include="IndexerTests\XElementExtensionsFixture.cs" />
<Compile Include="JobTests\JobRepositoryFixture.cs" /> <Compile Include="JobTests\JobRepositoryFixture.cs" />
<Compile Include="DecisionEngineTests\LanguageSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\LanguageSpecificationFixture.cs" />

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -7,6 +8,12 @@ namespace NzbDrone.Core.Indexers.Newznab
{ {
public class NewznabParser : RssParserBase public class NewznabParser : RssParserBase
{ {
private static readonly string[] IgnoredErrors =
{
"Request limit reached",
};
protected override string GetNzbInfoUrl(XElement item) protected override string GetNzbInfoUrl(XElement item)
{ {
return item.Comments().Replace("#comments", ""); return item.Comments().Replace("#comments", "");
@ -25,6 +32,23 @@ namespace NzbDrone.Core.Indexers.Newznab
return ParseSize(item.Description()); return ParseSize(item.Description());
} }
public override IEnumerable<ReleaseInfo> Process(string xml, string url)
{
try
{
return base.Process(xml, url);
}
catch (NewznabException e)
{
if (!IgnoredErrors.Any(ignoredError => e.Message.Contains(ignoredError)))
{
throw;
}
_logger.Error(e.Message);
return new List<ReleaseInfo>();
}
}
protected override ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult) protected override ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult)
{ {
if (currentResult != null) if (currentResult != null)

View File

@ -15,10 +15,17 @@ namespace NzbDrone.Core.Indexers.Newznab
if (error == null) return; if (error == null) return;
var code = Convert.ToInt32(error.Attribute("code").Value); var code = Convert.ToInt32(error.Attribute("code").Value);
var errorMessage = error.Attribute("description").Value;
if (code >= 100 && code <= 199) throw new ApiKeyException("Invalid API key: {0}");
throw new NewznabException("Newznab error detected: {0}", error.Attribute("description").Value); if (code >= 100 && code <= 199) throw new ApiKeyException("Invalid API key");
if (!url.Contains("apikey=") && errorMessage == "Missing parameter")
{
throw new ApiKeyException("Indexer requires and API key");
}
throw new NewznabException("Newznab error detected: {0}", errorMessage);
} }
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FluentValidation; using FluentValidation;
using FluentValidation.Results; using FluentValidation.Results;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
@ -10,9 +11,25 @@ namespace NzbDrone.Core.Indexers.Newznab
{ {
public class NewznabSettingsValidator : AbstractValidator<NewznabSettings> public class NewznabSettingsValidator : AbstractValidator<NewznabSettings>
{ {
private static readonly string[] ApiKeyWhiteList =
{
"nzbs.org",
"nzb.su",
"dognzb.cr",
"nzbplanet.net",
"nzbid.org",
"nzbndx.com",
};
private static bool ShouldHaveApiKey(NewznabSettings settings)
{
return ApiKeyWhiteList.Any(c => settings.Url.ToLowerInvariant().Contains(c));
}
public NewznabSettingsValidator() public NewznabSettingsValidator()
{ {
RuleFor(c => c.Url).ValidRootUrl(); RuleFor(c => c.Url).ValidRootUrl();
RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey);
} }
} }

View File

@ -41,14 +41,14 @@ namespace NzbDrone.Core.Indexers
NewznabPreProcessor.Process(xml, url); NewznabPreProcessor.Process(xml, url);
} }
catch (ApiKeyException apiKeyException) catch (ApiKeyException)
{ {
_logger.Warn("Indexer returned result for Newznab RSS URL, API Key appears to be invalid"); _logger.Warn("Indexer returned result for Newznab RSS URL, API Key appears to be invalid");
var apiKeyFailure = new ValidationFailure("ApiKey", "Invalid API Key"); var apiKeyFailure = new ValidationFailure("ApiKey", "Invalid API Key");
throw new ValidationException(new List<ValidationFailure> { apiKeyFailure }.ToArray()); throw new ValidationException(new List<ValidationFailure> { apiKeyFailure }.ToArray());
} }
catch (Exception ex) catch (Exception)
{ {
_logger.Warn("Indexer doesn't appear to be Newznab based"); _logger.Warn("Indexer doesn't appear to be Newznab based");

View File

@ -15,7 +15,7 @@ namespace NzbDrone.Core.Indexers
{ {
public abstract class RssParserBase : IParseFeed public abstract class RssParserBase : IParseFeed
{ {
private readonly Logger _logger; protected readonly Logger _logger;
protected virtual ReleaseInfo CreateNewReleaseInfo() protected virtual ReleaseInfo CreateNewReleaseInfo()
{ {
@ -27,7 +27,7 @@ namespace NzbDrone.Core.Indexers
_logger = NzbDroneLogger.GetLogger(this); _logger = NzbDroneLogger.GetLogger(this);
} }
public IEnumerable<ReleaseInfo> Process(string xml, string url) public virtual IEnumerable<ReleaseInfo> Process(string xml, string url)
{ {
PreProcess(xml, url); PreProcess(xml, url);