// ----------------------------------------------------------------------------- // RuntimeTracesEndpoints.cs // Sprint: SPRINT_20260107_006_002_FE_diff_runtime_tabs // Task: DR-014 — Runtime traces API endpoints // ----------------------------------------------------------------------------- using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using StellaOps.Findings.Ledger.WebService.Contracts; namespace StellaOps.Findings.Ledger.WebService.Endpoints; /// /// API endpoints for runtime traces evidence. /// public static class RuntimeTracesEndpoints { /// /// Maps runtime traces endpoints to the application. /// public static void MapRuntimeTracesEndpoints(this WebApplication app) { var group = app.MapGroup("/api/v1/findings") .WithTags("Runtime Evidence") .RequireAuthorization(); // GET /api/v1/findings/{findingId}/runtime/traces group.MapGet("/{findingId:guid}/runtime/traces", GetRuntimeTraces) .WithName("GetRuntimeTraces") .WithDescription("Get runtime function traces for a finding") .Produces(200) .Produces(404); // GET /api/v1/findings/{findingId}/runtime/score group.MapGet("/{findingId:guid}/runtime/score", GetRtsScore) .WithName("GetRtsScore") .WithDescription("Get Runtime Trustworthiness Score for a finding") .Produces(200) .Produces(404); } /// /// Gets runtime function traces for a finding. /// private static async Task, NotFound>> GetRuntimeTraces( Guid findingId, IRuntimeTracesService service, CancellationToken ct, [FromQuery] int? limit = null, [FromQuery] string? sortBy = null) { var options = new RuntimeTracesQueryOptions { Limit = limit ?? 50, SortBy = sortBy ?? "hits" }; var traces = await service.GetTracesAsync(findingId, options, ct); return traces is not null ? TypedResults.Ok(traces) : TypedResults.NotFound(); } /// /// Gets the RTS score for a finding. /// private static async Task, NotFound>> GetRtsScore( Guid findingId, IRuntimeTracesService service, CancellationToken ct) { var score = await service.GetRtsScoreAsync(findingId, ct); return score is not null ? TypedResults.Ok(score) : TypedResults.NotFound(); } } /// /// Query options for runtime traces. /// public sealed record RuntimeTracesQueryOptions { /// /// Maximum number of traces to return. /// public int Limit { get; init; } = 50; /// /// Sort by field (hits, recent). /// public string SortBy { get; init; } = "hits"; } /// /// Service for retrieving runtime traces. /// public interface IRuntimeTracesService { /// /// Gets runtime traces for a finding. /// /// Finding identifier. /// Query options. /// Cancellation token. /// Runtime traces response or null if not found. Task GetTracesAsync( Guid findingId, RuntimeTracesQueryOptions options, CancellationToken ct); /// /// Gets RTS score for a finding. /// /// Finding identifier. /// Cancellation token. /// RTS score response or null if not found. Task GetRtsScoreAsync(Guid findingId, CancellationToken ct); }