using Microsoft.AspNetCore.Mvc; using StellaOps.Policy.Engine.Violations; namespace StellaOps.Policy.Engine.Endpoints; public static class ViolationEndpoint { public static IEndpointRouteBuilder MapViolations(this IEndpointRouteBuilder routes) { routes.MapPost("/policy/violations/events", EmitEventsAsync) .WithName("PolicyEngine.Violations.Events"); routes.MapPost("/policy/violations/severity", FuseAsync) .WithName("PolicyEngine.Violations.Severity"); routes.MapPost("/policy/violations/conflicts", ConflictsAsync) .WithName("PolicyEngine.Violations.Conflicts"); return routes; } private static async Task EmitEventsAsync( [FromBody] ViolationEventRequest request, ViolationEventService service, CancellationToken cancellationToken) { try { var events = await service.EmitAsync(request, cancellationToken).ConfigureAwait(false); return Results.Json(new { events }); } catch (Exception ex) when (ex is ArgumentException or KeyNotFoundException) { return Results.BadRequest(new { message = ex.Message }); } } private static async Task FuseAsync( [FromBody] ViolationEventRequest request, ViolationEventService eventService, SeverityFusionService fusionService, CancellationToken cancellationToken) { try { await eventService.EmitAsync(request, cancellationToken).ConfigureAwait(false); var fused = await fusionService.FuseAsync(request.SnapshotId, cancellationToken).ConfigureAwait(false); return Results.Json(new { fused }); } catch (Exception ex) when (ex is ArgumentException or KeyNotFoundException) { return Results.BadRequest(new { message = ex.Message }); } } private static async Task ConflictsAsync( [FromBody] ConflictRequest request, ViolationEventService eventService, SeverityFusionService fusionService, ConflictHandlingService conflictService, CancellationToken cancellationToken) { try { await eventService.EmitAsync(new ViolationEventRequest(request.SnapshotId), cancellationToken).ConfigureAwait(false); var fused = await fusionService.FuseAsync(request.SnapshotId, cancellationToken).ConfigureAwait(false); var conflicts = await conflictService.ComputeAsync(request.SnapshotId, fused, cancellationToken).ConfigureAwait(false); return Results.Json(new { conflicts }); } catch (Exception ex) when (ex is ArgumentException or KeyNotFoundException) { return Results.BadRequest(new { message = ex.Message }); } } }