Add workflow and document APIs, middleware, and Swagger
Introduced `CloseWorkflowRequest` for closing workflows. Added `DocumentsController` for document downloads and `WorkflowsController` for workflow retrieval and closing. Implemented `ExceptionHandlingMiddleware` for global exception handling with structured API responses. Enhanced Swagger configuration with metadata and production support. Added `Scalar.AspNetCore` for API reference mapping. Updated project dependencies and improved API documentation.
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
using DigitalData.TaskFlow.Application.Features.Documents.Queries.GetDocument;
|
||||
using DigitalData.TaskFlow.Domain.Common;
|
||||
using DigitalData.TaskFlow.Domain.Documents;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DigitalData.TaskFlow.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides document binary download endpoints.
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/documents")]
|
||||
public sealed class DocumentsController(ISender sender) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns binary document content by document identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">Document identifier.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Binary stream when available.</returns>
|
||||
[HttpGet("{id}")]
|
||||
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiEnvelope<object>), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ApiEnvelope<object>), StatusCodes.Status500InternalServerError)]
|
||||
public async Task<IActionResult> GetAsync([FromRoute] string id, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await sender.Send(new GetDocumentQuery(id), cancellationToken);
|
||||
var data = response.Data;
|
||||
|
||||
if (data is null)
|
||||
{
|
||||
return NotFound(response);
|
||||
}
|
||||
|
||||
return File(data.Content, data.ContentType, data.FileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using DigitalData.TaskFlow.API.Contracts.Workflows;
|
||||
using DigitalData.TaskFlow.Application.Features.Workflows.Commands.CloseWorkflow;
|
||||
using DigitalData.TaskFlow.Application.Features.Workflows.Queries.GetWorkflows;
|
||||
using DigitalData.TaskFlow.Domain.Closing;
|
||||
using DigitalData.TaskFlow.Domain.Common;
|
||||
using DigitalData.TaskFlow.Domain.Workflows;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DigitalData.TaskFlow.API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides workflow read and close endpoints.
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/workflows")]
|
||||
public sealed class WorkflowsController(ISender sender) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns open workflows for a WISAG user identifier.
|
||||
/// </summary>
|
||||
/// <param name="wisagId">WISAG user identifier.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Workflow response envelope grouped by source.</returns>
|
||||
[HttpGet("{wisagId:int}")]
|
||||
[ProducesResponseType(typeof(ApiEnvelope<WorkflowSourcesResponse>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiEnvelope<object>), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ApiEnvelope<object>), StatusCodes.Status500InternalServerError)]
|
||||
public async Task<ActionResult<ApiEnvelope<WorkflowSourcesResponse>>> GetAsync(
|
||||
[FromRoute] int wisagId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await sender.Send(new GetWorkflowsQuery(wisagId), cancellationToken);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a closing decision to a workflow process.
|
||||
/// </summary>
|
||||
/// <param name="wisagId">WISAG user identifier.</param>
|
||||
/// <param name="source">Source system name.</param>
|
||||
/// <param name="processId">Process identifier.</param>
|
||||
/// <param name="request">Closing request payload.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Close result envelope.</returns>
|
||||
[HttpPost("{wisagId:int}/{source}/{processId:int}/closing")]
|
||||
[ProducesResponseType(typeof(ApiEnvelope<CloseWorkflowResult>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiEnvelope<object>), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ApiEnvelope<object>), StatusCodes.Status500InternalServerError)]
|
||||
public async Task<ActionResult<ApiEnvelope<CloseWorkflowResult>>> CloseAsync(
|
||||
[FromRoute] int wisagId,
|
||||
[FromRoute] string source,
|
||||
[FromRoute] int processId,
|
||||
[FromBody] CloseWorkflowRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var command = new CloseWorkflowCommand(
|
||||
wisagId,
|
||||
source,
|
||||
processId,
|
||||
request.ClosingMethodId,
|
||||
request.Comment,
|
||||
request.RejectionReasonId);
|
||||
|
||||
var response = await sender.Send(command, cancellationToken);
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user