Compare commits
10 Commits
9434f83b3e
...
ddb2439b29
| Author | SHA1 | Date | |
|---|---|---|---|
| ddb2439b29 | |||
| d48514bbad | |||
| 00077a647a | |||
| ee7eb08e75 | |||
| 6a34b65825 | |||
| 20d312a84e | |||
| 87c5e7e4de | |||
| bb93b980b4 | |||
| 950ae5a418 | |||
| 582cc1eb13 |
@@ -1,6 +1,10 @@
|
||||
using EnvelopeGenerator.Application.Dto;
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using EnvelopeGenerator.Application.Dto;
|
||||
using EnvelopeGenerator.Application.Interfaces.SQLExecutor;
|
||||
using MediatR;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
||||
|
||||
@@ -30,4 +34,14 @@ public record CreateEnvelopeCommand : IRequest<EnvelopeDto?>
|
||||
/// ID des Absenders
|
||||
/// </summary>
|
||||
public int UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines which component is used for envelope processing.
|
||||
/// When <c>true</c>, processing is delegated to <see cref="IEnvelopeExecutor"/>;
|
||||
/// when <c>false</c>, <see cref="IRepository{Envelope}"/> is used instead.
|
||||
/// Note: <see cref="IRepository{Envelope}"/> should only be used in testing scenarios.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
[NotMapped]
|
||||
public bool UseSQLExecutor { get; set; } = true;
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
using EnvelopeGenerator.Application.Interfaces.SQLExecutor;
|
||||
using EnvelopeGenerator.Application.Dto;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
||||
|
||||
@@ -10,18 +13,22 @@ namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
||||
/// </summary>
|
||||
public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeCommand, EnvelopeDto?>
|
||||
{
|
||||
private readonly IEnvelopeExecutor _envelopeExecutor;
|
||||
private readonly IServiceProvider _provider;
|
||||
|
||||
private IEnvelopeExecutor Executor => _provider.GetRequiredService<IEnvelopeExecutor>();
|
||||
|
||||
private IRepository<Envelope> Repository => _provider.GetRequiredService<IRepository<Envelope>>();
|
||||
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="envelopeExecutor"></param>
|
||||
/// <param name="provider"></param>
|
||||
/// <param name="mapper"></param>
|
||||
public CreateEnvelopeCommandHandler(IEnvelopeExecutor envelopeExecutor, IMapper mapper)
|
||||
public CreateEnvelopeCommandHandler(IServiceProvider provider, IMapper mapper)
|
||||
{
|
||||
_envelopeExecutor = envelopeExecutor;
|
||||
_provider = provider;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
@@ -29,12 +36,14 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <param name="cancel"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<EnvelopeDto?> Handle(CreateEnvelopeCommand request, CancellationToken cancellationToken)
|
||||
public async Task<EnvelopeDto?> Handle(CreateEnvelopeCommand request, CancellationToken cancel)
|
||||
{
|
||||
var envelope = await _envelopeExecutor.CreateEnvelopeAsync(request.UserId, request.Title, request.Message, request.TFAEnabled, cancellationToken);
|
||||
var envelope = request.UseSQLExecutor
|
||||
? await Executor.CreateEnvelopeAsync(request.UserId, request.Title, request.Message, request.TFAEnabled, cancel)
|
||||
: await Repository.CreateAsync(request, cancel);
|
||||
|
||||
return _mapper.Map<EnvelopeDto>(envelope);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using AutoMapper;
|
||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Commands;
|
||||
using EnvelopeGenerator.Application.Envelopes.Commands;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Envelopes;
|
||||
@@ -15,5 +16,6 @@ public class MappingProfile : Profile
|
||||
public MappingProfile()
|
||||
{
|
||||
CreateMap<Envelope, CreateEnvelopeReceiverResponse>();
|
||||
CreateMap<CreateEnvelopeCommand, Envelope>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Extensions
|
||||
namespace EnvelopeGenerator.Application.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for decoding and extracting information from an envelope receiver ID.
|
||||
/// </summary>
|
||||
public static class EncodingExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for decoding and extracting information from an envelope receiver ID.
|
||||
///
|
||||
/// </summary>
|
||||
public static class EncodingExtensions
|
||||
/// <param name="readOnlyId"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToEnvelopeKey(this long readOnlyId)
|
||||
{
|
||||
public static string EncodeEnvelopeReceiverId(this long readOnlyId)
|
||||
{
|
||||
//The random number is used as a salt to increase security but it is not saved in the database.
|
||||
string combinedString = $"{Random.Shared.Next()}::{readOnlyId}::{Random.Shared.Next()}";
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(combinedString);
|
||||
string base64String = Convert.ToBase64String(bytes);
|
||||
//The random number is used as a salt to increase security but it is not saved in the database.
|
||||
string combinedString = $"{Random.Shared.Next()}::{readOnlyId}::{Random.Shared.Next()}";
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(combinedString);
|
||||
string base64String = Convert.ToBase64String(bytes);
|
||||
|
||||
return base64String;
|
||||
}
|
||||
return base64String;
|
||||
}
|
||||
|
||||
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);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToEnvelopeKey(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;
|
||||
}
|
||||
return base64String;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using AutoMapper;
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using EnvelopeGenerator.Application.Dto.Receiver;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -13,7 +15,7 @@ namespace EnvelopeGenerator.Application.Receivers.Commands;
|
||||
///
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public record CreateReceiverCommand : IRequest<(int Id, bool AlreadyExists)>
|
||||
public record CreateReceiverCommand : IRequest<(ReceiverReadDto Receiver, bool AlreadyExists)>
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
@@ -52,20 +54,23 @@ public record CreateReceiverCommand : IRequest<(int Id, bool AlreadyExists)>
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class CreateReceiverCommandHandler : IRequestHandler<CreateReceiverCommand, (int Id, bool AlreadyExists)>
|
||||
public class CreateReceiverCommandHandler : IRequestHandler<CreateReceiverCommand, (ReceiverReadDto Receiver, bool AlreadyExists)>
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
private readonly IRepository<Receiver> _repo;
|
||||
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="repo"></param>
|
||||
public CreateReceiverCommandHandler(IRepository<Receiver> repo)
|
||||
public CreateReceiverCommandHandler(IRepository<Receiver> repo, IMapper mapper)
|
||||
{
|
||||
_repo = repo;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -74,7 +79,7 @@ public class CreateReceiverCommandHandler : IRequestHandler<CreateReceiverComman
|
||||
/// <param name="request"></param>
|
||||
/// <param name="cancel"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<(int Id, bool AlreadyExists)> Handle(CreateReceiverCommand request, CancellationToken cancel)
|
||||
public async Task<(ReceiverReadDto Receiver, bool AlreadyExists)> Handle(CreateReceiverCommand request, CancellationToken cancel)
|
||||
{
|
||||
var receiver = await _repo.ReadOnly()
|
||||
.Where(r => r.EmailAddress == request.EmailAddress)
|
||||
@@ -85,6 +90,7 @@ public class CreateReceiverCommandHandler : IRequestHandler<CreateReceiverComman
|
||||
if (!alreadyExists)
|
||||
receiver = await _repo.CreateAsync(request, cancel);
|
||||
|
||||
return (receiver!.Id, alreadyExists);
|
||||
var receiverDto = _mapper.Map<ReceiverReadDto>(receiver);
|
||||
return (receiverDto, alreadyExists);
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ private async Task<Dictionary<string, string>> CreatePlaceholders(string? access
|
||||
|
||||
if(envelopeReceiverDto?.Envelope is not null && envelopeReceiverDto.Receiver is not null)
|
||||
{
|
||||
var erId = (envelopeReceiverDto.Envelope.Uuid, envelopeReceiverDto.Receiver.Signature).EncodeEnvelopeReceiverId();
|
||||
var erId = (envelopeReceiverDto.Envelope.Uuid, envelopeReceiverDto.Receiver.Signature).ToEnvelopeKey();
|
||||
var sigHost = await _configService.ReadDefaultSignatureHost();
|
||||
var linkToDoc = $"{sigHost}/EnvelopeKey/{erId}";
|
||||
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
|
||||
@@ -71,7 +71,7 @@ private async Task<Dictionary<string, string>> CreatePlaceholders(string? access
|
||||
if (readOnlyDto?.Envelope is not null && readOnlyDto.Receiver is not null)
|
||||
{
|
||||
_placeholders["[NAME_RECEIVER]"] = await _envRcvService.ReadLastUsedReceiverNameByMailAsync(readOnlyDto.AddedWho).ThenAsync(res => res, (msg, ntc) => string.Empty) ?? string.Empty;
|
||||
var erReadOnlyId = (readOnlyDto.Id).EncodeEnvelopeReceiverId();
|
||||
var erReadOnlyId = (readOnlyDto.Id).ToEnvelopeKey();
|
||||
var sigHost = await _configService.ReadDefaultSignatureHost();
|
||||
var linkToDoc = $"{sigHost}/EnvelopeKey/{erReadOnlyId}";
|
||||
_placeholders["[LINK_TO_DOCUMENT]"] = linkToDoc;
|
||||
|
||||
@@ -104,8 +104,8 @@ public class Fake
|
||||
var mediator = Mediator;
|
||||
foreach (var cmd in Provider.CreateReceiverCommands())
|
||||
{
|
||||
var (Id, _) = await mediator.Send(cmd);
|
||||
_sampleReceivers.Add((Id, cmd.EmailAddress));
|
||||
var (receiver, _) = await mediator.Send(cmd);
|
||||
_sampleReceivers.Add((receiver.Id, cmd.EmailAddress));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -156,7 +156,8 @@ public static class Extensions
|
||||
{
|
||||
Message = fake.Lorem.Paragraph(fake.Random.Number(2, 5)),
|
||||
Title = fake.Lorem.Paragraph(fake.Random.Number(1, 2)),
|
||||
UserId = userId
|
||||
UserId = userId,
|
||||
UseSQLExecutor = false
|
||||
};
|
||||
|
||||
public static List<CreateEnvelopeCommand> CreateEnvelopeCommands(this Faker fake, params int[] userIDs)
|
||||
@@ -205,9 +206,7 @@ public static class Extensions
|
||||
#endregion
|
||||
|
||||
#region History
|
||||
public static CreateHistoryCommand CreateHistoryCommand<TEnvelopeQuery, TReceiverQuery>(this Faker fake, string key, EnvelopeStatus? status = null)
|
||||
where TEnvelopeQuery : EnvelopeQueryBase
|
||||
where TReceiverQuery : ReceiverQueryBase
|
||||
public static CreateHistoryCommand CreateHistoryCommand(this Faker fake, string key, EnvelopeStatus? status = null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
|
||||
@@ -1,48 +1,50 @@
|
||||
using EnvelopeGenerator.Application.Histories.Commands;
|
||||
using EnvelopeGenerator.Application.Extensions;
|
||||
using EnvelopeGenerator.Application.Histories.Commands;
|
||||
using EnvelopeGenerator.Application.Histories.Queries;
|
||||
using EnvelopeGenerator.Domain;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
|
||||
namespace EnvelopeGenerator.Tests.Application;
|
||||
|
||||
[TestFixture]
|
||||
public class HistoryTests
|
||||
public class HistoryTests : TestBase
|
||||
{
|
||||
private Fake.Host _host;
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
public override Task Setup()
|
||||
{
|
||||
_host = Fake.CreateHost();
|
||||
await _host.AddSampleReceivers();
|
||||
return base.Setup();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
public override void TearDown()
|
||||
{
|
||||
_host.Dispose();
|
||||
base.TearDown();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CreateHistory_And_ReadHistory_Should_Work()
|
||||
{
|
||||
// Arrange
|
||||
var createCmd = new CreateHistoryCommand
|
||||
{
|
||||
EnvelopeId = 1,
|
||||
UserReference = "UserA",
|
||||
Status = EnvelopeStatus.EnvelopeCreated,
|
||||
Comment = "First create"
|
||||
};
|
||||
/// Arrange
|
||||
// Create envelope
|
||||
var createEnvelopeCmd = FakeCreateEnvelopeCommand;
|
||||
var envelope = await Mediator.Send(createEnvelopeCmd).ThrowIfNull(Exceptions.NotFound);
|
||||
|
||||
// Create receiver
|
||||
var createReceiverCmd = this.CreateReceiverCommand();
|
||||
(var receiver, _) = await Mediator.Send(createReceiverCmd);
|
||||
|
||||
var key = (envelope.Uuid, receiver.Signature).ToEnvelopeKey();
|
||||
|
||||
var createCmd = Fake.Provider.CreateHistoryCommand(key);
|
||||
|
||||
// Act
|
||||
var id = await _host.Mediator.Send(createCmd);
|
||||
var id = await Mediator.Send(createCmd);
|
||||
|
||||
// Assert
|
||||
Assert.That(id, Is.Not.Null);
|
||||
|
||||
// ReadHistory query
|
||||
var query = new ReadHistoryQuery(1);
|
||||
var result = await _host.Mediator.Send(query);
|
||||
var result = await Mediator.Send(query);
|
||||
|
||||
Assert.That(result, Is.Not.Empty);
|
||||
}
|
||||
@@ -65,11 +67,11 @@ public class HistoryTests
|
||||
Status = EnvelopeStatus.EnvelopePartlySigned
|
||||
};
|
||||
|
||||
await _host.Mediator.Send(createCmd1);
|
||||
await _host.Mediator.Send(createCmd2);
|
||||
await Mediator.Send(createCmd1);
|
||||
await Mediator.Send(createCmd2);
|
||||
|
||||
// Act
|
||||
var result = await _host.Mediator.Send(new ReadHistoryQuery(2, EnvelopeStatus.EnvelopePartlySigned));
|
||||
var result = await Mediator.Send(new ReadHistoryQuery(2, EnvelopeStatus.EnvelopePartlySigned));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Has.Exactly(1).Items);
|
||||
@@ -81,7 +83,7 @@ public class HistoryTests
|
||||
public async Task ReadHistory_Should_Return_Empty_When_No_Record()
|
||||
{
|
||||
// Act
|
||||
var result = await _host.Mediator.Send(new ReadHistoryQuery(999));
|
||||
var result = await Mediator.Send(new ReadHistoryQuery(999));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Empty);
|
||||
|
||||
31
EnvelopeGenerator.Tests.Application/TestBase.cs
Normal file
31
EnvelopeGenerator.Tests.Application/TestBase.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Bogus;
|
||||
using DigitalData.UserManager.Domain.Entities;
|
||||
using EnvelopeGenerator.Application.Envelopes.Commands;
|
||||
using EnvelopeGenerator.Application.Receivers.Commands;
|
||||
using MediatR;
|
||||
|
||||
namespace EnvelopeGenerator.Tests.Application;
|
||||
|
||||
public class TestBase : Faker
|
||||
{
|
||||
private Fake.Host Host;
|
||||
|
||||
public User User => Host.User;
|
||||
|
||||
public IMediator Mediator => Host.Mediator;
|
||||
|
||||
public CreateEnvelopeCommand FakeCreateEnvelopeCommand => this.CreateEnvelopeCommand(Host.User.Id);
|
||||
|
||||
[SetUp]
|
||||
public virtual async Task Setup()
|
||||
{
|
||||
Host = Fake.CreateHost();
|
||||
await Host.AddSamples();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public virtual void TearDown()
|
||||
{
|
||||
Host.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -458,7 +458,7 @@ public class HomeController : ViewControllerBase
|
||||
return await _envRcvService.ReadByUuidSignatureAsync(uuid: erro.Envelope!.Uuid, erro.Receiver!.Signature).ThenAsync(
|
||||
SuccessAsync: async er =>
|
||||
{
|
||||
var envelopeKey = (er.Envelope!.Uuid, er.Receiver!.Signature).EncodeEnvelopeReceiverId();
|
||||
var envelopeKey = (er.Envelope!.Uuid, er.Receiver!.Signature).ToEnvelopeKey();
|
||||
|
||||
//TODO: implement multi-threading to history process (Task)
|
||||
var hist_res = await _historyService.RecordAsync((int)erro.EnvelopeId, erro.AddedWho, EnvelopeStatus.EnvelopeViewed);
|
||||
|
||||
@@ -75,8 +75,8 @@ public class TestEnvelopeReceiverController : ControllerBase
|
||||
public IActionResult EncodeEnvelopeReceiverId(string? uuid = null, string? signature = null, long? readOnlyId = null)
|
||||
{
|
||||
if(readOnlyId is long readOnlyId_long)
|
||||
return Ok(readOnlyId_long.EncodeEnvelopeReceiverId());
|
||||
return Ok(readOnlyId_long.ToEnvelopeKey());
|
||||
else
|
||||
return Ok((uuid ?? string.Empty, signature ?? string.Empty).EncodeEnvelopeReceiverId());
|
||||
return Ok((uuid ?? string.Empty, signature ?? string.Empty).ToEnvelopeKey());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user