From 1cf9b1f9deb5f90e4f6cec201d512d72abeddbbc Mon Sep 17 00:00:00 2001 From: Nguyen Quang Chien <87883163+ChienNQuang@users.noreply.github.com> Date: Sun, 30 Jul 2023 11:04:51 +0700 Subject: [PATCH] Update/mail (#390) * xxx * xyz * ehehe --- src/Api/appsettings.Development.json | 6 +- .../Borrows/Commands/BorrowDocument.cs | 3 + .../Common/Interfaces/IMailService.cs | 4 +- src/Application/Common/Models/HtmlMailData.cs | 42 ++++++++- .../EventHandlers/RequestCreatedHandler.cs | 37 ++++++++ .../Entries/Commands/ShareEntry.cs | 10 +++ .../EventHandlers/ShareEntryEventHandler.cs | 21 +++++ .../EventHandlers/UserCreatedEventHandler.cs | 2 +- src/Domain/Events/RequestCreated.cs | 24 +++++ src/Domain/Events/ShareEntryEvent.cs | 25 ++++++ src/Infrastructure/ConfigureServices.cs | 2 +- src/Infrastructure/Services/MailService.cs | 90 ++++++++++++++++++- src/Infrastructure/Shared/MailSettings.cs | 10 ++- .../CustomMailService.cs | 14 ++- 14 files changed, 277 insertions(+), 13 deletions(-) create mode 100644 src/Application/Documents/EventHandlers/RequestCreatedHandler.cs create mode 100644 src/Application/Entries/EventHandlers/ShareEntryEventHandler.cs create mode 100644 src/Domain/Events/RequestCreated.cs create mode 100644 src/Domain/Events/ShareEntryEvent.cs diff --git a/src/Api/appsettings.Development.json b/src/Api/appsettings.Development.json index b547eab4..7d9b8098 100644 --- a/src/Api/appsettings.Development.json +++ b/src/Api/appsettings.Development.json @@ -18,7 +18,11 @@ "Token": "745f040659edff0ce87b545567da72d2", "SenderName": "ProFile", "SenderEmail": "profile@ezarp.dev", - "TemplateUuid": "9d6a8f25-65e9-4819-be7d-106ce077acf1" + "TemplateUuids": { + "ResetPassword": "9d6a8f25-65e9-4819-be7d-106ce077acf1", + "ShareEntry": "ad69df89-885a-48fb-b8f6-6d06af1a54e3", + "Request": "bce7e60c-d848-4f80-af96-cccd264dcc32" + } }, "Seed": true, "Serilog" : { diff --git a/src/Application/Borrows/Commands/BorrowDocument.cs b/src/Application/Borrows/Commands/BorrowDocument.cs index 4ef3dbaf..79a7de68 100644 --- a/src/Application/Borrows/Commands/BorrowDocument.cs +++ b/src/Application/Borrows/Commands/BorrowDocument.cs @@ -157,7 +157,10 @@ is BorrowRequestStatus.Approved entity.Status = BorrowRequestStatus.Approved; } + var result = await _context.Borrows.AddAsync(entity, cancellationToken); + entity.AddDomainEvent(new RequestCreated($"{user.FirstName} {user.LastName}", "borrow request", "borrow", + document.Title, entity.Id, request.BorrowReason, document.Id)); await _context.SaveChangesAsync(cancellationToken); using (Logging.PushProperties("Request", document.Id, user.Id)) { diff --git a/src/Application/Common/Interfaces/IMailService.cs b/src/Application/Common/Interfaces/IMailService.cs index 1da7a46d..d02b4ae6 100644 --- a/src/Application/Common/Interfaces/IMailService.cs +++ b/src/Application/Common/Interfaces/IMailService.cs @@ -4,5 +4,7 @@ namespace Application.Common.Interfaces; public interface IMailService { - bool SendResetPasswordHtmlMail(string userEmail, string temporaryPassword); + bool SendResetPasswordHtmlMail(string userEmail, string temporaryPassword, string tokenHash); + bool SendShareEntryHtmlMail(bool isDirectory, string name, string sharerName, string operation, string ownerName, string email, string path); + bool SendCreateRequestHtmlMail(string userName, string requestType, string operation, string documentName, string reason, Guid documentId, string email); } \ No newline at end of file diff --git a/src/Application/Common/Models/HtmlMailData.cs b/src/Application/Common/Models/HtmlMailData.cs index 9fa0959b..105953a9 100644 --- a/src/Application/Common/Models/HtmlMailData.cs +++ b/src/Application/Common/Models/HtmlMailData.cs @@ -11,7 +11,7 @@ public class HtmlMailData [JsonPropertyName("template_uuid")] public string TemplateUuid { get; set; } [JsonPropertyName("template_variables")] - public TemplateVariables TemplateVariables { get; set; } + public object TemplateVariables { get; set; } } public class From @@ -28,12 +28,46 @@ public class To public string Email { get; set; } } -public class TemplateVariables +public class ResetPasswordTemplateVariables { [JsonPropertyName("user_email")] public string UserEmail { get; set; } - [JsonPropertyName("reset_password_token_hash")] - public string ResetPasswordTokenHash { get; set; } + [JsonPropertyName("token_hash")] + public string TokenHash { get; set; } [JsonPropertyName("user_password")] public string UserPassword { get; set; } +} + +public class ShareEntryTemplateVariables +{ + [JsonPropertyName("entry_type")] + public string EntryType { get; set; } + [JsonPropertyName("entry_name")] + public string EntryName { get; set; } + [JsonPropertyName("sharer_name")] + public string SharerName { get; set; } + [JsonPropertyName("operation")] + public string Operation { get; set; } + [JsonPropertyName("owner_name")] + public string OwnerName { get; set; } + [JsonPropertyName("path")] + public string Path { get; set; } +} + +public class CreateRequestTemplateVariables +{ + [JsonPropertyName("user_name")] + public string UserName { get; set; } + [JsonPropertyName("request_type")] + public string RequestType { get; set; } + [JsonPropertyName("operation")] + public string Operation { get; set; } + [JsonPropertyName("document_name")] + public string DocumentName { get; set; } + [JsonPropertyName("reason")] + public string Reason { get; set; } + [JsonPropertyName("path")] + public string Path { get; set; } + [JsonPropertyName("id")] + public string Id { get; set; } } \ No newline at end of file diff --git a/src/Application/Documents/EventHandlers/RequestCreatedHandler.cs b/src/Application/Documents/EventHandlers/RequestCreatedHandler.cs new file mode 100644 index 00000000..fe2cc6ea --- /dev/null +++ b/src/Application/Documents/EventHandlers/RequestCreatedHandler.cs @@ -0,0 +1,37 @@ +using Application.Common.Interfaces; +using Domain.Events; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Application.Documents.EventHandlers; + +public class RequestCreatedHandler : INotificationHandler +{ + private readonly IApplicationDbContext _context; + private readonly IMailService _mailService; + + public RequestCreatedHandler(IMailService mailService, IApplicationDbContext context) + { + _mailService = mailService; + _context = context; + } + + public async Task Handle(RequestCreated notification, CancellationToken cancellationToken) + { + var document = await _context.Documents + .Include(x => x.Department) + .FirstOrDefaultAsync(x => x.Id == notification.DocumentId, cancellationToken); + + var departmentId = document!.Department!.Id; + + var staff = await _context.Staffs + .Include(x => x.User) + .FirstOrDefaultAsync(x => x.Room!.DepartmentId == departmentId, cancellationToken); + + if (staff is not null) + { + _mailService.SendCreateRequestHtmlMail(notification.UserName, notification.RequestType, notification.Operation, + notification.DocumentTitle, notification.Reason, notification.RequestId, staff.User.Email); + } + } +} \ No newline at end of file diff --git a/src/Application/Entries/Commands/ShareEntry.cs b/src/Application/Entries/Commands/ShareEntry.cs index 320454b6..5ca36e64 100644 --- a/src/Application/Entries/Commands/ShareEntry.cs +++ b/src/Application/Entries/Commands/ShareEntry.cs @@ -7,6 +7,7 @@ using AutoMapper; using Domain.Entities; using Domain.Entities.Digital; +using Domain.Events; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -103,6 +104,15 @@ public async Task Handle(Command request, CancellationToken await GrantOrRevokePermission(childEntry, user, childAllowOperations, request.ExpiryDate, false, cancellationToken); } } + + if (request.CanView) + { + var p = "view"; + if (request.CanEdit) p = "edit"; + entry.AddDomainEvent(new ShareEntryEvent(entry.Name, + request.CurrentUser.FirstName + request.CurrentUser.LastName, + entry.Owner.FirstName + entry.Owner.LastName, user.Email, entry.IsDirectory, p, entry.Id.ToString())); + } await _context.SaveChangesAsync(cancellationToken); using (Logging.PushProperties(nameof(Entry), entry.Id, request.CurrentUser.Id)) { diff --git a/src/Application/Entries/EventHandlers/ShareEntryEventHandler.cs b/src/Application/Entries/EventHandlers/ShareEntryEventHandler.cs new file mode 100644 index 00000000..eeb2a243 --- /dev/null +++ b/src/Application/Entries/EventHandlers/ShareEntryEventHandler.cs @@ -0,0 +1,21 @@ +using Application.Common.Interfaces; +using Domain.Events; +using MediatR; + +namespace Application.Entries.EventHandlers; + +public class ShareEntryEventHandler : INotificationHandler +{ + private readonly IMailService _mailService; + + public ShareEntryEventHandler(IMailService mailService) + { + _mailService = mailService; + } + + public async Task Handle(ShareEntryEvent notification, CancellationToken cancellationToken) + { + _mailService.SendShareEntryHtmlMail(notification.IsDirectory, notification.EntryName, notification.SharerName, + notification.Operation, notification.OwnerName, notification.SharedUserEmail, notification.Path); + } +} \ No newline at end of file diff --git a/src/Application/Users/EventHandlers/UserCreatedEventHandler.cs b/src/Application/Users/EventHandlers/UserCreatedEventHandler.cs index 17538775..c0aed008 100644 --- a/src/Application/Users/EventHandlers/UserCreatedEventHandler.cs +++ b/src/Application/Users/EventHandlers/UserCreatedEventHandler.cs @@ -35,6 +35,6 @@ public async Task Handle(UserCreatedEvent notification, CancellationToken cancel await _authDbContext.ResetPasswordTokens.AddAsync(resetPasswordToken, cancellationToken); await _authDbContext.SaveChangesAsync(cancellationToken); - _mailService.SendResetPasswordHtmlMail(notification.User.Email, notification.Password); + _mailService.SendResetPasswordHtmlMail(notification.User.Email, notification.Password, resetPasswordToken.TokenHash); } } \ No newline at end of file diff --git a/src/Domain/Events/RequestCreated.cs b/src/Domain/Events/RequestCreated.cs new file mode 100644 index 00000000..81276057 --- /dev/null +++ b/src/Domain/Events/RequestCreated.cs @@ -0,0 +1,24 @@ +using Domain.Common; + +namespace Domain.Events; + +public class RequestCreated : BaseEvent +{ + public RequestCreated(string userName, string requestType, string operation, string documentTitle, Guid requestId, string reason, Guid documentId) + { + UserName = userName; + RequestType = requestType; + Operation = operation; + DocumentTitle = documentTitle; + RequestId = requestId; + Reason = reason; + DocumentId = documentId; + } + public string UserName { get; } + public string RequestType { get; } + public string Operation { get; } + public string DocumentTitle { get; } + public string Reason { get; } + public Guid DocumentId { get; } + public Guid RequestId { get; } +} \ No newline at end of file diff --git a/src/Domain/Events/ShareEntryEvent.cs b/src/Domain/Events/ShareEntryEvent.cs new file mode 100644 index 00000000..0e2f4a2f --- /dev/null +++ b/src/Domain/Events/ShareEntryEvent.cs @@ -0,0 +1,25 @@ +using Domain.Common; + +namespace Domain.Events; + +public class ShareEntryEvent : BaseEvent +{ + public ShareEntryEvent(string entryName, string sharerName, string ownerName, string sharedUserEmail, bool isDirectory, string operation, string path) + { + EntryName = entryName; + SharerName = sharerName; + OwnerName = ownerName; + SharedUserEmail = sharedUserEmail; + IsDirectory = isDirectory; + Operation = operation; + Path = path; + } + + public string EntryName { get; } + public string SharerName { get; } + public string OwnerName { get; } + public string SharedUserEmail { get; } + public bool IsDirectory { get; } + public string Operation { get; } + public string Path { get; } +} \ No newline at end of file diff --git a/src/Infrastructure/ConfigureServices.cs b/src/Infrastructure/ConfigureServices.cs index 710b935b..dfade6b9 100644 --- a/src/Infrastructure/ConfigureServices.cs +++ b/src/Infrastructure/ConfigureServices.cs @@ -108,7 +108,7 @@ private static IServiceCollection AddMailService(this IServiceCollection service options.Token = mailSettings!.Token; options.SenderEmail = mailSettings!.SenderEmail; options.SenderName = mailSettings!.SenderName; - options.TemplateUuid = mailSettings!.TemplateUuid; + options.TemplateUuids = mailSettings!.TemplateUuids; }); services.AddTransient(); diff --git a/src/Infrastructure/Services/MailService.cs b/src/Infrastructure/Services/MailService.cs index 62faf977..dc045777 100644 --- a/src/Infrastructure/Services/MailService.cs +++ b/src/Infrastructure/Services/MailService.cs @@ -17,7 +17,7 @@ public MailService(IOptions mailSettingsOptions) _mailSettings = mailSettingsOptions.Value; } - public bool SendResetPasswordHtmlMail(string userEmail, string temporaryPassword) + public bool SendResetPasswordHtmlMail(string userEmail, string temporaryPassword, string tokenHash) { var data = new HtmlMailData() { @@ -30,11 +30,95 @@ public bool SendResetPasswordHtmlMail(string userEmail, string temporaryPassword { new (){ Email = userEmail }, }, - TemplateUuid = _mailSettings.TemplateUuid, - TemplateVariables = new TemplateVariables() + TemplateUuid = _mailSettings.TemplateUuids.ResetPassword, + TemplateVariables = new ResetPasswordTemplateVariables() { UserEmail = userEmail, UserPassword = temporaryPassword, + TokenHash = tokenHash + }, + }; + + var json = JsonSerializer.Serialize(data, new JsonSerializerOptions(JsonSerializerDefaults.Web)); + + var client = new RestClient(_mailSettings.ClientUrl); + var request = new RestRequest + { + Method = Method.Post + }; + + request.AddHeader("Authorization", $"{JwtBearerDefaults.AuthenticationScheme} {_mailSettings.Token}"); + request.AddHeader("Content-Type", "application/json"); + request.AddParameter("application/json", json, ParameterType.RequestBody); + var response = client.Execute(request); + return response.IsSuccessStatusCode; + } + + public bool SendShareEntryHtmlMail(bool isDirectory, string name, string sharerName, string operation, string ownerName, + string email, string path) + { + var data = new HtmlMailData() + { + From = new From() + { + Email = _mailSettings.SenderEmail, + Name = _mailSettings.SenderName, + }, + To = new To[] + { + new (){ Email = email }, + }, + TemplateUuid = _mailSettings.TemplateUuids.ShareEntry, + TemplateVariables = new ShareEntryTemplateVariables() + { + EntryName = name, + Operation = operation, + EntryType = isDirectory ? "Folder" : "File", + OwnerName = ownerName, + SharerName = sharerName, + Path = path + }, + }; + + var json = JsonSerializer.Serialize(data, new JsonSerializerOptions(JsonSerializerDefaults.Web)); + + var client = new RestClient(_mailSettings.ClientUrl); + var request = new RestRequest + { + Method = Method.Post + }; + + request.AddHeader("Authorization", $"{JwtBearerDefaults.AuthenticationScheme} {_mailSettings.Token}"); + request.AddHeader("Content-Type", "application/json"); + request.AddParameter("application/json", json, ParameterType.RequestBody); + var response = client.Execute(request); + return response.IsSuccessStatusCode; + } + + public bool SendCreateRequestHtmlMail(string userName, string requestType, string operation, string documentName, + string reason, Guid documentId, string email) + { + var data = new HtmlMailData() + { + From = new From() + { + Email = _mailSettings.SenderEmail, + Name = _mailSettings.SenderName, + }, + To = new To[] + { + new (){ Email = email }, + }, + TemplateUuid = _mailSettings.TemplateUuids.Request, + TemplateVariables = new CreateRequestTemplateVariables() + { + Operation = operation, + Reason = reason, + DocumentName = documentName, + UserName = userName, + Id = documentId.ToString(), + RequestType = requestType, + Path = !requestType.Equals("borrow request") ? "import/manage" : "requests" }, }; diff --git a/src/Infrastructure/Shared/MailSettings.cs b/src/Infrastructure/Shared/MailSettings.cs index 2e139992..da97ad6b 100644 --- a/src/Infrastructure/Shared/MailSettings.cs +++ b/src/Infrastructure/Shared/MailSettings.cs @@ -6,5 +6,13 @@ public class MailSettings public string Token { get; set; } public string SenderName { get; set; } public string SenderEmail { get; set; } - public string TemplateUuid { get; set; } + + public Template TemplateUuids { get; set; } +} + +public class Template +{ + public string ResetPassword { get; set; } + public string ShareEntry { get; set; } + public string Request { get; set; } } \ No newline at end of file diff --git a/tests/Application.Tests.Integration/CustomMailService.cs b/tests/Application.Tests.Integration/CustomMailService.cs index aaeabc16..3327efea 100644 --- a/tests/Application.Tests.Integration/CustomMailService.cs +++ b/tests/Application.Tests.Integration/CustomMailService.cs @@ -4,7 +4,19 @@ namespace Application.Tests.Integration; public class CustomMailService : IMailService { - public bool SendResetPasswordHtmlMail(string userEmail, string temporaryPassword) + public bool SendResetPasswordHtmlMail(string userEmail, string temporaryPassword, string tokenHash) + { + return true; + } + + public bool SendShareEntryHtmlMail(bool isDirectory, string name, string sharerName, string operation, string ownerName, + string email, string path) + { + return true; + } + + public bool SendCreateRequestHtmlMail(string userName, string requestType, string operation, string documentName, + string reason, Guid documentId, string email) { return true; }