diff --git a/src/NzbDrone.Core/Notifications/Join/JoinInvalidDeviceException.cs b/src/NzbDrone.Core/Notifications/Join/JoinInvalidDeviceException.cs new file mode 100644 index 000000000..09e69678d --- /dev/null +++ b/src/NzbDrone.Core/Notifications/Join/JoinInvalidDeviceException.cs @@ -0,0 +1,16 @@ +using System; +using NzbDrone.Common.Exceptions; + +namespace NzbDrone.Core.Notifications.Join +{ + public class JoinInvalidDeviceException : JoinException + { + public JoinInvalidDeviceException(string message) : base(message) + { + } + + public JoinInvalidDeviceException(string message, Exception innerException, params object[] args) : base(message, innerException, args) + { + } + } +} diff --git a/src/NzbDrone.Core/Notifications/Join/JoinProxy.cs b/src/NzbDrone.Core/Notifications/Join/JoinProxy.cs index a9da72656..8fe8cba60 100644 --- a/src/NzbDrone.Core/Notifications/Join/JoinProxy.cs +++ b/src/NzbDrone.Core/Notifications/Join/JoinProxy.cs @@ -36,7 +36,7 @@ namespace NzbDrone.Core.Notifications.Join catch (JoinException ex) { _logger.Error(ex, "Unable to send Join message."); - throw new JoinException("Unable to send Join notifications. " + ex.Message); + throw ex; } } @@ -50,46 +50,77 @@ namespace NzbDrone.Core.Notifications.Join SendNotification(title, body, settings); return null; } + catch(JoinInvalidDeviceException ex) + { + _logger.Error(ex, "Unable to send test Join message. Invalid Device IDs supplied."); + return new ValidationFailure("DeviceIds", "Device IDs appear invalid."); + } catch (JoinException ex) { _logger.Error(ex, "Unable to send test Join message."); - return new ValidationFailure("APIKey", ex.Message); + return new ValidationFailure("ApiKey", ex.Message); + } + catch(RestException ex) + { + _logger.Error(ex, "Unable to send test Join message. Server connection failed."); + return new ValidationFailure("ApiKey", "Unable to connect to Join API. Please try again later."); + } + catch (Exception ex) + { + _logger.Error(ex, "Unable to send test Join message. Unknown error."); + return new ValidationFailure("ApiKey", ex.Message); } } private void SendNotification(string title, string message, RestRequest request, JoinSettings settings) { - try - { - var client = RestClientFactory.BuildClient(URL); + var client = RestClientFactory.BuildClient(URL); + + if (!string.IsNullOrEmpty(settings.DeviceIds)) + { + request.AddParameter("deviceIds", settings.DeviceIds); + } + else + { request.AddParameter("deviceId", "group.all"); - request.AddParameter("apikey", settings.APIKey); - request.AddParameter("title", title); - request.AddParameter("text", message); - request.AddParameter("icon", "https://cdn.rawgit.com/Sonarr/Sonarr/develop/Logo/256.png"); // Use the Sonarr logo. + } + + request.AddParameter("apikey", settings.ApiKey); + request.AddParameter("title", title); + request.AddParameter("text", message); + request.AddParameter("icon", "https://cdn.rawgit.com/Sonarr/Sonarr/develop/Logo/256.png"); // Use the Sonarr logo. + + var response = client.ExecuteAndValidate(request); + var res = Json.Deserialize(response.Content); - var response = client.ExecuteAndValidate(request); - var res = Json.Deserialize(response.Content); + if (res.success) return; - if (res.success) return; + if (res.userAuthError) + { + throw new JoinAuthException("Authentication failed."); + } - if (res.errorMessage != null) + if (res.errorMessage != null) + { + // Unfortunately hard coding this string here is the only way to determine that there aren't any devices to send to. + // There isn't an enum or flag contained in the response that can be used instead. + if (res.errorMessage.Equals("No devices to send to")) { - throw new JoinException(res.errorMessage); + throw new JoinInvalidDeviceException(res.errorMessage); } - - if (res.userAuthError) + // Oddly enough, rather than give us an "Invalid API key", the Join API seems to assume the key is valid, + // but fails when doing a device lookup associated with that key. + // In our case we are using "deviceIds" rather than "deviceId" so when the singular form error shows up + // we know the API key was the fault. + else if (res.errorMessage.Equals("No device to send message to")) { throw new JoinAuthException("Authentication failed."); } - - throw new JoinException("Unknown error. Join message failed to send."); - } - catch(Exception e) - { - throw new JoinException(e.Message, e); + throw new JoinException(res.errorMessage); } + + throw new JoinException("Unknown error. Join message failed to send."); } } } diff --git a/src/NzbDrone.Core/Notifications/Join/JoinSettings.cs b/src/NzbDrone.Core/Notifications/Join/JoinSettings.cs index a8adb77b9..cc8a063eb 100644 --- a/src/NzbDrone.Core/Notifications/Join/JoinSettings.cs +++ b/src/NzbDrone.Core/Notifications/Join/JoinSettings.cs @@ -10,7 +10,8 @@ namespace NzbDrone.Core.Notifications.Join { public JoinSettingsValidator() { - RuleFor(c => c.APIKey).NotEmpty(); + RuleFor(s => s.ApiKey).NotEmpty(); + RuleFor(s => s.DeviceIds).Matches(@"\A\S+\z").When(s => !string.IsNullOrEmpty(s.DeviceIds)); } } @@ -19,7 +20,10 @@ namespace NzbDrone.Core.Notifications.Join private static readonly JoinSettingsValidator Validator = new JoinSettingsValidator(); [FieldDefinition(0, Label = "API Key", HelpText = "The API Key from your Join account settings (click Join API button).", HelpLink = "https://joinjoaomgcd.appspot.com/")] - public string APIKey { get; set; } + public string ApiKey { get; set; } + + [FieldDefinition(1, Label = "Device IDs", HelpText = "Comma separated list of Device IDs you'd like to send notifications to. If unset, all devices will receive notifications.", HelpLink = "https://joinjoaomgcd.appspot.com/")] + public string DeviceIds { get; set; } public NzbDroneValidationResult Validate() { diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 3db8078e4..7a32cde0d 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -806,6 +806,7 @@ +