Add query for sensitive envelope receiver data

Introduced `ReadEnvelopeReceiverSecretQuery` to fetch sensitive
fields (e.g., access code, phone number) for envelope receivers.
Added extension methods for dispatching the query via `IMediator`
and implemented `ReadEnvelopeReceiverSecretQueryHandler` to
process the query using repositories and AutoMapper.

Updated `EnvelopeController` with a new HTTP GET endpoint
`EnvelopeReceiverWithSecretByMediatr` to expose the query
functionality. This endpoint returns sensitive data as
`EnvelopeReceiverSecretDto` or a 404 response if no match is found.

These changes improve modularity, testability, and separation of
concerns by leveraging MediatR and CQRS patterns.
This commit is contained in:
2026-05-28 23:46:31 +02:00
parent 67e6f288eb
commit bfae72529c
2 changed files with 133 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Application.Envelopes.Queries;
using EnvelopeGenerator.Application.Receivers.Queries;
using MediatR;
using EnvelopeGenerator.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using EnvelopeGenerator.Application.Common.Query;
using EnvelopeGenerator.Application.Common.Extensions;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
/// <summary>
/// Represents a query for reading an envelope receiver including sensitive fields
/// (access code, phone number) that are excluded from the standard <see cref="ReadEnvelopeReceiverQuery"/>.
/// </summary>
/// <remarks>
/// Returns a single <see cref="EnvelopeReceiverSecretDto"/> matched by UUID and receiver signature.
/// Equivalent to the legacy <c>ReadWithSecretByUuidSignatureAsync</c> service method.
/// </remarks>
public record ReadEnvelopeReceiverSecretQuery
: EnvelopeReceiverQueryBase<ReadEnvelopeQuery, ReadReceiverQuery>,
IRequest<EnvelopeReceiverSecretDto?>;
/// <summary>
/// Extension methods for dispatching <see cref="ReadEnvelopeReceiverSecretQuery"/> via <see cref="IMediator"/>.
/// </summary>
public static class ReadEnvelopeReceiverSecretQueryExtensions
{
/// <summary>
/// Sends a <see cref="ReadEnvelopeReceiverSecretQuery"/> using the composite key (uuid::signature).
/// </summary>
/// <param name="mediator">The mediator instance.</param>
/// <param name="key">Composite key in the format <c>uuid::signature</c>.</param>
/// <param name="cancel">Cancellation token.</param>
/// <returns>The matching <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if not found.</returns>
public static Task<EnvelopeReceiverSecretDto?> ReadEnvelopeReceiverSecretAsync(
this IMediator mediator,
string key,
CancellationToken cancel = default)
=> mediator.Send(new ReadEnvelopeReceiverSecretQuery { Key = key }, cancel);
/// <summary>
/// Sends a <see cref="ReadEnvelopeReceiverSecretQuery"/> using UUID and receiver signature.
/// </summary>
/// <param name="mediator">The mediator instance.</param>
/// <param name="uuid">Envelope UUID.</param>
/// <param name="signature">Receiver signature.</param>
/// <param name="cancel">Cancellation token.</param>
/// <returns>The matching <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if not found.</returns>
public static Task<EnvelopeReceiverSecretDto?> ReadEnvelopeReceiverSecretAsync(
this IMediator mediator,
string uuid,
string signature,
CancellationToken cancel = default)
{
var q = new ReadEnvelopeReceiverSecretQuery();
q.Envelope.Uuid = uuid;
q.Receiver.Signature = signature;
return mediator.Send(q, cancel);
}
/// <summary>
/// Handles <see cref="ReadEnvelopeReceiverSecretQuery"/> and returns a
/// <see cref="EnvelopeReceiverSecretDto"/> containing sensitive fields.
/// </summary>
public class ReadEnvelopeReceiverSecretQueryHandler
: IRequestHandler<ReadEnvelopeReceiverSecretQuery, EnvelopeReceiverSecretDto?>
{
private readonly IRepository<EnvelopeReceiver> _repo;
private readonly IRepository<Receiver> _rcvRepo;
private readonly IMapper _mapper;
/// <summary>
/// Initializes a new instance of <see cref="ReadEnvelopeReceiverSecretQueryHandler"/>.
/// </summary>
/// <param name="envelopeReceiver">Repository for <see cref="EnvelopeReceiver"/>.</param>
/// <param name="rcvRepo">Repository for <see cref="Receiver"/>.</param>
/// <param name="mapper">AutoMapper instance.</param>
public ReadEnvelopeReceiverSecretQueryHandler(
IRepository<EnvelopeReceiver> envelopeReceiver,
IRepository<Receiver> rcvRepo,
IMapper mapper)
{
_repo = envelopeReceiver;
_rcvRepo = rcvRepo;
_mapper = mapper;
}
/// <summary>
/// Handles the query and returns the matching <see cref="EnvelopeReceiverSecretDto"/>.
/// </summary>
/// <param name="request">The query containing filter criteria.</param>
/// <param name="cancel">Cancellation token.</param>
/// <returns>
/// The matched <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if no record is found.
/// </returns>
public async Task<EnvelopeReceiverSecretDto?> Handle(
ReadEnvelopeReceiverSecretQuery request,
CancellationToken cancel)
{
var q = _repo.Query.Where(request, notnull: false);
var envRcvs = await q
.Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements)
.Include(er => er.Envelope).ThenInclude(e => e!.Histories)
.Include(er => er.Envelope).ThenInclude(e => e!.User)
.Include(er => er.Receiver)
.ToListAsync(cancel);
if (request.Receiver.HasAnyCriteria && envRcvs.Count != 0)
{
var receiver = await _rcvRepo.Query.Where(request.Receiver).FirstAsync(cancel);
foreach (var item in envRcvs)
item.Envelope?.Documents?.FirstOrDefault()?.Elements?.RemoveAll(s => s.ReceiverId != receiver.Id);
}
var envRcv = envRcvs.FirstOrDefault();
if (envRcv is null)
return null;
return _mapper.Map<EnvelopeReceiverSecretDto>(envRcv);
}
}
}

View File

@@ -438,4 +438,10 @@ public class EnvelopeController : ViewControllerBase
return this.ViewDocumentNotFound();
}
}
[HttpGet("EnvelopeReceiverWithSecretByMediatr")]
public async Task<IActionResult> EnvelopeReceiverWithSecretByMediatr([FromQuery] ReadEnvelopeReceiverSecretQuery q, CancellationToken cancel)
{
return await _mediator.Send(q, cancel) is EnvelopeReceiverSecretDto dto ? Ok(dto) : NotFound();
}
}