sprints completion. new product advisories prepared
This commit is contained in:
@@ -59,6 +59,16 @@ internal static class ReachabilityEndpoints
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireAuthorization(ScannerPolicies.ScansRead);
|
||||
|
||||
// Sprint: SPRINT_20260112_004_SCANNER_reachability_trace_runtime_evidence
|
||||
// GET /scans/{scanId}/reachability/traces/export - Trace export with runtime evidence
|
||||
scansGroup.MapGet("/{scanId}/reachability/traces/export", HandleTraceExportAsync)
|
||||
.WithName("scanner.scans.reachability.traces.export")
|
||||
.WithTags("Reachability")
|
||||
.Produces<ReachabilityTraceExportDto>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.RequireAuthorization(ScannerPolicies.ScansRead);
|
||||
}
|
||||
|
||||
private static async Task<IResult> HandleComputeReachabilityAsync(
|
||||
@@ -315,9 +325,145 @@ internal static class ReachabilityEndpoints
|
||||
return Json(response, StatusCodes.Status200OK);
|
||||
}
|
||||
|
||||
// Sprint: SPRINT_20260112_004_SCANNER_reachability_trace_runtime_evidence (SCAN-RT-003)
|
||||
private static async Task<IResult> HandleTraceExportAsync(
|
||||
string scanId,
|
||||
string? format,
|
||||
bool? includeRuntimeEvidence,
|
||||
double? minReachabilityScore,
|
||||
bool? runtimeConfirmedOnly,
|
||||
IScanCoordinator coordinator,
|
||||
IReachabilityQueryService queryService,
|
||||
HttpContext context,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(coordinator);
|
||||
ArgumentNullException.ThrowIfNull(queryService);
|
||||
|
||||
if (!ScanId.TryParse(scanId, out var parsed))
|
||||
{
|
||||
return ProblemResultFactory.Create(
|
||||
context,
|
||||
ProblemTypes.Validation,
|
||||
"Invalid scan identifier",
|
||||
StatusCodes.Status400BadRequest,
|
||||
detail: "Scan identifier is required.");
|
||||
}
|
||||
|
||||
var snapshot = await coordinator.GetAsync(parsed, cancellationToken).ConfigureAwait(false);
|
||||
if (snapshot is null)
|
||||
{
|
||||
return ProblemResultFactory.Create(
|
||||
context,
|
||||
ProblemTypes.NotFound,
|
||||
"Scan not found",
|
||||
StatusCodes.Status404NotFound,
|
||||
detail: "Requested scan could not be located.");
|
||||
}
|
||||
|
||||
// Determine export format (default to json-lines for determinism)
|
||||
var exportFormat = (format?.ToLowerInvariant()) switch
|
||||
{
|
||||
"graphson" => "graphson",
|
||||
"ndjson" or "json-lines" => "json-lines",
|
||||
_ => "json-lines"
|
||||
};
|
||||
|
||||
var options = new TraceExportOptions
|
||||
{
|
||||
Format = exportFormat,
|
||||
IncludeRuntimeEvidence = includeRuntimeEvidence ?? true,
|
||||
MinReachabilityScore = minReachabilityScore,
|
||||
RuntimeConfirmedOnly = runtimeConfirmedOnly ?? false
|
||||
};
|
||||
|
||||
var export = await queryService.ExportTracesAsync(parsed, options, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (export is null)
|
||||
{
|
||||
return ProblemResultFactory.Create(
|
||||
context,
|
||||
ProblemTypes.NotFound,
|
||||
"No reachability data",
|
||||
StatusCodes.Status404NotFound,
|
||||
detail: "No reachability data found for this scan.");
|
||||
}
|
||||
|
||||
var response = new ReachabilityTraceExportDto(
|
||||
Format: export.Format,
|
||||
CanonicalizationMethod: "StellaOps.Canonical.Json",
|
||||
ContentDigest: export.ContentDigest,
|
||||
Timestamp: export.Timestamp,
|
||||
NodeCount: export.Nodes.Count,
|
||||
EdgeCount: export.Edges.Count,
|
||||
RuntimeCoverage: export.RuntimeCoverage,
|
||||
AverageReachabilityScore: export.AverageReachabilityScore,
|
||||
Nodes: export.Nodes.Select(n => new TraceNodeDto(
|
||||
Id: n.Id,
|
||||
SymbolId: n.SymbolId,
|
||||
ReachabilityScore: n.ReachabilityScore,
|
||||
RuntimeConfirmed: n.RuntimeConfirmed,
|
||||
RuntimeObservationCount: n.RuntimeObservationCount,
|
||||
Evidence: n.Evidence)).ToList(),
|
||||
Edges: export.Edges.Select(e => new TraceEdgeDto(
|
||||
From: e.From,
|
||||
To: e.To,
|
||||
Kind: e.Kind,
|
||||
Confidence: e.Confidence,
|
||||
RuntimeConfirmed: e.RuntimeConfirmed,
|
||||
RuntimeObservationCount: e.RuntimeObservationCount,
|
||||
Evidence: e.Evidence)).ToList());
|
||||
|
||||
return Json(response, StatusCodes.Status200OK);
|
||||
}
|
||||
|
||||
private static IResult Json<T>(T value, int statusCode)
|
||||
{
|
||||
var payload = JsonSerializer.Serialize(value, SerializerOptions);
|
||||
return Results.Content(payload, "application/json", System.Text.Encoding.UTF8, statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
// Sprint: SPRINT_20260112_004_SCANNER_reachability_trace_runtime_evidence
|
||||
// Trace export DTOs
|
||||
|
||||
/// <summary>Options for trace export.</summary>
|
||||
public sealed record TraceExportOptions
|
||||
{
|
||||
public string Format { get; init; } = "json-lines";
|
||||
public bool IncludeRuntimeEvidence { get; init; } = true;
|
||||
public double? MinReachabilityScore { get; init; }
|
||||
public bool RuntimeConfirmedOnly { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>Trace export response.</summary>
|
||||
public sealed record ReachabilityTraceExportDto(
|
||||
string Format,
|
||||
string CanonicalizationMethod,
|
||||
string ContentDigest,
|
||||
DateTimeOffset Timestamp,
|
||||
int NodeCount,
|
||||
int EdgeCount,
|
||||
double RuntimeCoverage,
|
||||
double? AverageReachabilityScore,
|
||||
IReadOnlyList<TraceNodeDto> Nodes,
|
||||
IReadOnlyList<TraceEdgeDto> Edges);
|
||||
|
||||
/// <summary>Node in trace export.</summary>
|
||||
public sealed record TraceNodeDto(
|
||||
string Id,
|
||||
string SymbolId,
|
||||
double? ReachabilityScore,
|
||||
bool? RuntimeConfirmed,
|
||||
ulong? RuntimeObservationCount,
|
||||
IReadOnlyList<string>? Evidence);
|
||||
|
||||
/// <summary>Edge in trace export.</summary>
|
||||
public sealed record TraceEdgeDto(
|
||||
string From,
|
||||
string To,
|
||||
string Kind,
|
||||
double Confidence,
|
||||
bool? RuntimeConfirmed,
|
||||
ulong? RuntimeObservationCount,
|
||||
IReadOnlyList<string>? Evidence);
|
||||
|
||||
Reference in New Issue
Block a user