using FastEndpoints;
using KArtSell.BuildingBlocks.Time;
using Microsoft.Extensions.Logging;
namespace KArtSell.Host.Features.ShadowRun;
///
/// POST /api/shadow-runs
/// Initiate a 252+ trading-day model validation run.
/// Returns 202 Accepted with job tracking info.
///
public class InitiateShadowRunEndpoint : Endpoint
{
private InitiateShadowRunHandler? _handler;
private ILogger? _logger;
private static readonly Action LogRequestReceived =
LoggerMessage.Define(
LogLevel.Information,
new EventId(1, nameof(LogRequestReceived)),
"Shadow run request received: {ModelId}");
private static readonly Action LogRequestFailed =
LoggerMessage.Define(
LogLevel.Error,
new EventId(2, nameof(LogRequestFailed)),
"Shadow run initiation failed");
public override void Configure()
{
Post("/api/shadow-runs");
Roles("Admin", "Researcher"); // RBAC: Only Admin or Researcher can initiate
Validator();
}
public override async Task HandleAsync(InitiateShadowRunRequest req, CancellationToken ct)
{
_handler = Resolve();
_logger = Resolve>();
var correlationId = HttpContext.Items["CorrelationId"] as Guid? ?? Guid.NewGuid();
try
{
LogRequestReceived(_logger, req.ModelId, null);
var response = await _handler.HandleAsync(req, correlationId, ct);
// 202 Accepted: Job queued, results available later via polling
HttpContext.Response.StatusCode = 202;
await HttpContext.Response.WriteAsJsonAsync(response, ct);
}
catch (InvalidOperationException ex)
{
ThrowError(ex.Message, 400);
}
catch (Exception ex)
{
LogRequestFailed(_logger, ex);
ThrowError("Shadow run initiation failed. Please retry.", 500);
}
}
}