From 5e00b6e9ec0a055d1d257d345be900abaf94c036 Mon Sep 17 00:00:00 2001 From: TekH Date: Thu, 29 Oct 2026 15:54:07 +0100 Subject: [PATCH] Refactor notification and email handling Refactored `DocumentReceivedNotification` to use new properties `Origin`, `EnvelopeUuid`, and `ReceiverSignature` for constructing `EnvelopeURL`. Updated `LinkToDocument` and `LinkToDocumentText` to derive from `EnvelopeURL`. Simplified `CreateEnvelopeCommandHandler` with record-like syntax, removing private fields and directly injecting dependencies. Renamed and refactored `QueueInvitationEmailsAsync` to `SendInvitationEmailsAsync`, using `EnvNtfService.Notify` for email notifications, centralizing logic and improving clarity. Improved error handling in email sending with logging. Updated variable names for clarity and cleaned up `using` directives. --- .../DocumentReceivedNotification.cs | 31 +++- .../Commands/CreateEnvelopeCommandHandler.cs | 148 +++++++----------- 2 files changed, 88 insertions(+), 91 deletions(-) diff --git a/EnvelopeGenerator.Application/Common/Interfaces/Services/Notification/DocumentReceivedNotification.cs b/EnvelopeGenerator.Application/Common/Interfaces/Services/Notification/DocumentReceivedNotification.cs index 7ca460ab..f88eb754 100644 --- a/EnvelopeGenerator.Application/Common/Interfaces/Services/Notification/DocumentReceivedNotification.cs +++ b/EnvelopeGenerator.Application/Common/Interfaces/Services/Notification/DocumentReceivedNotification.cs @@ -1,4 +1,5 @@ -using EnvelopeGenerator.Domain.Constants; +using EnvelopeGenerator.Application.Common.Extensions; +using EnvelopeGenerator.Domain.Constants; namespace EnvelopeGenerator.Application.Common.Interfaces.Services.Notification; @@ -45,12 +46,36 @@ public record DocumentReceivedNotification : EnvelopeNotificationBase /// /// /// - public required string LinkToDocument { get; init; } + public required Uri Origin { private get; init; } /// /// /// - public string LinkToDocumentText => LinkToDocument[..Math.Min(LinkToDocument.Length, 40)]; + public required string EnvelopeUuid { private get; init; } + + /// + /// + /// + public required string ReceiverSignature { private get; init; } + + private Uri EnvelopeURL => new(Origin, $"Envelope/{(EnvelopeUuid, ReceiverSignature).ToEnvelopeKey()}"); + + /// + /// + /// + public string LinkToDocument => EnvelopeURL.AbsoluteUri; + + /// + /// + /// + public string LinkToDocumentText + { + get + { + var urlText = EnvelopeURL.ToString(); + return $"{urlText[..Math.Min(urlText.Length, 40)]}.."; + } + } /// /// diff --git a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs index 33134c80..cb444ae1 100644 --- a/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs +++ b/EnvelopeGenerator.Application/Envelopes/Commands/CreateEnvelopeCommandHandler.cs @@ -1,71 +1,66 @@ using AutoMapper; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using DigitalData.Core.Abstraction.Application.Repository; -using DigitalData.EmailProfilerDispatcher.Abstraction.Entities; using DigitalData.Core.Exceptions; -using EnvelopeGenerator.Domain.Entities; -using EnvelopeGenerator.Application.Common.Dto; +using DigitalData.EmailProfilerDispatcher.Abstraction.Entities; +using DigitalData.UserManager.Domain.Entities; using EnvelopeGenerator.Application.Common.Configurations; +using EnvelopeGenerator.Application.Common.Dto; using EnvelopeGenerator.Application.Common.Extensions; +using EnvelopeGenerator.Application.Common.Interfaces; using EnvelopeGenerator.Application.Common.Interfaces.Services; +using EnvelopeGenerator.Application.Common.Interfaces.Services.Notification; using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor; using EnvelopeGenerator.Application.Histories.Commands; -using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain; +using EnvelopeGenerator.Domain.Constants; +using EnvelopeGenerator.Domain.Entities; +using EnvelopeGenerator.Domain.Interfaces; +using MediatR; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace EnvelopeGenerator.Application.Envelopes.Commands; /// /// /// -public class CreateEnvelopeCommandHandler : IRequestHandler +/// +/// +/// +/// +/// +public class CreateEnvelopeCommandHandler(IServiceProvider Provider, IMapper Mapper, IEnvelopeNotificationService EnvNtfService, IRequestContext ReqCntxt, ILogger Logger) : IRequestHandler { - private readonly IServiceProvider _provider; + private IEnvelopeExecutor Executor => Provider.GetRequiredService(); - private IEnvelopeExecutor Executor => _provider.GetRequiredService(); + private IRepository Repository => Provider.GetRequiredService>(); - private IRepository Repository => _provider.GetRequiredService>(); + private IRepository DocumentRepository => Provider.GetRequiredService>(); - private IRepository DocumentRepository => _provider.GetRequiredService>(); + private IRepository EnvelopeReceiverRepository => Provider.GetRequiredService>(); - private IRepository EnvelopeReceiverRepository => _provider.GetRequiredService>(); + private IRepository EnvelopeTypeRepository => Provider.GetRequiredService>(); - private IRepository EnvelopeTypeRepository => _provider.GetRequiredService>(); + private IRepository ReceiverRepository => Provider.GetRequiredService>(); - private IRepository ReceiverRepository => _provider.GetRequiredService>(); + private IRepository DocReceiverElementRepository => Provider.GetRequiredService>(); - private IRepository DocReceiverElementRepository => _provider.GetRequiredService>(); + private IRepository EmailTemplateRepository => Provider.GetRequiredService>(); - private IRepository EmailTemplateRepository => _provider.GetRequiredService>(); + private IRepository EmailOutRepository => Provider.GetRequiredService>(); - private IRepository EmailOutRepository => _provider.GetRequiredService>(); + private MailParams MailParams => Provider.GetRequiredService>().Value; - private MailParams MailParams => _provider.GetRequiredService>().Value; + private DispatcherParams DispatcherParams => Provider.GetRequiredService>().Value; - private DispatcherParams DispatcherParams => _provider.GetRequiredService>().Value; - - private SignatureFieldParams SignatureFieldParams => _provider.GetRequiredService>().CurrentValue; + private SignatureFieldParams SignatureFieldParams => Provider.GetRequiredService>().CurrentValue; [Obsolete("Use IConfigService instead.")] - private IConfigService ConfigService => _provider.GetRequiredService(); + private IConfigService ConfigService => Provider.GetRequiredService(); - private IMediator Mediator => _provider.GetRequiredService(); - - private readonly IMapper _mapper; - - /// - /// - /// - /// - /// - public CreateEnvelopeCommandHandler(IServiceProvider provider, IMapper mapper) - { - _provider = provider; - _mapper = mapper; - } + private IMediator Mediator => Provider.GetRequiredService(); /// /// @@ -233,7 +228,7 @@ public class CreateEnvelopeCommandHandler : IRequestHandler er.Receiver) .FirstAsync(e => e.Id == envelope.Id, cancel); - return _mapper.Map(envelope); + return Mapper.Map(envelope); } private async Task ResolveEnvelopeSettingsAsync(CreateEnvelopeCommand request, Envelope? existing, CancellationToken cancel) @@ -532,15 +527,15 @@ public class CreateEnvelopeCommandHandler : IRequestHandler er.Receiver) .Where(er => er.EnvelopeId == envelope.Id) .ToListAsync(cancel); - if (links.Count == 0) + if (envRcvs.Count == 0) throw new BadRequestException("Cannot send envelope without receivers."); - var receiverIds = links.Select(l => l.ReceiverId).Distinct().ToList(); + var receiverIds = envRcvs.Select(l => l.ReceiverId).Distinct().ToList(); var documentIds = await DocumentRepository.Query .Where(d => d.EnvelopeId == envelope.Id) .Select(d => d.Id) @@ -553,7 +548,7 @@ public class CreateEnvelopeCommandHandler : IRequestHandler new { ReceiverId = g.Key, Count = g.Count() }) .ToDictionaryAsync(x => x.ReceiverId, x => x.Count, cancel); - var receiversWithoutFields = links + var receiversWithoutFields = envRcvs .Where(link => !receiverFieldCounts.TryGetValue(link.ReceiverId, out var count) || count <= 0) .Select(link => link.Name) .ToList(); @@ -577,62 +572,39 @@ public class CreateEnvelopeCommandHandler : IRequestHandler links, - string senderReference, + IReadOnlyCollection envRcvs, CancellationToken cancel) { - var template = await EmailTemplateRepository.Query - .SingleOrDefaultAsync(x => x.Name == EmailTemplateType.DocumentReceived.ToString(), cancel) - ?? throw new InvalidOperationException($"Email template not found: {EmailTemplateType.DocumentReceived}"); - - var signatureHost = await ConfigService.ReadDefaultSignatureHost(); - var senderName = senderReference; - - foreach (var link in links) + foreach (var er in envRcvs) { - var receiver = link.Receiver; - if (receiver is null || string.IsNullOrWhiteSpace(receiver.EmailAddress) || string.IsNullOrWhiteSpace(receiver.Signature)) - continue; - - var envelopeKey = (envelope.Uuid, receiver.Signature).ToEnvelopeKey(); - var documentLink = $"{signatureHost.TrimEnd('/')}/envelope/login/{Uri.EscapeDataString(envelopeKey)}"; - - var placeholders = new Dictionary(MailParams.Placeholders) + var advancedSignature = envelope.CertificationType == CertificationType.AdvancedElectronicSignature; + var origin = new Uri(ReqCntxt.Origin ?? throw new InvalidOperationException($"The request origin is missing in {nameof(CreateEnvelopeCommandHandler)}.")); + try { - ["[NAME_RECEIVER]"] = link.Name ?? string.Empty, - ["[NAME_SENDER]"] = senderName ?? string.Empty, - ["[DOCUMENT_TITLE]"] = envelope.Title ?? string.Empty, - ["[MESSAGE]"] = envelope.Message ?? string.Empty, - ["[LINK_TO_DOCUMENT]"] = documentLink, - ["[LINK_TO_DOCUMENT_TEXT]"] = documentLink[..Math.Min(40, documentLink.Length)] + ".." - }; - - var subject = ReplacePlaceHolders(template.Subject, placeholders); - var body = ReplacePlaceHolders(template.Body, placeholders); - - var emailOut = new EmailOut + await EnvNtfService.Notify(er.Receiver.EmailAddress, new DocumentReceivedNotification + { + AdvancedSignature = advancedSignature, + DocumentTitle = envelope.Title ?? string.Empty, + NameReceiver = er.Name ?? string.Empty, + EnvelopeUuid = envelope.Uuid, + IsReadAndConfirm = envelope.IsReadAndConfirm(), + Message = envelope.Message ?? string.Empty, + NameSender = $"{envelope.User.Prename} {envelope.User.Name}", + Origin = origin, + ReceiverSignature = er.Receiver.Signature, + }, cancel); + } + catch(Exception ex) { - EmailAddress = receiver.EmailAddress, - EmailSubj = subject, - EmailBody = body, - AddedWhen = DateTime.Now, - AddedWho = DispatcherParams.AddedWho, - SendingProfile = DispatcherParams.SendingProfile, - ReminderTypeId = DispatcherParams.ReminderTypeId, - EmailAttmt1 = DispatcherParams.EmailAttmt1, - WfId = (int)EnvelopeStatus.MessageInvitationSent, - ReferenceString = receiver.EmailAddress, - ReferenceId = link.ReceiverId - }; - - await EmailOutRepository.CreateAsync(emailOut, cancel); + Logger.LogError(ex, "Failed to send invitation email for receiver {emailAddress} in envelope {envelopeId}.", er.Receiver.EmailAddress, envelope.Id); + } } }