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:
2026-10-10 13:50:26 +02:00
parent b44be4f9e2
commit 3126f025be
6 changed files with 227 additions and 2 deletions

View File

@@ -0,0 +1,60 @@
using DigitalData.TaskFlow.Application.Common.Factories;
using DigitalData.TaskFlow.Domain.Common;
using DigitalData.TaskFlow.Domain.Enums;
using FluentValidation;
using System.Text.Json;
namespace DigitalData.TaskFlow.API.Middleware;
/// <summary>
/// Handles exceptions globally and returns ticket-compatible API envelopes.
/// </summary>
public sealed class ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
/// <summary>
/// Executes the middleware.
/// </summary>
/// <param name="context">HTTP context.</param>
public async Task InvokeAsync(HttpContext context)
{
try
{
await next(context);
}
catch (ValidationException validationException)
{
logger.LogWarning(validationException, "Validation failed.");
context.Response.StatusCode = StatusCodes.Status400BadRequest;
context.Response.ContentType = "application/json";
var message = validationException.Errors.FirstOrDefault()?.ErrorMessage
?? "Ungultiger Request";
var response = ApiEnvelopeFactory.Create<object?>(null, MetaCode.InvalidRequest, message);
await context.Response.WriteAsync(JsonSerializer.Serialize(response, JsonOptions));
}
catch (NotSupportedException notSupportedException)
{
logger.LogWarning(notSupportedException, "Operation not supported yet.");
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = "application/json";
var response = ApiEnvelopeFactory.Create<object?>(null, MetaCode.InternalError, notSupportedException.Message);
await context.Response.WriteAsync(JsonSerializer.Serialize(response, JsonOptions));
}
catch (Exception exception)
{
logger.LogError(exception, "Unhandled exception.");
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = "application/json";
var response = ApiEnvelopeFactory.Create<object?>(null, MetaCode.InternalError);
await context.Response.WriteAsync(JsonSerializer.Serialize(response, JsonOptions));
}
}
}