EmailDispatcher ist integriert.

This commit is contained in:
Developer 02
2024-06-12 00:40:50 +02:00
parent 0268756cf9
commit 38aa6a6217
21 changed files with 367 additions and 94 deletions

View File

@@ -10,6 +10,9 @@ namespace EnvelopeGenerator.Application.Contracts
{
Task<DataResult<ConfigDto>> ReadFirstAsync();
async Task<DataResult<ConfigDto>> ReadDefaultAsync() => await ReadFirstAsync();
}
Task<ConfigDto> ReadDefaultAsync();
Task<string> ReadDefaultSignatureHost();
}
}

View File

@@ -17,7 +17,9 @@ namespace EnvelopeGenerator.Application.Contracts
Task<DataResult<EnvelopeReceiverDto>> ReadByEnvelopeReceiverIdAsync(string envelopeReceiverId, bool withEnvelope = true, bool withReceiver = true);
Task<DataResult<bool>> VerifyAccessCodeAsync(string uuid, string signature, string accessCode);
Task<DataResult<string>> ReadAccessCodeByIdAsync(int envelopeId, int receiverId);
Task<DataResult<bool>> VerifyAccessCodeAsync(string uuid, string signature, string accessCode);
Task<DataResult<bool>> VerifyAccessCodeAsync(string envelopeReceiverId, string accessCode);

View File

@@ -1,38 +1,66 @@
using DigitalData.UserManager.Domain.Entities;
using DigitalData.EmailProfilerDispatcher.Domain.Attributes;
using DigitalData.UserManager.Application.DTOs.User;
using DigitalData.UserManager.Domain.Entities;
using EnvelopeGenerator.Application.DTOs.EnvelopeHistory;
using EnvelopeGenerator.Domain.Entities;
namespace EnvelopeGenerator.Application.DTOs
{
public record EnvelopeDto(
int Id,
int UserId,
int Status,
string Uuid,
string Message,
DateTime? ExpiresWhen,
DateTime? ExpiresWarningWhen,
DateTime AddedWhen,
DateTime? ChangedWhen,
string Title,
int? ContractType,
string Language,
bool? SendReminderEmails,
int? FirstReminderDays,
int? ReminderIntervalDays,
int? EnvelopeTypeId,
int? CertificationType,
bool? UseAccessCode,
int? FinalEmailToCreator,
int? FinalEmailToReceivers,
int? ExpiresWhenDays,
int? ExpiresWarningWhenDays,
bool DmzMoved,
User? User,
EnvelopeType? EnvelopeType,
string? EnvelopeTypeTitle,
bool IsAlreadySent,
string? StatusTranslated,
string? ContractTypeTranslated,
IEnumerable<EnvelopeDocumentDto>? Documents);
public record EnvelopeDto()
{
public int Id { get; set; }
public int UserId { get; set; }
public int Status { get; set; }
public string Uuid { get; set; }
[TemplatePlaceholder("[MESSAGE]")]
public string Message { get; set; }
public DateTime? ExpiresWhen { get; set; }
public DateTime? ExpiresWarningWhen { get; set; }
public DateTime AddedWhen { get; set; }
public DateTime? ChangedWhen { get; set; }
[TemplatePlaceholder("[DOCUMENT_TITLE]")]
public string Title { get; set; }
public int? ContractType { get; set; }
public string Language { get; set; }
public bool? SendReminderEmails { get; set; }
public int? FirstReminderDays { get; set; }
public int? ReminderIntervalDays { get; set; }
public int? EnvelopeTypeId { get; set; }
public int? CertificationType { get; set; }
public bool? UseAccessCode { get; set; }
public int? FinalEmailToCreator { get; set; }
public int? FinalEmailToReceivers { get; set; }
public int? ExpiresWhenDays { get; set; }
public int? ExpiresWarningWhenDays { get; set; }
public bool DmzMoved { get; set; }
public UserReadDto? User { get; set; }
public EnvelopeType? EnvelopeType { get; set; }
public string? EnvelopeTypeTitle { get; set; }
public bool IsAlreadySent { get; set; }
public string? StatusTranslated { get; set; }
public string? ContractTypeTranslated { get; set; }
public IEnumerable<EnvelopeDocumentDto>? Documents { get; set; }
}
}

View File

@@ -1,15 +1,32 @@
namespace EnvelopeGenerator.Application.DTOs
using DigitalData.EmailProfilerDispatcher.Domain.Attributes;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace EnvelopeGenerator.Application.DTOs
{
public record EnvelopeReceiverDto(
int EnvelopeId,
int ReceiverId,
int Sequence,
string? Name,
string? JobTitle,
string? CompanyName,
string? PrivateMessage,
DateTime AddedWhen,
DateTime? ChangedWhen,
EnvelopeDto? Envelope,
ReceiverDto? Receiver);
public record EnvelopeReceiverDto()
{
public int EnvelopeId { get; set; }
public int ReceiverId { get; set; }
public int Sequence { get; set; }
[TemplatePlaceholder("[NAME_RECEIVER]")]
public string? Name { get; set; }
public string? JobTitle { get; set; }
public string? CompanyName { get; set; }
public string? PrivateMessage { get; set; }
public DateTime AddedWhen { get; set; }
public DateTime? ChangedWhen { get; set; }
public EnvelopeDto? Envelope { get; set; }
public ReceiverDto? Receiver { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
namespace EnvelopeGenerator.Application
{
public class DispatcherConfig
{
public int SendingProfile { get; init; } = 1;
public string AddedWho { get; init; } = "DDEnvelopGenerator";
public int ReminderTypeId { get; init; } = 202377;
public string EmailAttmt1 { get; init; } = string.Empty;
}
}

View File

@@ -102,7 +102,16 @@ namespace EnvelopeGenerator.Application
/// <returns>The receiver signature.</returns>
public static string? GetReceiverSignature(this string envelopeReceiverId) => envelopeReceiverId.DecodeEnvelopeReceiverId().ReceiverSignature;
public static void LogEnvelopeError(this ILogger logger, string envelopeReceiverId, Exception? exception = null, string? message = null, params object?[] args)
public static string EncodeEnvelopeReceiverId(this (string envelopeUuid, string receiverSignature) input)
{
string combinedString = $"{input.envelopeUuid}::{input.receiverSignature}";
byte[] bytes = Encoding.UTF8.GetBytes(combinedString);
string base64String = Convert.ToBase64String(bytes);
return base64String;
}
public static void LogEnvelopeError(this ILogger logger, string envelopeReceiverId, Exception? exception = null, string? message = null, params object?[] args)
{
var sb = new StringBuilder().AppendLine(envelopeReceiverId.DecodeEnvelopeReceiverId().ToTitle());

View File

@@ -6,22 +6,57 @@ using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Application.Resources;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
namespace EnvelopeGenerator.Application.Services
{
public class ConfigService : BasicCRUDService<IConfigRepository, ConfigDto, Config, int>, IConfigService
{
public ConfigService(IConfigRepository repository, IStringLocalizer<Resource> localizer, IMapper mapper) : base(repository, localizer, mapper)
private static readonly Guid DefaultConfigCacheId = Guid.NewGuid();
private readonly IMemoryCache _cache;
private readonly ILogger<ConfigService> _logger;
public ConfigService(IConfigRepository repository, IStringLocalizer<Resource> localizer, IMapper mapper, IMemoryCache memoryCache, ILogger<ConfigService> logger) : base(repository, localizer, mapper)
{
_cache = memoryCache;
_logger = logger;
}
public async Task<DataResult<ConfigDto>> ReadFirstAsync()
{
var config = await _repository.ReadFirstAsync();
return config is null ? Result.Fail<ConfigDto>().Message("There is no configuration in DB.") : Result.Success(_mapper.MapOrThrow<ConfigDto>(config));
return config is null
? Result.Fail<ConfigDto>().Notice(LogLevel.Error, Flag.DataIntegrityIssue, "There is no configuration in DB.")
: Result.Success(_mapper.MapOrThrow<ConfigDto>(config));
}
public async Task<DataResult<ConfigDto>> ReadDefaultAsync() => await ReadFirstAsync();
/// <summary>
/// Reads the default configuration asynchronously.
/// </summary>
/// <remarks>
/// The configuration is cached in memory upon the first retrieval. If the configuration is updated,
/// the application needs to be restarted for the changes to take effect as the memory cache will not be updated automatically.
/// </remarks>
/// <returns>
/// A task that represents the asynchronous read operation. The task result contains the default configuration as a <see cref="ConfigDto"/>.
/// </returns>
/// <exception cref="InvalidOperationException">
/// Thrown when the default configuration cannot be found.
/// </exception>
public async Task<ConfigDto> ReadDefaultAsync()
{
var config = await _cache.GetOrCreateAsync(DefaultConfigCacheId, _ => ReadFirstAsync().ThenAsync(
Success: config => config,
Fail: (mssg, ntc) =>
{
_logger.LogNotice(ntc);
throw new InvalidOperationException("Default configuration cannot find.");
}));
return config!;
}
public async Task<string> ReadDefaultSignatureHost() => (await ReadDefaultAsync()).SignatureHost;
}
}

View File

@@ -7,30 +7,93 @@ using DigitalData.UserManager.Application;
using EnvelopeGenerator.Application.Contracts;
using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Common;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using static EnvelopeGenerator.Common.Constants;
namespace EnvelopeGenerator.Application.Services
{
public class EnvelopeMailService : EmailOutService<Resource>, IEnvelopeMailService
public class EnvelopeMailService : EmailOutService<Resource>, IEnvelopeMailService
{
private readonly IEmailTemplateService _tempService;
private readonly IMemoryCache _cache;
private readonly IEmailTemplateService _tempService;
private readonly IEnvelopeReceiverService _envRcvService;
private readonly DispatcherConfig _dConfig;
private readonly IConfigService _configService;
public EnvelopeMailService(IEmailOutRepository repository, IStringLocalizer<Resource> localizer, IMapper mapper, IEmailTemplateService tempService, IMemoryCache cache) : base(repository, localizer, mapper)
public EnvelopeMailService(IEmailOutRepository repository, IStringLocalizer<Resource> localizer, IMapper mapper, IEmailTemplateService tempService, IEnvelopeReceiverService envelopeReceiverService, IOptions<DispatcherConfig> dispatcherConfigOptions, IConfigService configService) : base(repository, localizer, mapper)
{
_tempService = tempService;
_cache = cache;
_envRcvService = envelopeReceiverService;
_dConfig = dispatcherConfigOptions.Value;
_configService = configService;
}
public Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto envelopeReceiverDto)
{
throw new NotImplementedException();
}
//TODO: create ioptions and implement TemplatePlaceHolderAttribute instead of this method
private async Task<Dictionary<string, string>> CreatePlaceholders(string? accessCode = null, EnvelopeReceiverDto? envelopeReceiverDto = null)
{
Dictionary<string, string> placeholders = new() {
{ "[NAME_PORTAL]", "signFlow" },
{ "[SIGNATURE_TYPE]" , "signieren"},
{ "[REASON]", string.Empty } };
public Task<DataResult<int>> SendAsync(EnvelopeReceiverDto envelopeReceiverDto, Constants.EmailTemplateType tempType)
if (accessCode is not null)
placeholders["[DOCUMENT_ACCESS_CODE]"] = accessCode;
if(envelopeReceiverDto is not null && envelopeReceiverDto.Envelope is not null && envelopeReceiverDto.Receiver is not null)
{
var erId = (envelopeReceiverDto.Envelope.Uuid, envelopeReceiverDto.Receiver.Signature).EncodeEnvelopeReceiverId();
var sigHost = await _configService.ReadDefaultSignatureHost();
var linkToDoc = $"{sigHost}/envelope/{erId}";
placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
placeholders["[LINK_TO_DOCUMENT_TEXT]"] = linkToDoc[..Math.Min(40, linkToDoc.Length)];
}
return placeholders;
}
public async Task<DataResult<int>> SendAccessCodeAsync(EnvelopeReceiverDto dto) => await SendAsync(dto: dto, tempType: Constants.EmailTemplateType.DocumentAccessCodeReceived);
public async Task<DataResult<int>> SendAsync(EnvelopeReceiverDto dto, Constants.EmailTemplateType tempType)
{
throw new NotImplementedException();
}
}
var tempSerResult = await _tempService.ReadByNameAsync(tempType);
if (tempSerResult.IsFailed)
return tempSerResult.ToFail<int>().Notice(LogLevel.Error, Flag.DataIntegrityIssue, $"The email cannot send because '{tempType}' template cannot found.");
var temp = tempSerResult.Data;
var mail = new EmailOutCreateDto()
{
EmailAddress = dto.Receiver!.EmailAddress,
EmailSubj = temp.Subject,
EmailBody = temp.Body,
//email_type = envelope_status,
//message = envelope_message,
ReferenceId = dto.EnvelopeId, //REFERENCE_ID = ENVELOPE_ID
ReferenceString = dto!.Envelope!.Uuid, //REFERENCE_STRING = ENVELOPE_UUID
//receiver_name = receiver.name,
//receiver_access_code = receiver.access_code,
//sender_adress = envelope.user.email,
//sender_name = envelope.user.full_name,
//envelope_title = envelope.title,
ReminderTypeId = _dConfig.ReminderTypeId,
SendingProfile = _dConfig.SendingProfile,
EntityId = null,
WfId = (int) EnvelopeStatus.MessageAccessCodeSent,
WfReference = null,
AddedWho = _dConfig.AddedWho,
EmailAttmt1 = _dConfig.EmailAttmt1
};
//get acccess code
var acResult = await _envRcvService.ReadAccessCodeByIdAsync(envelopeId: dto.EnvelopeId, receiverId: dto.ReceiverId);
if (acResult.IsFailed)
return acResult.ToFail<int>().Notice(LogLevel.Error, "Therefore, access code cannot be sent");
var accessCode = acResult.Data;
var placeholders = await CreatePlaceholders(accessCode: accessCode, envelopeReceiverDto: dto);
return await CreateWithTemplateAsync(createDto: mail,placeholders: placeholders,
dto, dto.Envelope.User!, dto.Envelope);
}
}
}

View File

@@ -104,5 +104,13 @@ namespace EnvelopeGenerator.Application.Services
int count = await _repository.CountAsync(uuid:uuid, signature:signature);
return Result.Success(count > 0);
}
}
public async Task<DataResult<string>> ReadAccessCodeByIdAsync(int envelopeId, int receiverId)
{
var code = await _repository.ReadAccessCodeByIdAsync(envelopeId: envelopeId, receiverId: receiverId);
return code is null ?
Result.Fail<string>().Notice(LogLevel.Error, Flag.DataIntegrityIssue, $"Access code is null. Envelope ID is {envelopeId} and receiver ID {receiverId}")
: Result.Success(code);
}
}
}

View File

@@ -6,6 +6,7 @@ using EnvelopeGenerator.Application.DTOs;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.Infrastructure.Contracts;
using EnvelopeGenerator.Application.Resources;
using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.Application.Services
{