Implement equality checks for providers

pull/2006/head
Bogdan 1 month ago
parent 3492b75287
commit 87ed43b071

@ -3,7 +3,6 @@ using FluentAssertions;
using FluentValidation.Results; using FluentValidation.Results;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Notifications; using NzbDrone.Core.Notifications;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
@ -12,9 +11,9 @@ namespace NzbDrone.Core.Test.NotificationTests
[TestFixture] [TestFixture]
public class NotificationBaseFixture : TestBase public class NotificationBaseFixture : TestBase
{ {
private class TestSetting : IProviderConfig private class TestSetting : NotificationBaseSettings
{ {
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(); return new NzbDroneValidationResult();
} }

@ -5,6 +5,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using AngleSharp.Html.Parser; using AngleSharp.Html.Parser;
using Equ;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
@ -210,7 +211,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
} }
public class BinSearchSettings : IIndexerSettings public class BinSearchSettings : MemberwiseEquatable<BinSearchSettings>, IIndexerSettings
{ {
[FieldDefinition(1, Label = "Base Url", Type = FieldType.Select, SelectOptionsProviderAction = "getUrls", HelpText = "Select which baseurl Prowlarr will use for requests to the site")] [FieldDefinition(1, Label = "Base Url", Type = FieldType.Select, SelectOptionsProviderAction = "getUrls", HelpText = "Select which baseurl Prowlarr will use for requests to the site")]
public string BaseUrl { get; set; } public string BaseUrl { get; set; }

@ -1,3 +1,4 @@
using Equ;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
@ -14,7 +15,7 @@ namespace NzbDrone.Core.Indexers.Headphones
} }
} }
public class HeadphonesSettings : IIndexerSettings public class HeadphonesSettings : MemberwiseEquatable<HeadphonesSettings>, IIndexerSettings
{ {
private static readonly HeadphonesSettingsValidator Validator = new (); private static readonly HeadphonesSettingsValidator Validator = new ();

@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Equ;
using FluentValidation; using FluentValidation;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
@ -46,7 +47,7 @@ namespace NzbDrone.Core.Indexers.Newznab
} }
} }
public class NewznabSettings : IIndexerSettings public class NewznabSettings : MemberwiseEquatable<NewznabSettings>, IIndexerSettings
{ {
private static readonly NewznabSettingsValidator Validator = new (); private static readonly NewznabSettingsValidator Validator = new ();
@ -75,6 +76,7 @@ namespace NzbDrone.Core.Indexers.Newznab
[FieldDefinition(7)] [FieldDefinition(7)]
public IndexerBaseSettings BaseSettings { get; set; } = new (); public IndexerBaseSettings BaseSettings { get; set; } = new ();
[MemberwiseEqualityIgnore]
public NewznabCapabilitiesSettings Capabilities { get; set; } public NewznabCapabilitiesSettings Capabilities { get; set; }
// Field 8 is used by TorznabSettings MinimumSeeders // Field 8 is used by TorznabSettings MinimumSeeders

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Equ;
using FluentValidation; using FluentValidation;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog; using NLog;
@ -1178,7 +1179,7 @@ namespace NzbDrone.Core.Indexers.Definitions
} }
} }
public class NzbIndexSettings : IIndexerSettings public class NzbIndexSettings : MemberwiseEquatable<NzbIndexSettings>, IIndexerSettings
{ {
private static readonly NzbIndexSettingsValidator Validator = new (); private static readonly NzbIndexSettingsValidator Validator = new ();

@ -1,3 +1,4 @@
using Equ;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
@ -19,7 +20,7 @@ namespace NzbDrone.Core.Indexers
} }
} }
public class IndexerBaseSettings public class IndexerBaseSettings : MemberwiseEquatable<IndexerBaseSettings>
{ {
[FieldDefinition(1, Type = FieldType.Number, Label = "IndexerSettingsQueryLimit", HelpText = "IndexerSettingsQueryLimitHelpText", Advanced = true)] [FieldDefinition(1, Type = FieldType.Number, Label = "IndexerSettingsQueryLimit", HelpText = "IndexerSettingsQueryLimitHelpText", Advanced = true)]
public int? QueryLimit { get; set; } public int? QueryLimit { get; set; }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Equ;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers.Definitions.Cardigann; using NzbDrone.Core.Indexers.Definitions.Cardigann;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles;
@ -10,23 +11,47 @@ namespace NzbDrone.Core.Indexers
{ {
public class IndexerDefinition : ProviderDefinition public class IndexerDefinition : ProviderDefinition
{ {
public string[] IndexerUrls { get; set; } [MemberwiseEqualityIgnore]
public string[] LegacyUrls { get; set; } public IEnumerable<string> IndexerUrls { get; set; }
[MemberwiseEqualityIgnore]
public IEnumerable<string> LegacyUrls { get; set; }
public string Description { get; set; } public string Description { get; set; }
public Encoding Encoding { get; set; } public Encoding Encoding { get; set; }
public string Language { get; set; } public string Language { get; set; }
[MemberwiseEqualityIgnore]
public DownloadProtocol Protocol { get; set; } public DownloadProtocol Protocol { get; set; }
[MemberwiseEqualityIgnore]
public IndexerPrivacy Privacy { get; set; } public IndexerPrivacy Privacy { get; set; }
[MemberwiseEqualityIgnore]
public bool SupportsRss { get; set; } public bool SupportsRss { get; set; }
[MemberwiseEqualityIgnore]
public bool SupportsSearch { get; set; } public bool SupportsSearch { get; set; }
[MemberwiseEqualityIgnore]
public bool SupportsRedirect { get; set; } public bool SupportsRedirect { get; set; }
[MemberwiseEqualityIgnore]
public bool SupportsPagination { get; set; } public bool SupportsPagination { get; set; }
[MemberwiseEqualityIgnore]
public IndexerCapabilities Capabilities { get; set; } public IndexerCapabilities Capabilities { get; set; }
public int Priority { get; set; } = 25; public int Priority { get; set; } = 25;
public bool Redirect { get; set; } public bool Redirect { get; set; }
public int DownloadClientId { get; set; } public int DownloadClientId { get; set; }
[MemberwiseEqualityIgnore]
public DateTime Added { get; set; } public DateTime Added { get; set; }
public int AppProfileId { get; set; } public int AppProfileId { get; set; }
[MemberwiseEqualityIgnore]
public LazyLoaded<AppSyncProfile> AppProfile { get; set; } public LazyLoaded<AppSyncProfile> AppProfile { get; set; }
public List<SettingsField> ExtraFields { get; set; } = new (); public List<SettingsField> ExtraFields { get; set; } = new ();

@ -1,3 +1,4 @@
using Equ;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
@ -50,7 +51,7 @@ namespace NzbDrone.Core.Indexers
} }
} }
public class IndexerTorrentBaseSettings public class IndexerTorrentBaseSettings : MemberwiseEquatable<IndexerTorrentBaseSettings>
{ {
[FieldDefinition(1, Type = FieldType.Number, Label = "IndexerSettingsAppsMinimumSeeders", HelpText = "IndexerSettingsAppsMinimumSeedersHelpText", Advanced = true)] [FieldDefinition(1, Type = FieldType.Number, Label = "IndexerSettingsAppsMinimumSeeders", HelpText = "IndexerSettingsAppsMinimumSeedersHelpText", Advanced = true)]
public int? AppMinimumSeeders { get; set; } public int? AppMinimumSeeders { get; set; }

@ -1,3 +1,4 @@
using Equ;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
@ -14,7 +15,7 @@ namespace NzbDrone.Core.Indexers.Settings
} }
} }
public class NoAuthTorrentBaseSettings : ITorrentIndexerSettings public class NoAuthTorrentBaseSettings : MemberwiseEquatable<NoAuthTorrentBaseSettings>, ITorrentIndexerSettings
{ {
private static readonly NoAuthSettingsValidator<NoAuthTorrentBaseSettings> Validator = new (); private static readonly NoAuthSettingsValidator<NoAuthTorrentBaseSettings> Validator = new ();

@ -3,7 +3,6 @@ using System.Collections.Generic;
using FluentValidation; using FluentValidation;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Apprise namespace NzbDrone.Core.Notifications.Apprise
@ -35,7 +34,7 @@ namespace NzbDrone.Core.Notifications.Apprise
} }
} }
public class AppriseSettings : IProviderConfig public class AppriseSettings : NotificationBaseSettings
{ {
private static readonly AppriseSettingsValidator Validator = new (); private static readonly AppriseSettingsValidator Validator = new ();
@ -66,7 +65,7 @@ namespace NzbDrone.Core.Notifications.Apprise
[FieldDefinition(7, Label = "Password", Type = FieldType.Password, HelpText = "HTTP Basic Auth Password", Privacy = PrivacyLevel.Password)] [FieldDefinition(7, Label = "Password", Type = FieldType.Password, HelpText = "HTTP Basic Auth Password", Privacy = PrivacyLevel.Password)]
public string AuthPassword { get; set; } public string AuthPassword { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
using NzbDrone.Core.Validation.Paths; using NzbDrone.Core.Validation.Paths;
@ -16,7 +15,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
} }
} }
public class CustomScriptSettings : IProviderConfig public class CustomScriptSettings : NotificationBaseSettings
{ {
private static readonly CustomScriptSettingsValidator Validator = new CustomScriptSettingsValidator(); private static readonly CustomScriptSettingsValidator Validator = new CustomScriptSettingsValidator();
@ -26,7 +25,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
[FieldDefinition(1, Label = "Arguments", HelpText = "Arguments to pass to the script", Hidden = HiddenType.HiddenIfNotSet)] [FieldDefinition(1, Label = "Arguments", HelpText = "Arguments to pass to the script", Hidden = HiddenType.HiddenIfNotSet)]
public string Arguments { get; set; } public string Arguments { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Discord namespace NzbDrone.Core.Notifications.Discord
@ -14,7 +13,7 @@ namespace NzbDrone.Core.Notifications.Discord
} }
} }
public class DiscordSettings : IProviderConfig public class DiscordSettings : NotificationBaseSettings
{ {
public DiscordSettings() public DiscordSettings()
{ {
@ -47,7 +46,7 @@ namespace NzbDrone.Core.Notifications.Discord
[FieldDefinition(4, Label = "On Grab Fields", Advanced = true, SelectOptions = typeof(DiscordGrabFieldType), HelpText = "Change the fields that are passed in for this 'on grab' notification", Type = FieldType.Select)] [FieldDefinition(4, Label = "On Grab Fields", Advanced = true, SelectOptions = typeof(DiscordGrabFieldType), HelpText = "Change the fields that are passed in for this 'on grab' notification", Type = FieldType.Select)]
public IEnumerable<int> GrabFields { get; set; } public IEnumerable<int> GrabFields { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Email namespace NzbDrone.Core.Notifications.Email
@ -26,7 +25,7 @@ namespace NzbDrone.Core.Notifications.Email
} }
} }
public class EmailSettings : IProviderConfig public class EmailSettings : NotificationBaseSettings
{ {
private static readonly EmailSettingsValidator Validator = new EmailSettingsValidator(); private static readonly EmailSettingsValidator Validator = new EmailSettingsValidator();
@ -65,7 +64,7 @@ namespace NzbDrone.Core.Notifications.Email
[FieldDefinition(8, Label = "BCC Address(es)", HelpText = "Comma separated list of email bcc recipients", Placeholder = "example@email.com,example1@email.com", Advanced = true)] [FieldDefinition(8, Label = "BCC Address(es)", HelpText = "Comma separated list of email bcc recipients", Placeholder = "example@email.com,example1@email.com", Advanced = true)]
public IEnumerable<string> Bcc { get; set; } public IEnumerable<string> Bcc { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Gotify namespace NzbDrone.Core.Notifications.Gotify
@ -14,7 +13,7 @@ namespace NzbDrone.Core.Notifications.Gotify
} }
} }
public class GotifySettings : IProviderConfig public class GotifySettings : NotificationBaseSettings
{ {
private static readonly GotifySettingsValidator Validator = new GotifySettingsValidator(); private static readonly GotifySettingsValidator Validator = new GotifySettingsValidator();
@ -32,7 +31,7 @@ namespace NzbDrone.Core.Notifications.Gotify
[FieldDefinition(2, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(GotifyPriority), HelpText = "Priority of the notification")] [FieldDefinition(2, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(GotifyPriority), HelpText = "Priority of the notification")]
public int Priority { get; set; } public int Priority { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Join namespace NzbDrone.Core.Notifications.Join
@ -14,7 +13,7 @@ namespace NzbDrone.Core.Notifications.Join
} }
} }
public class JoinSettings : IProviderConfig public class JoinSettings : NotificationBaseSettings
{ {
public JoinSettings() public JoinSettings()
{ {
@ -35,7 +34,7 @@ namespace NzbDrone.Core.Notifications.Join
[FieldDefinition(3, Label = "Notification Priority", Type = FieldType.Select, SelectOptions = typeof(JoinPriority))] [FieldDefinition(3, Label = "Notification Priority", Type = FieldType.Select, SelectOptions = typeof(JoinPriority))]
public int Priority { get; set; } public int Priority { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Mailgun namespace NzbDrone.Core.Notifications.Mailgun
@ -17,7 +16,7 @@ namespace NzbDrone.Core.Notifications.Mailgun
} }
} }
public class MailgunSettings : IProviderConfig public class MailgunSettings : NotificationBaseSettings
{ {
private static readonly MailGunSettingsValidator Validator = new MailGunSettingsValidator(); private static readonly MailGunSettingsValidator Validator = new MailGunSettingsValidator();
@ -41,7 +40,7 @@ namespace NzbDrone.Core.Notifications.Mailgun
[FieldDefinition(4, Label = "Recipient Address(es)", Type = FieldType.Tag, Placeholder = "example@email.com,example1@email.com")] [FieldDefinition(4, Label = "Recipient Address(es)", Type = FieldType.Tag, Placeholder = "example@email.com,example1@email.com")]
public IEnumerable<string> Recipients { get; set; } public IEnumerable<string> Recipients { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Notifiarr namespace NzbDrone.Core.Notifications.Notifiarr
@ -13,14 +12,14 @@ namespace NzbDrone.Core.Notifications.Notifiarr
} }
} }
public class NotifiarrSettings : IProviderConfig public class NotifiarrSettings : NotificationBaseSettings
{ {
private static readonly NotifiarrSettingsValidator Validator = new NotifiarrSettingsValidator(); private static readonly NotifiarrSettingsValidator Validator = new NotifiarrSettingsValidator();
[FieldDefinition(0, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "Your API key from your profile", HelpLink = "https://notifiarr.com")] [FieldDefinition(0, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "Your API key from your profile", HelpLink = "https://notifiarr.com")]
public string APIKey { get; set; } public string APIKey { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -6,7 +6,7 @@ using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Notifications namespace NzbDrone.Core.Notifications
{ {
public abstract class NotificationBase<TSettings> : INotification public abstract class NotificationBase<TSettings> : INotification
where TSettings : IProviderConfig, new() where TSettings : NotificationBaseSettings, new()
{ {
protected const string RELEASE_GRABBED_TITLE = "Release Grabbed"; protected const string RELEASE_GRABBED_TITLE = "Release Grabbed";
protected const string HEALTH_ISSUE_TITLE = "Health Check Failure"; protected const string HEALTH_ISSUE_TITLE = "Health Check Failure";

@ -0,0 +1,11 @@
using Equ;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications
{
public abstract class NotificationBaseSettings : MemberwiseEquatable<NotificationBaseSettings>, IProviderConfig
{
public abstract NzbDroneValidationResult Validate();
}
}

@ -3,7 +3,6 @@ using System.Collections.Generic;
using FluentValidation; using FluentValidation;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Ntfy namespace NzbDrone.Core.Notifications.Ntfy
@ -24,7 +23,7 @@ namespace NzbDrone.Core.Notifications.Ntfy
private static List<string> InvalidTopics => new List<string> { "announcements", "app", "docs", "settings", "stats", "mytopic-rw", "mytopic-ro", "mytopic-wo" }; private static List<string> InvalidTopics => new List<string> { "announcements", "app", "docs", "settings", "stats", "mytopic-rw", "mytopic-ro", "mytopic-wo" };
} }
public class NtfySettings : IProviderConfig public class NtfySettings : NotificationBaseSettings
{ {
private static readonly NtfySettingsValidator Validator = new NtfySettingsValidator(); private static readonly NtfySettingsValidator Validator = new NtfySettingsValidator();
@ -58,7 +57,7 @@ namespace NzbDrone.Core.Notifications.Ntfy
[FieldDefinition(7, Label = "Click URL", Type = FieldType.Url, HelpText = "Optional link when user clicks notification")] [FieldDefinition(7, Label = "Click URL", Type = FieldType.Url, HelpText = "Optional link when user clicks notification")]
public string ClickUrl { get; set; } public string ClickUrl { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Prowl namespace NzbDrone.Core.Notifications.Prowl
@ -13,7 +12,7 @@ namespace NzbDrone.Core.Notifications.Prowl
} }
} }
public class ProwlSettings : IProviderConfig public class ProwlSettings : NotificationBaseSettings
{ {
private static readonly ProwlSettingsValidator Validator = new ProwlSettingsValidator(); private static readonly ProwlSettingsValidator Validator = new ProwlSettingsValidator();
@ -25,7 +24,7 @@ namespace NzbDrone.Core.Notifications.Prowl
public bool IsValid => !string.IsNullOrWhiteSpace(ApiKey) && Priority >= -2 && Priority <= 2; public bool IsValid => !string.IsNullOrWhiteSpace(ApiKey) && Priority >= -2 && Priority <= 2;
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.PushBullet namespace NzbDrone.Core.Notifications.PushBullet
@ -15,7 +14,7 @@ namespace NzbDrone.Core.Notifications.PushBullet
} }
} }
public class PushBulletSettings : IProviderConfig public class PushBulletSettings : NotificationBaseSettings
{ {
private static readonly PushBulletSettingsValidator Validator = new PushBulletSettingsValidator(); private static readonly PushBulletSettingsValidator Validator = new PushBulletSettingsValidator();
@ -37,7 +36,7 @@ namespace NzbDrone.Core.Notifications.PushBullet
[FieldDefinition(3, Label = "Sender ID", HelpText = "The device ID to send notifications from, use device_iden in the device's URL on pushbullet.com (leave blank to send from yourself)")] [FieldDefinition(3, Label = "Sender ID", HelpText = "The device ID to send notifications from, use device_iden in the device's URL on pushbullet.com (leave blank to send from yourself)")]
public string SenderId { get; set; } public string SenderId { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Pushcut namespace NzbDrone.Core.Notifications.Pushcut
@ -14,7 +13,7 @@ namespace NzbDrone.Core.Notifications.Pushcut
} }
} }
public class PushcutSettings : IProviderConfig public class PushcutSettings : NotificationBaseSettings
{ {
private static readonly PushcutSettingsValidator Validator = new (); private static readonly PushcutSettingsValidator Validator = new ();
@ -27,7 +26,7 @@ namespace NzbDrone.Core.Notifications.Pushcut
[FieldDefinition(2, Label = "Time sensitive", Type = FieldType.Checkbox, HelpText = "Check to mark the notification as \"Time-Sensitive\"")] [FieldDefinition(2, Label = "Time sensitive", Type = FieldType.Checkbox, HelpText = "Check to mark the notification as \"Time-Sensitive\"")]
public bool TimeSensitive { get; set; } public bool TimeSensitive { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Pushover namespace NzbDrone.Core.Notifications.Pushover
@ -17,7 +16,7 @@ namespace NzbDrone.Core.Notifications.Pushover
} }
} }
public class PushoverSettings : IProviderConfig public class PushoverSettings : NotificationBaseSettings
{ {
private static readonly PushoverSettingsValidator Validator = new PushoverSettingsValidator(); private static readonly PushoverSettingsValidator Validator = new PushoverSettingsValidator();
@ -51,7 +50,7 @@ namespace NzbDrone.Core.Notifications.Pushover
public bool IsValid => !string.IsNullOrWhiteSpace(UserKey) && Priority >= -1 && Priority <= 2; public bool IsValid => !string.IsNullOrWhiteSpace(UserKey) && Priority >= -1 && Priority <= 2;
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.SendGrid namespace NzbDrone.Core.Notifications.SendGrid
@ -18,7 +17,7 @@ namespace NzbDrone.Core.Notifications.SendGrid
} }
} }
public class SendGridSettings : IProviderConfig public class SendGridSettings : NotificationBaseSettings
{ {
private static readonly SendGridSettingsValidator Validator = new SendGridSettingsValidator(); private static readonly SendGridSettingsValidator Validator = new SendGridSettingsValidator();
@ -39,7 +38,7 @@ namespace NzbDrone.Core.Notifications.SendGrid
[FieldDefinition(3, Label = "Recipient Address(es)", Type = FieldType.Tag, Placeholder = "example@email.com,example1@email.com")] [FieldDefinition(3, Label = "Recipient Address(es)", Type = FieldType.Tag, Placeholder = "example@email.com,example1@email.com")]
public IEnumerable<string> Recipients { get; set; } public IEnumerable<string> Recipients { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Signal namespace NzbDrone.Core.Notifications.Signal
@ -16,7 +15,7 @@ namespace NzbDrone.Core.Notifications.Signal
} }
} }
public class SignalSettings : IProviderConfig public class SignalSettings : NotificationBaseSettings
{ {
private static readonly SignalSettingsValidator Validator = new (); private static readonly SignalSettingsValidator Validator = new ();
@ -41,7 +40,7 @@ namespace NzbDrone.Core.Notifications.Signal
[FieldDefinition(6, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "Password used to authenticate requests toward signal-api")] [FieldDefinition(6, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "Password used to authenticate requests toward signal-api")]
public string AuthPassword { get; set; } public string AuthPassword { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Simplepush namespace NzbDrone.Core.Notifications.Simplepush
@ -13,7 +12,7 @@ namespace NzbDrone.Core.Notifications.Simplepush
} }
} }
public class SimplepushSettings : IProviderConfig public class SimplepushSettings : NotificationBaseSettings
{ {
private static readonly SimplepushSettingsValidator Validator = new SimplepushSettingsValidator(); private static readonly SimplepushSettingsValidator Validator = new SimplepushSettingsValidator();
@ -25,7 +24,7 @@ namespace NzbDrone.Core.Notifications.Simplepush
public bool IsValid => !string.IsNullOrWhiteSpace(Key); public bool IsValid => !string.IsNullOrWhiteSpace(Key);
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Slack namespace NzbDrone.Core.Notifications.Slack
@ -14,7 +13,7 @@ namespace NzbDrone.Core.Notifications.Slack
} }
} }
public class SlackSettings : IProviderConfig public class SlackSettings : NotificationBaseSettings
{ {
private static readonly SlackSettingsValidator Validator = new SlackSettingsValidator(); private static readonly SlackSettingsValidator Validator = new SlackSettingsValidator();
@ -30,7 +29,7 @@ namespace NzbDrone.Core.Notifications.Slack
[FieldDefinition(3, Label = "Channel", HelpText = "Overrides the default channel for the incoming webhook (#other-channel)", Type = FieldType.Textbox)] [FieldDefinition(3, Label = "Channel", HelpText = "Overrides the default channel for the incoming webhook (#other-channel)", Type = FieldType.Textbox)]
public string Channel { get; set; } public string Channel { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,6 +1,5 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Telegram namespace NzbDrone.Core.Notifications.Telegram
@ -16,7 +15,7 @@ namespace NzbDrone.Core.Notifications.Telegram
} }
} }
public class TelegramSettings : IProviderConfig public class TelegramSettings : NotificationBaseSettings
{ {
private static readonly TelegramSettingsValidator Validator = new TelegramSettingsValidator(); private static readonly TelegramSettingsValidator Validator = new TelegramSettingsValidator();
@ -35,7 +34,7 @@ namespace NzbDrone.Core.Notifications.Telegram
[FieldDefinition(4, Label = "NotificationsTelegramSettingsIncludeAppName", Type = FieldType.Checkbox, HelpText = "NotificationsTelegramSettingsIncludeAppNameHelpText")] [FieldDefinition(4, Label = "NotificationsTelegramSettingsIncludeAppName", Type = FieldType.Checkbox, HelpText = "NotificationsTelegramSettingsIncludeAppNameHelpText")]
public bool IncludeAppNameInTitle { get; set; } public bool IncludeAppNameInTitle { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,7 +1,6 @@
using FluentValidation; using FluentValidation;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Twitter namespace NzbDrone.Core.Notifications.Twitter
@ -28,7 +27,7 @@ namespace NzbDrone.Core.Notifications.Twitter
} }
} }
public class TwitterSettings : IProviderConfig public class TwitterSettings : NotificationBaseSettings
{ {
private static readonly TwitterSettingsValidator Validator = new TwitterSettingsValidator(); private static readonly TwitterSettingsValidator Validator = new TwitterSettingsValidator();
@ -59,7 +58,7 @@ namespace NzbDrone.Core.Notifications.Twitter
[FieldDefinition(6, Label = "Connect to Twitter", Type = FieldType.OAuth)] [FieldDefinition(6, Label = "Connect to Twitter", Type = FieldType.OAuth)]
public string AuthorizeNotification { get; set; } public string AuthorizeNotification { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -1,10 +1,9 @@
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Notifications.Webhook namespace NzbDrone.Core.Notifications.Webhook
{ {
public abstract class WebhookBase<TSettings> : NotificationBase<TSettings> public abstract class WebhookBase<TSettings> : NotificationBase<TSettings>
where TSettings : IProviderConfig, new() where TSettings : NotificationBaseSettings, new()
{ {
private readonly IConfigFileProvider _configFileProvider; private readonly IConfigFileProvider _configFileProvider;
private readonly IConfigService _configService; private readonly IConfigService _configService;

@ -1,7 +1,6 @@
using System; using System;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Webhook namespace NzbDrone.Core.Notifications.Webhook
@ -14,7 +13,7 @@ namespace NzbDrone.Core.Notifications.Webhook
} }
} }
public class WebhookSettings : IProviderConfig public class WebhookSettings : NotificationBaseSettings
{ {
private static readonly WebhookSettingsValidator Validator = new WebhookSettingsValidator(); private static readonly WebhookSettingsValidator Validator = new WebhookSettingsValidator();
@ -35,7 +34,7 @@ namespace NzbDrone.Core.Notifications.Webhook
[FieldDefinition(3, Label = "Password", HelpText = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] [FieldDefinition(3, Label = "Password", HelpText = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; } public string Password { get; set; }
public NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));
} }

@ -5,6 +5,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AngleSharp.Xml" Version="1.0.0" /> <PackageReference Include="AngleSharp.Xml" Version="1.0.0" />
<PackageReference Include="Dapper" Version="2.0.123" /> <PackageReference Include="Dapper" Version="2.0.123" />
<PackageReference Include="Equ" Version="2.3.0" />
<PackageReference Include="MailKit" Version="3.6.0" /> <PackageReference Include="MailKit" Version="3.6.0" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="6.0.25" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="6.0.25" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />

@ -1,11 +1,15 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Equ;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.ThingiProvider namespace NzbDrone.Core.ThingiProvider
{ {
public abstract class ProviderDefinition : ModelBase public abstract class ProviderDefinition : ModelBase, IEquatable<ProviderDefinition>
{ {
private static readonly MemberwiseEqualityComparer<ProviderDefinition> Comparer = MemberwiseEqualityComparer<ProviderDefinition>.ByFields;
protected ProviderDefinition() protected ProviderDefinition()
{ {
Tags = new HashSet<int>(); Tags = new HashSet<int>();
@ -16,6 +20,7 @@ namespace NzbDrone.Core.ThingiProvider
public string Name { get; set; } public string Name { get; set; }
[JsonIgnore] [JsonIgnore]
[MemberwiseEqualityIgnore]
public string ImplementationName { get; set; } public string ImplementationName { get; set; }
public string Implementation { get; set; } public string Implementation { get; set; }
@ -24,12 +29,10 @@ namespace NzbDrone.Core.ThingiProvider
public ProviderMessage Message { get; set; } public ProviderMessage Message { get; set; }
public HashSet<int> Tags { get; set; } public HashSet<int> Tags { get; set; }
[MemberwiseEqualityIgnore]
public IProviderConfig Settings public IProviderConfig Settings
{ {
get get => _settings;
{
return _settings;
}
set set
{ {
_settings = value; _settings = value;
@ -39,5 +42,20 @@ namespace NzbDrone.Core.ThingiProvider
} }
} }
} }
public bool Equals(ProviderDefinition other)
{
return Comparer.Equals(this, other);
}
public override bool Equals(object obj)
{
return Equals(obj as ProviderDefinition);
}
public override int GetHashCode()
{
return Comparer.GetHashCode(this);
}
} }
} }

@ -13,8 +13,8 @@ namespace Prowlarr.Api.V1.Indexers
{ {
public class IndexerResource : ProviderResource<IndexerResource> public class IndexerResource : ProviderResource<IndexerResource>
{ {
public string[] IndexerUrls { get; set; } public IEnumerable<string> IndexerUrls { get; set; }
public string[] LegacyUrls { get; set; } public IEnumerable<string> LegacyUrls { get; set; }
public string DefinitionName { get; set; } public string DefinitionName { get; set; }
public string Description { get; set; } public string Description { get; set; }
public string Language { get; set; } public string Language { get; set; }

@ -3,7 +3,6 @@ using System.Linq;
using FluentValidation; using FluentValidation;
using FluentValidation.Results; using FluentValidation.Results;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
using NzbDrone.Http.REST.Attributes; using NzbDrone.Http.REST.Attributes;
@ -90,10 +89,8 @@ namespace Prowlarr.Api.V1
var existingDefinition = _providerFactory.Find(providerResource.Id); var existingDefinition = _providerFactory.Find(providerResource.Id);
var providerDefinition = GetDefinition(providerResource, existingDefinition, true, !forceSave, false); var providerDefinition = GetDefinition(providerResource, existingDefinition, true, !forceSave, false);
// Comparing via JSON string to eliminate the need for every provider implementation to implement equality checks.
// Compare settings separately because they are not serialized with the definition. // Compare settings separately because they are not serialized with the definition.
var hasDefinitionChanged = STJson.ToJson(existingDefinition) != STJson.ToJson(providerDefinition) || var hasDefinitionChanged = !existingDefinition.Equals(providerDefinition) || !existingDefinition.Settings.Equals(providerDefinition.Settings);
STJson.ToJson(existingDefinition.Settings) != STJson.ToJson(providerDefinition.Settings);
// Only test existing definitions if it is enabled and forceSave isn't set and the definition has changed. // Only test existing definitions if it is enabled and forceSave isn't set and the definition has changed.
if (providerDefinition.Enable && !forceSave && hasDefinitionChanged) if (providerDefinition.Enable && !forceSave && hasDefinitionChanged)

Loading…
Cancel
Save