Refactor code structure for improved readability and maintainability; optimize performance in key functions.
This commit is contained in:
@@ -62,6 +62,14 @@ builder.Services.AddSingleton<IOrchestratorControlService>(sp =>
|
||||
sp.GetRequiredService<IOrchestratorControlRepository>(),
|
||||
SbomMetrics.Meter));
|
||||
builder.Services.AddSingleton<IWatermarkService, InMemoryWatermarkService>();
|
||||
builder.Services.AddOptions<SbomLedgerOptions>()
|
||||
.Bind(builder.Configuration.GetSection("SbomService:Ledger"));
|
||||
builder.Services.AddSingleton<ISbomLedgerRepository, InMemorySbomLedgerRepository>();
|
||||
builder.Services.AddSingleton<ISbomNormalizationService, SbomNormalizationService>();
|
||||
builder.Services.AddSingleton<ISbomQualityScorer, SbomQualityScorer>();
|
||||
builder.Services.AddSingleton<ISbomLedgerService, SbomLedgerService>();
|
||||
builder.Services.AddSingleton<ISbomAnalysisTrigger, InMemorySbomAnalysisTrigger>();
|
||||
builder.Services.AddSingleton<ISbomUploadService, SbomUploadService>();
|
||||
|
||||
builder.Services.AddSingleton<IProjectionRepository>(sp =>
|
||||
{
|
||||
@@ -454,6 +462,162 @@ app.MapGet("/sbom/versions", async Task<IResult> (
|
||||
return Results.Ok(result.Result);
|
||||
});
|
||||
|
||||
var sbomUploadHandler = async Task<IResult> (
|
||||
[FromBody] SbomUploadRequest request,
|
||||
[FromServices] ISbomUploadService uploadService,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var (response, validation) = await uploadService.UploadAsync(request, cancellationToken);
|
||||
|
||||
if (!validation.Valid)
|
||||
{
|
||||
return Results.BadRequest(new
|
||||
{
|
||||
error = "sbom_upload_validation_failed",
|
||||
validation
|
||||
});
|
||||
}
|
||||
|
||||
SbomMetrics.LedgerUploadsTotal.Add(1);
|
||||
return Results.Accepted($"/sbom/ledger/history?artifact={Uri.EscapeDataString(response.ArtifactRef)}", response);
|
||||
};
|
||||
|
||||
app.MapPost("/sbom/upload", sbomUploadHandler);
|
||||
app.MapPost("/api/v1/sbom/upload", sbomUploadHandler);
|
||||
|
||||
app.MapGet("/sbom/ledger/history", async Task<IResult> (
|
||||
[FromServices] ISbomLedgerService ledgerService,
|
||||
[FromQuery] string? artifact,
|
||||
[FromQuery] string? cursor,
|
||||
[FromQuery] int? limit,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(artifact))
|
||||
{
|
||||
return Results.BadRequest(new { error = "artifact is required" });
|
||||
}
|
||||
|
||||
if (cursor is { Length: > 0 } && !int.TryParse(cursor, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
|
||||
{
|
||||
return Results.BadRequest(new { error = "cursor must be an integer offset" });
|
||||
}
|
||||
|
||||
var offset = cursor is null ? 0 : int.Parse(cursor, CultureInfo.InvariantCulture);
|
||||
var pageSize = NormalizeLimit(limit, 50, 200);
|
||||
var history = await ledgerService.GetHistoryAsync(artifact.Trim(), pageSize, offset, cancellationToken);
|
||||
if (history is null)
|
||||
{
|
||||
return Results.NotFound(new { error = "ledger history not found" });
|
||||
}
|
||||
|
||||
return Results.Ok(history);
|
||||
});
|
||||
|
||||
app.MapGet("/sbom/ledger/point", async Task<IResult> (
|
||||
[FromServices] ISbomLedgerService ledgerService,
|
||||
[FromQuery] string? artifact,
|
||||
[FromQuery] string? at,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(artifact))
|
||||
{
|
||||
return Results.BadRequest(new { error = "artifact is required" });
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(at) || !DateTimeOffset.TryParse(at, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var atUtc))
|
||||
{
|
||||
return Results.BadRequest(new { error = "at must be an ISO-8601 timestamp" });
|
||||
}
|
||||
|
||||
var result = await ledgerService.GetAtTimeAsync(artifact.Trim(), atUtc, cancellationToken);
|
||||
if (result is null)
|
||||
{
|
||||
return Results.NotFound(new { error = "ledger point not found" });
|
||||
}
|
||||
|
||||
return Results.Ok(result);
|
||||
});
|
||||
|
||||
app.MapGet("/sbom/ledger/range", async Task<IResult> (
|
||||
[FromServices] ISbomLedgerService ledgerService,
|
||||
[FromQuery] string? artifact,
|
||||
[FromQuery] string? start,
|
||||
[FromQuery] string? end,
|
||||
[FromQuery] string? cursor,
|
||||
[FromQuery] int? limit,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(artifact))
|
||||
{
|
||||
return Results.BadRequest(new { error = "artifact is required" });
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(start) || !DateTimeOffset.TryParse(start, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var startUtc))
|
||||
{
|
||||
return Results.BadRequest(new { error = "start must be an ISO-8601 timestamp" });
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(end) || !DateTimeOffset.TryParse(end, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var endUtc))
|
||||
{
|
||||
return Results.BadRequest(new { error = "end must be an ISO-8601 timestamp" });
|
||||
}
|
||||
|
||||
if (cursor is { Length: > 0 } && !int.TryParse(cursor, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
|
||||
{
|
||||
return Results.BadRequest(new { error = "cursor must be an integer offset" });
|
||||
}
|
||||
|
||||
var offset = cursor is null ? 0 : int.Parse(cursor, CultureInfo.InvariantCulture);
|
||||
var pageSize = NormalizeLimit(limit, 50, 200);
|
||||
var history = await ledgerService.GetRangeAsync(artifact.Trim(), startUtc, endUtc, pageSize, offset, cancellationToken);
|
||||
if (history is null)
|
||||
{
|
||||
return Results.NotFound(new { error = "ledger range not found" });
|
||||
}
|
||||
|
||||
return Results.Ok(history);
|
||||
});
|
||||
|
||||
app.MapGet("/sbom/ledger/diff", async Task<IResult> (
|
||||
[FromServices] ISbomLedgerService ledgerService,
|
||||
[FromQuery] string? before,
|
||||
[FromQuery] string? after,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Guid.TryParse(before, out var beforeId) || !Guid.TryParse(after, out var afterId))
|
||||
{
|
||||
return Results.BadRequest(new { error = "before and after must be GUIDs" });
|
||||
}
|
||||
|
||||
var diff = await ledgerService.DiffAsync(beforeId, afterId, cancellationToken);
|
||||
if (diff is null)
|
||||
{
|
||||
return Results.NotFound(new { error = "diff not found" });
|
||||
}
|
||||
|
||||
SbomMetrics.LedgerDiffsTotal.Add(1);
|
||||
return Results.Ok(diff);
|
||||
});
|
||||
|
||||
app.MapGet("/sbom/ledger/lineage", async Task<IResult> (
|
||||
[FromServices] ISbomLedgerService ledgerService,
|
||||
[FromQuery] string? artifact,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(artifact))
|
||||
{
|
||||
return Results.BadRequest(new { error = "artifact is required" });
|
||||
}
|
||||
|
||||
var lineage = await ledgerService.GetLineageAsync(artifact.Trim(), cancellationToken);
|
||||
if (lineage is null)
|
||||
{
|
||||
return Results.NotFound(new { error = "lineage not found" });
|
||||
}
|
||||
|
||||
return Results.Ok(lineage);
|
||||
});
|
||||
|
||||
app.MapGet("/sboms/{snapshotId}/projection", async Task<IResult> (
|
||||
[FromServices] ISbomQueryService service,
|
||||
[FromRoute] string? snapshotId,
|
||||
@@ -543,6 +707,34 @@ app.MapGet("/internal/sbom/asset-events", async Task<IResult> (
|
||||
return Results.Ok(events);
|
||||
});
|
||||
|
||||
app.MapGet("/internal/sbom/ledger/audit", async Task<IResult> (
|
||||
[FromServices] ISbomLedgerService ledgerService,
|
||||
[FromQuery] string? artifact,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(artifact))
|
||||
{
|
||||
return Results.BadRequest(new { error = "artifact is required" });
|
||||
}
|
||||
|
||||
var audit = await ledgerService.GetAuditAsync(artifact.Trim(), cancellationToken);
|
||||
return Results.Ok(audit.OrderBy(a => a.TimestampUtc).ToList());
|
||||
});
|
||||
|
||||
app.MapGet("/internal/sbom/analysis/jobs", async Task<IResult> (
|
||||
[FromServices] ISbomLedgerService ledgerService,
|
||||
[FromQuery] string? artifact,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(artifact))
|
||||
{
|
||||
return Results.BadRequest(new { error = "artifact is required" });
|
||||
}
|
||||
|
||||
var jobs = await ledgerService.ListAnalysisJobsAsync(artifact.Trim(), cancellationToken);
|
||||
return Results.Ok(jobs.OrderBy(j => j.CreatedAtUtc).ToList());
|
||||
});
|
||||
|
||||
app.MapPost("/internal/sbom/events/backfill", async Task<IResult> (
|
||||
[FromServices] IProjectionRepository repository,
|
||||
[FromServices] ISbomEventPublisher publisher,
|
||||
@@ -632,6 +824,19 @@ app.MapGet("/internal/sbom/resolver-feed/export", async Task<IResult> (
|
||||
return Results.Text(ndjson, "application/x-ndjson");
|
||||
});
|
||||
|
||||
app.MapPost("/internal/sbom/retention/prune", async Task<IResult> (
|
||||
[FromServices] ISbomLedgerService ledgerService,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var result = await ledgerService.ApplyRetentionAsync(cancellationToken);
|
||||
if (result.VersionsPruned > 0)
|
||||
{
|
||||
SbomMetrics.LedgerRetentionPrunedTotal.Add(result.VersionsPruned);
|
||||
}
|
||||
|
||||
return Results.Ok(result);
|
||||
});
|
||||
|
||||
app.MapGet("/internal/orchestrator/sources", async Task<IResult> (
|
||||
[FromQuery] string? tenant,
|
||||
[FromServices] IOrchestratorRepository repository,
|
||||
|
||||
Reference in New Issue
Block a user