Refactor document retrieval endpoints and authorization
- Updated DocumentController to use class-level [Authorize] and method-level role-based authorization for sender and receiver endpoints. - Replaced ReadEnvelopeReceiverQuery with ReadDocumentQuery for sender document retrieval; simplified response logic. - Added a new endpoint for fully authenticated receivers to fetch documents by envelope ID from user claims. - Refactored ReadDocumentQuery and handler to always return DocumentDto, throw NotFoundException when needed, and use _repo.Query. - Cleaned up using directives and removed legacy error handling and logging.
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
using DigitalData.Core.Exceptions;
|
using EnvelopeGenerator.API.Extensions;
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
using EnvelopeGenerator.Application.Documents.Queries;
|
||||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@@ -14,7 +13,7 @@ namespace EnvelopeGenerator.API.Controllers;
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Initializes a new instance of the <see cref="DocumentController"/> class.
|
/// Initializes a new instance of the <see cref="DocumentController"/> class.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[Authorize(Roles = Role.FullyAuth)]
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class DocumentController(IMediator mediator, ILogger<DocumentController> logger) : ControllerBase
|
public class DocumentController(IMediator mediator, ILogger<DocumentController> logger) : ControllerBase
|
||||||
@@ -25,19 +24,28 @@ public class DocumentController(IMediator mediator, ILogger<DocumentController>
|
|||||||
/// <param name="query">Encoded envelope key.</param>
|
/// <param name="query">Encoded envelope key.</param>
|
||||||
/// <param name="cancel">Cancellation token.</param>
|
/// <param name="cancel">Cancellation token.</param>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetDocument(ReadEnvelopeReceiverQuery query, CancellationToken cancel)
|
[Authorize(Roles = Role.Sender)]
|
||||||
|
public async Task<IActionResult> GetDocument(ReadDocumentQuery query, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
var envRcv = await mediator.Send(query, cancel).FirstAsync(Exceptions.NotFound);
|
var doc = await mediator.Send(query, cancel);
|
||||||
|
return doc.ByteData is byte[] docByte
|
||||||
|
? File(docByte, "application/octet-stream")
|
||||||
|
: NotFound("Document is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
var byteData = envRcv.Envelope?.Documents?.FirstOrDefault()?.ByteData;
|
/// <summary>
|
||||||
|
/// Returns the document bytes for the receiver.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancel">Cancellation token.</param>
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize(Roles = Role.Receiver.FullyAuth)]
|
||||||
|
public async Task<IActionResult> GetDocument(CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var envelopeId = User.GetEnvelopeIdOfReceiver();
|
||||||
|
|
||||||
if (byteData is null || byteData.Length == 0)
|
var doc = await mediator.Send(new ReadDocumentQuery() { EnvelopeId = envelopeId }, cancel);
|
||||||
{
|
return doc.ByteData is byte[] docByte
|
||||||
logger.LogError("Document byte data is null or empty for envelope-receiver entity:\n{envelopeKey}.",
|
? File(docByte, "application/octet-stream")
|
||||||
envRcv.ToJson(Format.Json.ForDiagnostics));
|
: NotFound("Document is empty.");
|
||||||
throw new NotFoundException("Document is empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return File(byteData, "application/octet-stream");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ using EnvelopeGenerator.Domain.Entities;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Documents.Queries;
|
namespace EnvelopeGenerator.Application.Documents.Queries;
|
||||||
|
|
||||||
@@ -12,14 +13,14 @@ namespace EnvelopeGenerator.Application.Documents.Queries;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Id">The unique identifier of the document. Optional.</param>
|
/// <param name="Id">The unique identifier of the document. Optional.</param>
|
||||||
/// <param name="EnvelopeId">The identifier of the envelope associated with the document. Optional.</param>
|
/// <param name="EnvelopeId">The identifier of the envelope associated with the document. Optional.</param>
|
||||||
public record ReadDocumentQuery(int? Id = null, int? EnvelopeId = null) : IRequest<DocumentDto?>
|
public record ReadDocumentQuery(int? Id = null, int? EnvelopeId = null) : IRequest<DocumentDto>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles queries for reading <see cref="Document"/> data based on either the document ID or the envelope ID.
|
/// Handles queries for reading <see cref="Document"/> data based on either the document ID or the envelope ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, DocumentDto?>
|
public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, DocumentDto>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TempRepo for accessing <see cref="Document"/> entities.
|
/// TempRepo for accessing <see cref="Document"/> entities.
|
||||||
@@ -50,20 +51,19 @@ public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, Docum
|
|||||||
/// <exception cref="InvalidOperationException">
|
/// <exception cref="InvalidOperationException">
|
||||||
/// Thrown when neither <see cref="ReadDocumentQuery.Id"/> nor <see cref="ReadDocumentQuery.EnvelopeId"/> is provided.
|
/// Thrown when neither <see cref="ReadDocumentQuery.Id"/> nor <see cref="ReadDocumentQuery.EnvelopeId"/> is provided.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public async Task<DocumentDto?> Handle(ReadDocumentQuery query, CancellationToken cancel)
|
public async Task<DocumentDto> Handle(ReadDocumentQuery query, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
if (query.Id is not null)
|
if (query.Id is not null)
|
||||||
{
|
{
|
||||||
var doc = await _repo.ReadOnly().Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
|
var doc = await _repo.Query.Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
|
||||||
return _mapper.Map<DocumentDto>(doc);
|
return _mapper.Map<DocumentDto>(doc);
|
||||||
}
|
}
|
||||||
else if (query.EnvelopeId is not null)
|
else if (query.EnvelopeId is not null)
|
||||||
{
|
{
|
||||||
var doc = await _repo.ReadOnly().Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
|
var doc = await _repo.Query.Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
|
||||||
return _mapper.Map<DocumentDto>(doc);
|
return _mapper.Map<DocumentDto>(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException(
|
throw new NotFoundException();
|
||||||
$"Invalid {nameof(ReadDocumentQuery)}: either {nameof(query.Id)} or {nameof(query.EnvelopeId)} must be provided.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user