Refactored the Notification service to how it should have really been done in the first place.

This commit is contained in:
tidusjar 2016-03-24 17:14:45 +00:00
parent 840deb6161
commit 3fe1f13bd1
17 changed files with 220 additions and 166 deletions

View file

@ -24,6 +24,8 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System.Threading.Tasks;
using PlexRequests.Api.Models.Notifications; using PlexRequests.Api.Models.Notifications;
namespace PlexRequests.Api.Interfaces namespace PlexRequests.Api.Interfaces
@ -38,6 +40,6 @@ namespace PlexRequests.Api.Interfaces
/// <param name="message">The message.</param> /// <param name="message">The message.</param>
/// <param name="deviceIdentifier">The device identifier.</param> /// <param name="deviceIdentifier">The device identifier.</param>
/// <returns></returns> /// <returns></returns>
PushbulletResponse Push(string accessToken, string title, string message, string deviceIdentifier = default(string)); Task<PushbulletResponse> PushAsync(string accessToken, string title, string message, string deviceIdentifier = default(string));
} }
} }

View file

@ -24,6 +24,7 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System.Threading.Tasks;
using PlexRequests.Api.Models.Notifications; using PlexRequests.Api.Models.Notifications;
@ -31,6 +32,6 @@ namespace PlexRequests.Api.Interfaces
{ {
public interface IPushoverApi public interface IPushoverApi
{ {
PushoverResponse Push(string accessToken, string message, string userToken); Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken);
} }
} }

View file

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System; using System;
using System.Threading.Tasks;
using PlexRequests.Api.Interfaces; using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Notifications; using PlexRequests.Api.Models.Notifications;
@ -35,7 +36,7 @@ namespace PlexRequests.Api
{ {
public class PushbulletApi : IPushbulletApi public class PushbulletApi : IPushbulletApi
{ {
public PushbulletResponse Push(string accessToken, string title, string message, string deviceIdentifier = default(string)) public async Task<PushbulletResponse> PushAsync(string accessToken, string title, string message, string deviceIdentifier = default(string))
{ {
var request = new RestRequest var request = new RestRequest
{ {
@ -56,7 +57,7 @@ namespace PlexRequests.Api
request.AddJsonBody(push); request.AddJsonBody(push);
var api = new ApiRequest(); var api = new ApiRequest();
return api.ExecuteJson<PushbulletResponse>(request, new Uri("https://api.pushbullet.com/v2/pushes")); return await Task.Run(() => api.ExecuteJson<PushbulletResponse>(request, new Uri("https://api.pushbullet.com/v2/pushes")));
} }
} }
} }

View file

@ -25,7 +25,8 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System; using System;
using Nancy.Helpers; using System.Threading.Tasks;
using PlexRequests.Api.Interfaces; using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Notifications; using PlexRequests.Api.Models.Notifications;
@ -35,7 +36,7 @@ namespace PlexRequests.Api
{ {
public class PushoverApi : IPushoverApi public class PushoverApi : IPushoverApi
{ {
public PushoverResponse Push(string accessToken, string message, string userToken) public async Task<PushoverResponse> PushAsync(string accessToken, string message, string userToken)
{ {
var request = new RestRequest var request = new RestRequest
{ {
@ -49,7 +50,7 @@ namespace PlexRequests.Api
var api = new ApiRequest(); var api = new ApiRequest();
return api.ExecuteJson<PushoverResponse>(request, new Uri("https://api.pushover.net/1")); return await Task.Run(() => api.ExecuteJson<PushoverResponse>(request, new Uri("https://api.pushover.net/1")));
} }
} }
} }

View file

@ -1,33 +1,37 @@
#region Copyright #region Copyright
// /************************************************************************ /************************************************************************
// Copyright (c) 2016 Jamie Rees Copyright (c) 2016 Jamie Rees
// File: NotificationServiceTests.cs File: NotificationServiceTests.cs
// Created By: Jamie Rees Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
// the following conditions: the following conditions:
//
// The above copyright notice and this permission notice shall be The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software. included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ ************************************************************************/
#endregion #endregion
using System;
using System.Threading.Tasks;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Notification; using PlexRequests.Services.Notification;
namespace PlexRequests.Services.Tests namespace PlexRequests.Services.Tests
@ -35,9 +39,15 @@ namespace PlexRequests.Services.Tests
[TestFixture] [TestFixture]
public class NotificationServiceTests public class NotificationServiceTests
{ {
public NotificationService NotificationService { get; set; }
[SetUp]
public void Setup()
{
NotificationService = new NotificationService();
}
[Test] [Test]
[Ignore("Need to rework due to static class")]
public void SubscribeNewNotifier() public void SubscribeNewNotifier()
{ {
var notificationMock = new Mock<INotification>(); var notificationMock = new Mock<INotification>();
@ -49,7 +59,6 @@ namespace PlexRequests.Services.Tests
} }
[Test] [Test]
[Ignore("Need to rework due to static class")]
public void SubscribeExistingNotifier() public void SubscribeExistingNotifier()
{ {
var notificationMock1 = new Mock<INotification>(); var notificationMock1 = new Mock<INotification>();
@ -68,7 +77,6 @@ namespace PlexRequests.Services.Tests
} }
[Test] [Test]
[Ignore("Need to rework due to static class")]
public void UnSubscribeMissingNotifier() public void UnSubscribeMissingNotifier()
{ {
var notificationMock = new Mock<INotification>(); var notificationMock = new Mock<INotification>();
@ -79,7 +87,6 @@ namespace PlexRequests.Services.Tests
} }
[Test] [Test]
[Ignore("Need to rework due to static class")]
public void UnSubscribeNotifier() public void UnSubscribeNotifier()
{ {
var notificationMock = new Mock<INotification>(); var notificationMock = new Mock<INotification>();
@ -92,17 +99,15 @@ namespace PlexRequests.Services.Tests
} }
[Test] [Test]
[Ignore("Need to rework due to static class")]
public void PublishWithNoObservers() public void PublishWithNoObservers()
{ {
Assert.DoesNotThrow( Assert.DoesNotThrowAsync(
() => async() =>
{ NotificationService.Publish(new NotificationModel()); }); { await NotificationService.Publish(new NotificationModel()); });
} }
[Test] [Test]
[Ignore("Need to rework due to static class")] public async Task PublishAllNotifiers()
public void PublishAllNotifiers()
{ {
var notificationMock1 = new Mock<INotification>(); var notificationMock1 = new Mock<INotification>();
var notificationMock2 = new Mock<INotification>(); var notificationMock2 = new Mock<INotification>();
@ -112,11 +117,21 @@ namespace PlexRequests.Services.Tests
NotificationService.Subscribe(notificationMock2.Object); NotificationService.Subscribe(notificationMock2.Object);
Assert.That(NotificationService.Observers.Count, Is.EqualTo(2)); Assert.That(NotificationService.Observers.Count, Is.EqualTo(2));
var model = new NotificationModel {Title = "abc", Body = "test"}; var model = new NotificationModel { Title = "abc", Body = "test" };
NotificationService.Publish(model); await NotificationService.Publish(model);
notificationMock1.Verify(x => x.Notify(model), Times.Once); notificationMock1.Verify(x => x.NotifyAsync(model), Times.Once);
notificationMock2.Verify(x => x.Notify(model), Times.Once); notificationMock2.Verify(x => x.NotifyAsync(model), Times.Once);
}
[Test]
public async Task PublishWithException()
{
var notificationMock = new Mock<INotification>();
notificationMock.Setup(x => x.NotifyAsync(It.IsAny<NotificationModel>())).Throws<Exception>();
notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1");
NotificationService.Subscribe(notificationMock.Object);
await NotificationService.Publish(new NotificationModel());
} }
} }
} }

View file

@ -24,22 +24,16 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
namespace PlexRequests.Services.Notification using System.Threading.Tasks;
using PlexRequests.Services.Notification;
namespace PlexRequests.Services.Interfaces
{ {
public interface INotification public interface INotification
{ {
/// <summary>
/// Gets the name of the notification.
/// </summary>
/// <value>
/// The name of the notification.
/// </value>
string NotificationName { get; } string NotificationName { get; }
/// <summary>
/// Notifies the specified title. Task NotifyAsync(NotificationModel model);
/// </summary>
/// <param name="model">The model.</param>
/// <returns></returns>
bool Notify(NotificationModel model);
} }
} }

View file

@ -0,0 +1,40 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: INotificationService.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System.Threading.Tasks;
using PlexRequests.Services.Notification;
namespace PlexRequests.Services.Interfaces
{
public interface INotificationService
{
Task Publish(NotificationModel model);
void Subscribe(INotification notification);
void UnSubscribe(INotification notification);
}
}

View file

@ -27,11 +27,13 @@
using System; using System;
using System.Net; using System.Net;
using System.Net.Mail; using System.Net.Mail;
using System.Threading.Tasks;
using NLog; using NLog;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Services.Interfaces;
namespace PlexRequests.Services.Notification namespace PlexRequests.Services.Notification
{ {
@ -47,31 +49,35 @@ namespace PlexRequests.Services.Notification
private EmailNotificationSettings Settings => GetConfiguration(); private EmailNotificationSettings Settings => GetConfiguration();
public string NotificationName => "EmailMessageNotification"; public string NotificationName => "EmailMessageNotification";
public bool Notify(NotificationModel model) public async Task NotifyAsync(NotificationModel model)
{ {
var configuration = GetConfiguration(); var configuration = GetConfiguration();
if (!ValidateConfiguration(configuration)) if (!ValidateConfiguration(configuration))
{ {
return false; return;
} }
switch (model.NotificationType) switch (model.NotificationType)
{ {
case NotificationType.NewRequest: case NotificationType.NewRequest:
return EmailNewRequest(model); await EmailNewRequest(model);
break;
case NotificationType.Issue: case NotificationType.Issue:
return EmailIssue(model); await EmailIssue(model);
break;
case NotificationType.RequestAvailable: case NotificationType.RequestAvailable:
break; throw new NotImplementedException();
case NotificationType.RequestApproved: case NotificationType.RequestApproved:
break; throw new NotImplementedException();
case NotificationType.AdminNote: case NotificationType.AdminNote:
break; throw new NotImplementedException();
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
return false;
} }
private EmailNotificationSettings GetConfiguration() private EmailNotificationSettings GetConfiguration()
@ -94,7 +100,7 @@ namespace PlexRequests.Services.Notification
return true; return true;
} }
private bool EmailNewRequest(NotificationModel model) private async Task EmailNewRequest(NotificationModel model)
{ {
var message = new MailMessage var message = new MailMessage
{ {
@ -111,22 +117,20 @@ namespace PlexRequests.Services.Notification
{ {
smtp.Credentials = new NetworkCredential(Settings.EmailUsername, Settings.EmailPassword); smtp.Credentials = new NetworkCredential(Settings.EmailUsername, Settings.EmailPassword);
smtp.EnableSsl = Settings.Ssl; smtp.EnableSsl = Settings.Ssl;
smtp.Send(message); await smtp.SendMailAsync(message).ConfigureAwait(false);
return true;
} }
} }
catch (SmtpException smtp) catch (SmtpException smtp)
{ {
Log.Fatal(smtp); Log.Error(smtp);
} }
catch (Exception e) catch (Exception e)
{ {
Log.Fatal(e); Log.Error(e);
} }
return false;
} }
private bool EmailIssue(NotificationModel model) private async Task EmailIssue(NotificationModel model)
{ {
var message = new MailMessage var message = new MailMessage
{ {
@ -143,19 +147,17 @@ namespace PlexRequests.Services.Notification
{ {
smtp.Credentials = new NetworkCredential(Settings.EmailUsername, Settings.EmailPassword); smtp.Credentials = new NetworkCredential(Settings.EmailUsername, Settings.EmailPassword);
smtp.EnableSsl = Settings.Ssl; smtp.EnableSsl = Settings.Ssl;
smtp.Send(message); await smtp.SendMailAsync(message).ConfigureAwait(false);
return true;
} }
} }
catch (SmtpException smtp) catch (SmtpException smtp)
{ {
Log.Fatal(smtp); Log.Error(smtp);
} }
catch (Exception e) catch (Exception e)
{ {
Log.Fatal(e); Log.Error(e);
} }
return false;
} }
} }
} }

View file

@ -24,64 +24,49 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System.Collections.Generic; using System;
using System.Threading; using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using NLog; using NLog;
using PlexRequests.Helpers; using PlexRequests.Services.Interfaces;
namespace PlexRequests.Services.Notification namespace PlexRequests.Services.Notification
{ {
public static class NotificationService public class NotificationService : INotificationService
{ {
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
public static Dictionary<string, INotification> Observers { get; } public ConcurrentDictionary<string, INotification> Observers { get; } = new ConcurrentDictionary<string, INotification>();
static NotificationService() public async Task Publish(NotificationModel model)
{ {
Observers = new Dictionary<string, INotification>(); var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model));
await Task.WhenAll(notificationTasks).ConfigureAwait(false);
} }
public static void Publish(NotificationModel model) public void Subscribe(INotification notification)
{ {
Log.Trace("Notifying all observers: "); Observers.TryAdd(notification.NotificationName, notification);
Log.Trace(Observers.DumpJson());
foreach (var observer in Observers)
{
var notification = observer.Value;
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
notification.Notify(model);
}).Start();
}
} }
public static void Subscribe(INotification notification) public void UnSubscribe(INotification notification)
{ {
INotification notificationValue; Observers.TryRemove(notification.NotificationName, out notification);
if (Observers.TryGetValue(notification.NotificationName, out notificationValue))
{
return;
} }
Observers[notification.NotificationName] = notification; private static async Task NotifyAsync(INotification notification, NotificationModel model)
}
public static void UnSubscribe(INotification notification)
{ {
Log.Trace("Unsubscribing Observer {0}", notification.NotificationName); try
INotification notificationValue;
if (!Observers.TryGetValue(notification.NotificationName, out notificationValue))
{ {
Log.Trace("Observer {0} doesn't exist to Unsubscribe", notification.NotificationName); await notification.NotifyAsync(model).ConfigureAwait(false);
// Observer doesn't exists }
return; catch (Exception ex)
{
Log.Error(ex, $"Notification '{notification.NotificationName}' failed with exception");
} }
Observers.Remove(notification.NotificationName);
} }
} }
} }

View file

@ -25,12 +25,14 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System; using System;
using System.Threading.Tasks;
using NLog; using NLog;
using PlexRequests.Api.Interfaces; using PlexRequests.Api.Interfaces;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Services.Interfaces;
namespace PlexRequests.Services.Notification namespace PlexRequests.Services.Notification
{ {
@ -47,21 +49,21 @@ namespace PlexRequests.Services.Notification
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
public string NotificationName => "PushbulletNotification"; public string NotificationName => "PushbulletNotification";
public bool Notify(NotificationModel model) public async Task NotifyAsync(NotificationModel model)
{ {
if (!ValidateConfiguration()) if (!ValidateConfiguration())
{ {
return false; return;
} }
switch (model.NotificationType) switch (model.NotificationType)
{ {
case NotificationType.NewRequest: case NotificationType.NewRequest:
return PushNewRequest(model); await PushNewRequestAsync(model);
break;
case NotificationType.Issue: case NotificationType.Issue:
return PushIssue(model); await PushIssueAsync(model);
break;
case NotificationType.RequestAvailable: case NotificationType.RequestAvailable:
break; break;
case NotificationType.RequestApproved: case NotificationType.RequestApproved:
@ -71,8 +73,6 @@ namespace PlexRequests.Services.Notification
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
return false;
} }
private bool ValidateConfiguration() private bool ValidateConfiguration()
@ -93,42 +93,40 @@ namespace PlexRequests.Services.Notification
return SettingsService.GetSettings(); return SettingsService.GetSettings();
} }
private bool PushNewRequest(NotificationModel model) private async Task PushNewRequestAsync(NotificationModel model)
{ {
var message = $"{model.Title} has been requested by user: {model.User}"; var message = $"{model.Title} has been requested by user: {model.User}";
var pushTitle = $"Plex Requests: {model.Title} has been requested!"; var pushTitle = $"Plex Requests: {model.Title} has been requested!";
try try
{ {
var result = PushbulletApi.Push(Settings.AccessToken, pushTitle, message, Settings.DeviceIdentifier); var result = await PushbulletApi.PushAsync(Settings.AccessToken, pushTitle, message, Settings.DeviceIdentifier);
if (result != null) if (result == null)
{ {
return true; Log.Error("Pushbullet api returned a null value, the notification did not get pushed");
} }
} }
catch (Exception e) catch (Exception e)
{ {
Log.Fatal(e); Log.Error(e);
} }
return false;
} }
private bool PushIssue(NotificationModel model) private async Task PushIssueAsync(NotificationModel model)
{ {
var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}"; var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
var pushTitle = $"Plex Requests: A new issue has been reported for {model.Title}"; var pushTitle = $"Plex Requests: A new issue has been reported for {model.Title}";
try try
{ {
var result = PushbulletApi.Push(Settings.AccessToken, pushTitle, message, Settings.DeviceIdentifier); var result = await PushbulletApi.PushAsync(Settings.AccessToken, pushTitle, message, Settings.DeviceIdentifier);
if (result != null) if (result != null)
{ {
return true; Log.Error("Pushbullet api returned a null value, the notification did not get pushed");
} }
} }
catch (Exception e) catch (Exception e)
{ {
Log.Fatal(e); Log.Error(e);
} }
return false;
} }
} }
} }

View file

@ -25,12 +25,14 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System; using System;
using System.Threading.Tasks;
using NLog; using NLog;
using PlexRequests.Api.Interfaces; using PlexRequests.Api.Interfaces;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Services.Interfaces;
namespace PlexRequests.Services.Notification namespace PlexRequests.Services.Notification
{ {
@ -47,21 +49,21 @@ namespace PlexRequests.Services.Notification
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
public string NotificationName => "PushoverNotification"; public string NotificationName => "PushoverNotification";
public bool Notify(NotificationModel model) public async Task NotifyAsync(NotificationModel model)
{ {
if (!ValidateConfiguration()) if (!ValidateConfiguration())
{ {
return false; return;
} }
switch (model.NotificationType) switch (model.NotificationType)
{ {
case NotificationType.NewRequest: case NotificationType.NewRequest:
return PushNewRequest(model); await PushNewRequestAsync(model);
break;
case NotificationType.Issue: case NotificationType.Issue:
return PushIssue(model); await PushIssueAsync(model);
break;
case NotificationType.RequestAvailable: case NotificationType.RequestAvailable:
break; break;
case NotificationType.RequestApproved: case NotificationType.RequestApproved:
@ -71,8 +73,6 @@ namespace PlexRequests.Services.Notification
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
return false;
} }
private bool ValidateConfiguration() private bool ValidateConfiguration()
@ -93,40 +93,38 @@ namespace PlexRequests.Services.Notification
return SettingsService.GetSettings(); return SettingsService.GetSettings();
} }
private bool PushNewRequest(NotificationModel model) private async Task PushNewRequestAsync(NotificationModel model)
{ {
var message = $"Plex Requests: {model.Title} has been requested by user: {model.User}"; var message = $"Plex Requests: {model.Title} has been requested by user: {model.User}";
try try
{ {
var result = PushoverApi.Push(Settings.AccessToken, message, Settings.UserToken); var result = await PushoverApi.PushAsync(Settings.AccessToken, message, Settings.UserToken);
if (result?.status == 1) if (result?.status != 1)
{ {
return true; Log.Error("Pushover api returned a status that was not 1, the notification did not get pushed");
} }
} }
catch (Exception e) catch (Exception e)
{ {
Log.Fatal(e); Log.Error(e);
} }
return false;
} }
private bool PushIssue(NotificationModel model) private async Task PushIssueAsync(NotificationModel model)
{ {
var message = $"Plex Requests: A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}"; var message = $"Plex Requests: A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
try try
{ {
var result = PushoverApi.Push(Settings.AccessToken, message, Settings.UserToken); var result = await PushoverApi.PushAsync(Settings.AccessToken, message, Settings.UserToken);
if (result != null) if (result?.status != 1)
{ {
return true; Log.Error("Pushover api returned a status that was not 1, the notification did not get pushed");
} }
} }
catch (Exception e) catch (Exception e)
{ {
Log.Fatal(e); Log.Error(e);
} }
return false;
} }
} }
} }

View file

@ -77,7 +77,8 @@
<Compile Include="Interfaces\IAvailabilityChecker.cs" /> <Compile Include="Interfaces\IAvailabilityChecker.cs" />
<Compile Include="Interfaces\IConfigurationReader.cs" /> <Compile Include="Interfaces\IConfigurationReader.cs" />
<Compile Include="Interfaces\IIntervals.cs" /> <Compile Include="Interfaces\IIntervals.cs" />
<Compile Include="Notification\INotification.cs" /> <Compile Include="Interfaces\INotification.cs" />
<Compile Include="Interfaces\INotificationService.cs" />
<Compile Include="Notification\EmailMessageNotification.cs" /> <Compile Include="Notification\EmailMessageNotification.cs" />
<Compile Include="Notification\NotificationModel.cs" /> <Compile Include="Notification\NotificationModel.cs" />
<Compile Include="Notification\NotificationService.cs" /> <Compile Include="Notification\NotificationService.cs" />

View file

@ -39,6 +39,7 @@ using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Plex; using PlexRequests.Api.Models.Plex;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Services.Interfaces;
using PlexRequests.Store.Models; using PlexRequests.Store.Models;
using PlexRequests.Store.Repository; using PlexRequests.Store.Repository;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
@ -64,6 +65,7 @@ namespace PlexRequests.UI.Tests
private Mock<IPushoverApi> PushoverApi { get; set; } private Mock<IPushoverApi> PushoverApi { get; set; }
private Mock<ICouchPotatoApi> CpApi { get; set; } private Mock<ICouchPotatoApi> CpApi { get; set; }
private Mock<IRepository<LogEntity>> LogRepo { get; set; } private Mock<IRepository<LogEntity>> LogRepo { get; set; }
private Mock<INotificationService> NotificationService { get; set; }
private ConfigurableBootstrapper Bootstrapper { get; set; } private ConfigurableBootstrapper Bootstrapper { get; set; }
@ -91,6 +93,7 @@ namespace PlexRequests.UI.Tests
LogRepo = new Mock<IRepository<LogEntity>>(); LogRepo = new Mock<IRepository<LogEntity>>();
PushoverSettings = new Mock<ISettingsService<PushoverNotificationSettings>>(); PushoverSettings = new Mock<ISettingsService<PushoverNotificationSettings>>();
PushoverApi = new Mock<IPushoverApi>(); PushoverApi = new Mock<IPushoverApi>();
NotificationService = new Mock<INotificationService>();
Bootstrapper = new ConfigurableBootstrapper(with => Bootstrapper = new ConfigurableBootstrapper(with =>
{ {
@ -110,6 +113,7 @@ namespace PlexRequests.UI.Tests
with.Dependency(LogRepo.Object); with.Dependency(LogRepo.Object);
with.Dependency(PushoverSettings.Object); with.Dependency(PushoverSettings.Object);
with.Dependency(PushoverApi.Object); with.Dependency(PushoverApi.Object);
with.Dependency(NotificationService.Object);
with.RootPathProvider<TestRootPathProvider>(); with.RootPathProvider<TestRootPathProvider>();
with.RequestStartup((container, pipelines, context) => with.RequestStartup((container, pipelines, context) =>
{ {

View file

@ -96,6 +96,9 @@ namespace PlexRequests.UI
container.Register<ISonarrApi, SonarrApi>(); container.Register<ISonarrApi, SonarrApi>();
container.Register<IPlexApi, PlexApi>(); container.Register<IPlexApi, PlexApi>();
// NotificationService
container.Register<INotificationService, NotificationService>().AsSingleton();
SubscribeAllObservers(container); SubscribeAllObservers(container);
base.ConfigureRequestContainer(container, context); base.ConfigureRequestContainer(container, context);
} }
@ -130,25 +133,27 @@ namespace PlexRequests.UI
private void SubscribeAllObservers(TinyIoCContainer container) private void SubscribeAllObservers(TinyIoCContainer container)
{ {
var notificationService = container.Resolve<INotificationService>();
var emailSettingsService = container.Resolve<ISettingsService<EmailNotificationSettings>>(); var emailSettingsService = container.Resolve<ISettingsService<EmailNotificationSettings>>();
var emailSettings = emailSettingsService.GetSettings(); var emailSettings = emailSettingsService.GetSettings();
if (emailSettings.Enabled) if (emailSettings.Enabled)
{ {
NotificationService.Subscribe(new EmailMessageNotification(emailSettingsService)); notificationService.Subscribe(new EmailMessageNotification(emailSettingsService));
} }
var pushbulletService = container.Resolve<ISettingsService<PushbulletNotificationSettings>>(); var pushbulletService = container.Resolve<ISettingsService<PushbulletNotificationSettings>>();
var pushbulletSettings = pushbulletService.GetSettings(); var pushbulletSettings = pushbulletService.GetSettings();
if (pushbulletSettings.Enabled) if (pushbulletSettings.Enabled)
{ {
NotificationService.Subscribe(new PushbulletNotification(container.Resolve<IPushbulletApi>(), pushbulletService)); notificationService.Subscribe(new PushbulletNotification(container.Resolve<IPushbulletApi>(), pushbulletService));
} }
var pushoverService = container.Resolve<ISettingsService<PushoverNotificationSettings>>(); var pushoverService = container.Resolve<ISettingsService<PushoverNotificationSettings>>();
var pushoverSettings = pushoverService.GetSettings(); var pushoverSettings = pushoverService.GetSettings();
if (pushoverSettings.Enabled) if (pushoverSettings.Enabled)
{ {
NotificationService.Subscribe(new PushoverNotification(container.Resolve<IPushoverApi>(), pushoverService)); notificationService.Subscribe(new PushoverNotification(container.Resolve<IPushoverApi>(), pushoverService));
} }
} }
} }

View file

@ -26,7 +26,6 @@
#endregion #endregion
using System.Dynamic; using System.Dynamic;
using System.Linq; using System.Linq;
using System.Web.UI.WebControls;
using MarkdownSharp; using MarkdownSharp;
@ -44,6 +43,7 @@ using PlexRequests.Api.Interfaces;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers; using PlexRequests.Helpers;
using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Notification; using PlexRequests.Services.Notification;
using PlexRequests.Store.Models; using PlexRequests.Store.Models;
using PlexRequests.Store.Repository; using PlexRequests.Store.Repository;
@ -69,6 +69,7 @@ namespace PlexRequests.UI.Modules
private IPushoverApi PushoverApi { get; } private IPushoverApi PushoverApi { get; }
private ICouchPotatoApi CpApi { get; } private ICouchPotatoApi CpApi { get; }
private IRepository<LogEntity> LogsRepo { get; } private IRepository<LogEntity> LogsRepo { get; }
private INotificationService NotificationService { get; }
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
public AdminModule(ISettingsService<PlexRequestSettings> rpService, public AdminModule(ISettingsService<PlexRequestSettings> rpService,
@ -85,7 +86,8 @@ namespace PlexRequests.UI.Modules
ICouchPotatoApi cpApi, ICouchPotatoApi cpApi,
ISettingsService<PushoverNotificationSettings> pushoverSettings, ISettingsService<PushoverNotificationSettings> pushoverSettings,
IPushoverApi pushoverApi, IPushoverApi pushoverApi,
IRepository<LogEntity> logsRepo) : base("admin") IRepository<LogEntity> logsRepo,
INotificationService notify) : base("admin")
{ {
RpService = rpService; RpService = rpService;
CpService = cpService; CpService = cpService;
@ -102,6 +104,7 @@ namespace PlexRequests.UI.Modules
LogsRepo = logsRepo; LogsRepo = logsRepo;
PushoverService = pushoverSettings; PushoverService = pushoverSettings;
PushoverApi = pushoverApi; PushoverApi = pushoverApi;
NotificationService = notify;
#if !DEBUG #if !DEBUG
this.RequiresAuthentication(); this.RequiresAuthentication();

View file

@ -26,7 +26,6 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Humanizer; using Humanizer;
@ -35,9 +34,9 @@ using Nancy;
using Nancy.Responses.Negotiation; using Nancy.Responses.Negotiation;
using Nancy.Security; using Nancy.Security;
using PlexRequests.Api;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Notification; using PlexRequests.Services.Notification;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.UI.Models; using PlexRequests.UI.Models;
@ -47,11 +46,12 @@ namespace PlexRequests.UI.Modules
public class RequestsModule : BaseModule public class RequestsModule : BaseModule
{ {
public RequestsModule(IRequestService service, ISettingsService<PlexRequestSettings> prSettings, ISettingsService<PlexSettings> plex) : base("requests") public RequestsModule(IRequestService service, ISettingsService<PlexRequestSettings> prSettings, ISettingsService<PlexSettings> plex, INotificationService notify) : base("requests")
{ {
Service = service; Service = service;
PrSettings = prSettings; PrSettings = prSettings;
PlexSettings = plex; PlexSettings = plex;
NotificationService = notify;
Get["/"] = _ => LoadRequests(); Get["/"] = _ => LoadRequests();
Get["/movies"] = _ => GetMovies(); Get["/movies"] = _ => GetMovies();
@ -67,6 +67,7 @@ namespace PlexRequests.UI.Modules
} }
private IRequestService Service { get; } private IRequestService Service { get; }
private INotificationService NotificationService { get; }
private ISettingsService<PlexRequestSettings> PrSettings { get; } private ISettingsService<PlexRequestSettings> PrSettings { get; }
private ISettingsService<PlexSettings> PlexSettings { get; } private ISettingsService<PlexSettings> PlexSettings { get; }

View file

@ -53,7 +53,8 @@ namespace PlexRequests.UI.Modules
public SearchModule(ICacheProvider cache, ISettingsService<CouchPotatoSettings> cpSettings, public SearchModule(ICacheProvider cache, ISettingsService<CouchPotatoSettings> cpSettings,
ISettingsService<PlexRequestSettings> prSettings, IAvailabilityChecker checker, ISettingsService<PlexRequestSettings> prSettings, IAvailabilityChecker checker,
IRequestService request, ISonarrApi sonarrApi, ISettingsService<SonarrSettings> sonarrSettings, IRequestService request, ISonarrApi sonarrApi, ISettingsService<SonarrSettings> sonarrSettings,
ISettingsService<SickRageSettings> sickRageService, ICouchPotatoApi cpApi, ISickRageApi srApi) : base("search") ISettingsService<SickRageSettings> sickRageService, ICouchPotatoApi cpApi, ISickRageApi srApi,
INotificationService notify) : base("search")
{ {
CpService = cpSettings; CpService = cpSettings;
PrService = prSettings; PrService = prSettings;
@ -67,6 +68,7 @@ namespace PlexRequests.UI.Modules
CouchPotatoApi = cpApi; CouchPotatoApi = cpApi;
SickRageService = sickRageService; SickRageService = sickRageService;
SickrageApi = srApi; SickrageApi = srApi;
NotificationService = notify;
Get["/"] = parameters => RequestLoad(); Get["/"] = parameters => RequestLoad();
@ -80,6 +82,7 @@ namespace PlexRequests.UI.Modules
Post["request/tv"] = parameters => RequestTvShow((int)Request.Form.tvId, (bool)Request.Form.latest); Post["request/tv"] = parameters => RequestTvShow((int)Request.Form.tvId, (bool)Request.Form.latest);
} }
private TheMovieDbApi MovieApi { get; } private TheMovieDbApi MovieApi { get; }
private INotificationService NotificationService { get; }
private ICouchPotatoApi CouchPotatoApi { get; } private ICouchPotatoApi CouchPotatoApi { get; }
private ISonarrApi SonarrApi { get; } private ISonarrApi SonarrApi { get; }
private TheTvDbApi TvApi { get; } private TheTvDbApi TvApi { get; }