diff --git a/AsbCloudApp/Services/Notifications/NotificationService.cs b/AsbCloudApp/Services/Notifications/NotificationService.cs index 4537f04e..d0414213 100644 --- a/AsbCloudApp/Services/Notifications/NotificationService.cs +++ b/AsbCloudApp/Services/Notifications/NotificationService.cs @@ -61,6 +61,9 @@ public class NotificationService var notificationTransportService = GetTransportService(request.IdTransportType); await notificationTransportService.SendAsync(notification, cancellationToken); + + notification.SentDate = DateTime.UtcNow; + await notificationRepository.UpdateAsync(notification, cancellationToken); } /// @@ -104,6 +107,14 @@ public class NotificationService await notificationTransportService.SendRangeAsync(notifications, cancellationToken); + + var tasks = notifications.Select(notification => + { + notification.SentDate = DateTime.UtcNow; + return notificationRepository.UpdateAsync(notification, cancellationToken); + }); + + await Task.WhenAll(tasks); } private INotificationTransportService GetTransportService(int idTransportType) diff --git a/AsbCloudInfrastructure/Background/WorkToSendEmail.cs b/AsbCloudInfrastructure/Background/WorkToSendEmail.cs index 96136cde..57605438 100644 --- a/AsbCloudInfrastructure/Background/WorkToSendEmail.cs +++ b/AsbCloudInfrastructure/Background/WorkToSendEmail.cs @@ -1,11 +1,8 @@ using AsbCloudApp.Data; -using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; -using Microsoft.Extensions.Configuration; +using AsbCloudApp.Services.Notifications; using Microsoft.Extensions.DependencyInjection; using System; -using System.Diagnostics; -using System.Net.Mail; using System.Threading; using System.Threading.Tasks; @@ -17,69 +14,22 @@ namespace AsbCloudInfrastructure.Background internal class WorkToSendEmail : Work { private NotificationDto notification; - private string sender; - private string smtpPassword; - private string smtpServer; - private bool IsConfigured; - public WorkToSendEmail(NotificationDto notification, IConfiguration configuration) : base(MakeWorkId(notification)) + public WorkToSendEmail(NotificationDto notification) : base(MakeWorkId(notification)) { this.notification = notification; - - sender = configuration.GetValue("email:sender", string.Empty); - smtpPassword = configuration.GetValue("email:password", string.Empty); - smtpServer = configuration.GetValue("email:smtpServer", string.Empty); - - var configError = string.IsNullOrEmpty(sender) || - string.IsNullOrEmpty(smtpPassword) || - string.IsNullOrEmpty(smtpServer); - - IsConfigured = !configError; } - protected override async Task Action(string id, IServiceProvider services, Action onProgressCallback, CancellationToken token) { - if (!IsConfigured) - { - Trace.TraceWarning("smtp is not configured"); - return; - } - + var notificationService = services.GetRequiredService(); var notificationRepository = services.GetRequiredService(); - var userRepository = services.GetRequiredService(); - var user = await userRepository.GetOrDefaultAsync(notification.IdUser, token) - ?? throw new ArgumentInvalidException(nameof(notification.IdUser), "Пользователь не найден"); + await notificationService.SendAsync(notification, token); - if (!MailAddress.TryCreate(user.Email, out var mailAddress)) - { - Trace.TraceWarning($"Mail {user.Email} is not correct."); - throw new ArgumentInvalidException(nameof(user.Email), $"Mail {user.Email} is not null."); - } - - var from = new MailAddress(sender); - var message = new MailMessage - { - From = from - }; - - message.To.Add(mailAddress.Address); - message.BodyEncoding = System.Text.Encoding.UTF8; - message.Body = notification.Message; - message.Subject = notification.Title; - message.IsBodyHtml = true; - - using var client = new SmtpClient(smtpServer); - client.EnableSsl = true; - client.UseDefaultCredentials = false; - client.Credentials = new System.Net.NetworkCredential(sender, smtpPassword); - - await client.SendMailAsync(message, token); notification.SentDate = DateTime.UtcNow; await notificationRepository.UpdateAsync(notification, token); - Trace.TraceInformation($"Send email to {user.Email} subj:{notification.Title} html body count {notification.Message.Length}"); } diff --git a/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs b/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs index f41bd7ad..2eec7b27 100644 --- a/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs +++ b/AsbCloudInfrastructure/Services/Email/EmailNotificationTransportService.cs @@ -1,9 +1,12 @@ using AsbCloudApp.Data; +using AsbCloudApp.Exceptions; +using AsbCloudApp.Repositories; using AsbCloudApp.Services.Notifications; -using AsbCloudInfrastructure.Background; using Microsoft.Extensions.Configuration; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using System.Net.Mail; using System.Threading; using System.Threading.Tasks; @@ -12,27 +15,68 @@ namespace AsbCloudInfrastructure.Services.Email public class EmailNotificationTransportService : INotificationTransportService { - private readonly IConfiguration configuration; - private readonly BackgroundWorker backgroundWorker; - - public EmailNotificationTransportService(BackgroundWorker backgroundWorker, - IConfiguration configuration) - { - this.configuration = configuration; - this.backgroundWorker = backgroundWorker; - } + private readonly IUserRepository userRepository; + private readonly string sender; + private readonly string smtpPassword; + private readonly string smtpServer; public int IdTransportType => 1; + public bool IsConfigured { get; } - public Task SendAsync(NotificationDto notification, CancellationToken cancellationToken) + public EmailNotificationTransportService( + IConfiguration configuration, + IUserRepository userRepository) { - var work = new WorkToSendEmail(notification, configuration); - if (!backgroundWorker.Works.Any(w => w.Id == work.Id)) + this.userRepository = userRepository; + + this.sender = configuration.GetValue("email:sender", string.Empty); + this.smtpPassword = configuration.GetValue("email:password", string.Empty); + this.smtpServer = configuration.GetValue("email:smtpServer", string.Empty); + + var configError = string.IsNullOrEmpty(this.sender) || + string.IsNullOrEmpty(this.smtpPassword) || + string.IsNullOrEmpty(this.smtpServer); + + this.IsConfigured = !configError; + } + + public async Task SendAsync(NotificationDto notification, CancellationToken token) + { + if (!IsConfigured) { - backgroundWorker.Enqueue(work); + Trace.TraceWarning("smtp is not configured"); + return; } - return Task.CompletedTask; + var user = await userRepository.GetOrDefaultAsync(notification.IdUser, token) + ?? throw new ArgumentInvalidException(nameof(notification.IdUser), "Пользователь не найден"); + + if (!MailAddress.TryCreate(user.Email, out var mailAddress)) + { + Trace.TraceWarning($"Mail {user.Email} is not correct."); + throw new ArgumentInvalidException(nameof(user.Email), $"Mail {user.Email} is not null."); + } + + var from = new MailAddress(sender); + var message = new MailMessage + { + From = from + }; + + message.To.Add(mailAddress.Address); + message.BodyEncoding = System.Text.Encoding.UTF8; + message.Body = notification.Message; + message.Subject = notification.Title; + message.IsBodyHtml = true; + + using var client = new SmtpClient(smtpServer); + client.EnableSsl = true; + client.UseDefaultCredentials = false; + client.Credentials = new System.Net.NetworkCredential(sender, smtpPassword); + + await client.SendMailAsync(message, token); + + Trace.TraceInformation($"Send email to {user.Email} subj:{notification.Title} html body count {notification.Message.Length}"); } public Task SendRangeAsync(IEnumerable notifications, CancellationToken cancellationToken) @@ -42,7 +86,5 @@ namespace AsbCloudInfrastructure.Services.Email return Task.WhenAll(tasks); } - - } } diff --git a/AsbCloudWebApi.Tests/UnitTests/Services/Notification/EmailNotificationTransportServiceTests.cs b/AsbCloudWebApi.Tests/UnitTests/Services/Notification/EmailNotificationTransportServiceTests.cs new file mode 100644 index 00000000..c3dc936a --- /dev/null +++ b/AsbCloudWebApi.Tests/UnitTests/Services/Notification/EmailNotificationTransportServiceTests.cs @@ -0,0 +1,72 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Data.User; +using AsbCloudApp.Repositories; +using AsbCloudApp.Services.Notifications; +using AsbCloudInfrastructure.Services.Email; +using Microsoft.Extensions.Configuration; +using NSubstitute; +using System; +using System.Collections.Generic; +using System.Net.Mail; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace AsbCloudWebApi.Tests.UnitTests.Services.Notification +{ + public class EmailNotificationTransportServiceTests + { + private IUserRepository userRepository; + private INotificationTransportService notificationTransportService; + + private readonly NotificationDto notification = new NotificationDto() + { + Message = "Message", + Title = "Title", + IdUser = 1, + IdTransportType = 1, + IdState = 0, + RegistrationDate = DateTime.Now, + IdNotificationCategory = 10000, + }; + private readonly UserExtendedDto user = new UserExtendedDto() + { + Id = 1, + IdCompany = 1, + Email = "studio@yandex.ru", + IdState = 1, + Login = "studio", + Name = "Test", + Patronymic = "Test", + Phone = "22-22-22", + Position = "Test", + Surname = "Test", + }; + + private static Dictionary configSettings = new() + { + { "email:sender", "bot@digitaldrilling.ru" }, + { "email:password", "8wZrXSfP" }, + { "email:smtpServer", "smtp.timeweb.ru" } + }; + + public EmailNotificationTransportServiceTests() + { + IConfiguration configuration = new ConfigurationBuilder() + .AddInMemoryCollection(configSettings) + .Build(); + + userRepository = Substitute.For(); + + notificationTransportService = new EmailNotificationTransportService(configuration, userRepository); + } + + [Fact] + public async Task SendAsyncThrowsMailboxUnavailable() + { + userRepository.GetOrDefaultAsync(Arg.Any(), Arg.Any()).Returns(user); + var exception = await Assert.ThrowsAsync(() => notificationTransportService.SendAsync(notification, CancellationToken.None)); + Assert.Equal(SmtpStatusCode.MailboxUnavailable, exception.StatusCode); + } + } +}