feat(ruby): Implement RubyManifestParser for parsing gem groups and dependencies
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
feat(ruby): Add RubyVendorArtifactCollector to collect vendor artifacts test(deno): Add golden tests for Deno analyzer with various fixtures test(deno): Create Deno module and package files for testing test(deno): Implement Deno lock and import map for dependency management test(deno): Add FFI and worker scripts for Deno testing feat(ruby): Set up Ruby workspace with Gemfile and dependencies feat(ruby): Add expected output for Ruby workspace tests feat(signals): Introduce CallgraphManifest model for signal processing
This commit is contained in:
@@ -206,15 +206,22 @@ app.MapGet("/readyz", (SignalsStartupState state, SignalsSealedModeMonitor seale
|
||||
: Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||
}).AllowAnonymous();
|
||||
|
||||
var fallbackAllowed = !bootstrap.Authority.Enabled || bootstrap.Authority.AllowAnonymousFallback;
|
||||
|
||||
var signalsGroup = app.MapGroup("/signals");
|
||||
|
||||
signalsGroup.MapGet("/ping", (HttpContext context, SignalsOptions options, SignalsSealedModeMonitor sealedModeMonitor) =>
|
||||
Program.TryAuthorize(context, requiredScope: SignalsPolicies.Read, fallbackAllowed: options.Authority.AllowAnonymousFallback, out var authFailure) &&
|
||||
Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure)
|
||||
? Results.NoContent()
|
||||
: authFailure ?? sealedFailure ?? Results.Unauthorized()).WithName("SignalsPing");
|
||||
{
|
||||
if (!Program.TryAuthorize(context, requiredScope: SignalsPolicies.Read, fallbackAllowed: options.Authority.AllowAnonymousFallback, out var authFailure))
|
||||
{
|
||||
return authFailure ?? Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
{
|
||||
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
|
||||
return Results.NoContent();
|
||||
}).WithName("SignalsPing");
|
||||
|
||||
signalsGroup.MapGet("/status", (HttpContext context, SignalsOptions options, SignalsSealedModeMonitor sealedModeMonitor) =>
|
||||
{
|
||||
@@ -245,33 +252,37 @@ signalsGroup.MapPost("/callgraphs", async Task<IResult> (
|
||||
SignalsSealedModeMonitor sealedModeMonitor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure) ||
|
||||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure))
|
||||
{
|
||||
return authFailure ?? sealedFailure ?? Results.Unauthorized();
|
||||
return authFailure ?? Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
{
|
||||
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await ingestionService.IngestAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
return Results.Accepted($"/signals/callgraphs/{result.CallgraphId}", result);
|
||||
}
|
||||
catch (CallgraphIngestionValidationException ex)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message });
|
||||
}
|
||||
catch (CallgraphParserNotFoundException ex)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message });
|
||||
}
|
||||
catch (CallgraphParserValidationException ex)
|
||||
{
|
||||
return Results.UnprocessableEntity(new { error = ex.Message });
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await ingestionService.IngestAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
return Results.Accepted($"/signals/callgraphs/{result.CallgraphId}", result);
|
||||
}
|
||||
catch (CallgraphIngestionValidationException ex)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message });
|
||||
}
|
||||
catch (CallgraphParserNotFoundException ex)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message });
|
||||
}
|
||||
catch (CallgraphParserValidationException ex)
|
||||
{
|
||||
return Results.UnprocessableEntity(new { error = ex.Message });
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message });
|
||||
}
|
||||
}).WithName("SignalsCallgraphIngest");
|
||||
|
||||
signalsGroup.MapGet("/callgraphs/{callgraphId}", async Task<IResult> (
|
||||
@@ -282,10 +293,14 @@ signalsGroup.MapGet("/callgraphs/{callgraphId}", async Task<IResult> (
|
||||
SignalsSealedModeMonitor sealedModeMonitor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure) ||
|
||||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure))
|
||||
{
|
||||
return authFailure ?? sealedFailure ?? Results.Unauthorized();
|
||||
return authFailure ?? Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
{
|
||||
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(callgraphId))
|
||||
@@ -296,7 +311,46 @@ signalsGroup.MapGet("/callgraphs/{callgraphId}", async Task<IResult> (
|
||||
var document = await callgraphRepository.GetByIdAsync(callgraphId.Trim(), cancellationToken).ConfigureAwait(false);
|
||||
return document is null ? Results.NotFound() : Results.Ok(document);
|
||||
}).WithName("SignalsCallgraphGet");
|
||||
|
||||
|
||||
signalsGroup.MapGet("/callgraphs/{callgraphId}/manifest", async Task<IResult> (
|
||||
HttpContext context,
|
||||
SignalsOptions options,
|
||||
string callgraphId,
|
||||
ICallgraphRepository callgraphRepository,
|
||||
SignalsSealedModeMonitor sealedModeMonitor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure))
|
||||
{
|
||||
return authFailure ?? Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
{
|
||||
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(callgraphId))
|
||||
{
|
||||
return Results.BadRequest(new { error = "callgraphId is required." });
|
||||
}
|
||||
|
||||
var document = await callgraphRepository.GetByIdAsync(callgraphId.Trim(), cancellationToken).ConfigureAwait(false);
|
||||
if (document is null || string.IsNullOrWhiteSpace(document.Artifact.ManifestPath))
|
||||
{
|
||||
return Results.NotFound();
|
||||
}
|
||||
|
||||
var manifestPath = Path.Combine(options.Storage.RootPath, document.Artifact.ManifestPath);
|
||||
if (!File.Exists(manifestPath))
|
||||
{
|
||||
return Results.NotFound(new { error = "manifest not found" });
|
||||
}
|
||||
|
||||
var bytes = await File.ReadAllBytesAsync(manifestPath, cancellationToken).ConfigureAwait(false);
|
||||
return Results.File(bytes, "application/json");
|
||||
}).WithName("SignalsCallgraphManifestGet");
|
||||
|
||||
signalsGroup.MapPost("/runtime-facts", async Task<IResult> (
|
||||
HttpContext context,
|
||||
SignalsOptions options,
|
||||
@@ -305,10 +359,14 @@ signalsGroup.MapPost("/runtime-facts", async Task<IResult> (
|
||||
SignalsSealedModeMonitor sealedModeMonitor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure) ||
|
||||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure))
|
||||
{
|
||||
return authFailure ?? sealedFailure ?? Results.Unauthorized();
|
||||
return authFailure ?? Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
{
|
||||
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -325,15 +383,19 @@ signalsGroup.MapPost("/runtime-facts", async Task<IResult> (
|
||||
signalsGroup.MapPost("/runtime-facts/ndjson", async Task<IResult> (
|
||||
HttpContext context,
|
||||
SignalsOptions options,
|
||||
RuntimeFactsStreamMetadata metadata,
|
||||
[AsParameters] RuntimeFactsStreamMetadata metadata,
|
||||
IRuntimeFactsIngestionService ingestionService,
|
||||
SignalsSealedModeMonitor sealedModeMonitor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure) ||
|
||||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Write, options.Authority.AllowAnonymousFallback, out var authFailure))
|
||||
{
|
||||
return authFailure ?? sealedFailure ?? Results.Unauthorized();
|
||||
return authFailure ?? Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
{
|
||||
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
|
||||
if (metadata is null || string.IsNullOrWhiteSpace(metadata.CallgraphId))
|
||||
@@ -341,13 +403,7 @@ signalsGroup.MapPost("/runtime-facts/ndjson", async Task<IResult> (
|
||||
return Results.BadRequest(new { error = "callgraphId is required." });
|
||||
}
|
||||
|
||||
var subject = new ReachabilitySubject
|
||||
{
|
||||
ScanId = metadata.ScanId,
|
||||
ImageDigest = metadata.ImageDigest,
|
||||
Component = metadata.Component,
|
||||
Version = metadata.Version
|
||||
};
|
||||
var subject = metadata.ToSubject();
|
||||
|
||||
var isGzip = string.Equals(context.Request.Headers.ContentEncoding, "gzip", StringComparison.OrdinalIgnoreCase);
|
||||
var events = await RuntimeFactsNdjsonReader.ReadAsync(context.Request.Body, isGzip, cancellationToken).ConfigureAwait(false);
|
||||
@@ -382,10 +438,14 @@ signalsGroup.MapGet("/facts/{subjectKey}", async Task<IResult> (
|
||||
SignalsSealedModeMonitor sealedModeMonitor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure) ||
|
||||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var authFailure))
|
||||
{
|
||||
return authFailure ?? sealedFailure ?? Results.Unauthorized();
|
||||
return authFailure ?? Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
{
|
||||
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(subjectKey))
|
||||
@@ -405,10 +465,14 @@ signalsGroup.MapPost("/reachability/recompute", async Task<IResult> (
|
||||
SignalsSealedModeMonitor sealedModeMonitor,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Admin, options.Authority.AllowAnonymousFallback, out var authFailure) ||
|
||||
!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Admin, options.Authority.AllowAnonymousFallback, out var authFailure))
|
||||
{
|
||||
return authFailure ?? sealedFailure ?? Results.Unauthorized();
|
||||
return authFailure ?? Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (!Program.TryEnsureSealedMode(sealedModeMonitor, out var sealedFailure))
|
||||
{
|
||||
return sealedFailure ?? Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -434,26 +498,6 @@ signalsGroup.MapPost("/reachability/recompute", async Task<IResult> (
|
||||
}
|
||||
}).WithName("SignalsReachabilityRecompute");
|
||||
|
||||
signalsGroup.MapGet("/facts/{subjectKey}", async Task<IResult> (
|
||||
HttpContext context,
|
||||
SignalsOptions options,
|
||||
string subjectKey,
|
||||
IReachabilityFactRepository factRepository,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (!Program.TryAuthorize(context, SignalsPolicies.Read, options.Authority.AllowAnonymousFallback, out var failure))
|
||||
{
|
||||
return failure ?? Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(subjectKey))
|
||||
{
|
||||
return Results.BadRequest(new { error = "subjectKey is required." });
|
||||
}
|
||||
|
||||
var fact = await factRepository.GetBySubjectAsync(subjectKey.Trim(), cancellationToken).ConfigureAwait(false);
|
||||
return fact is null ? Results.NotFound() : Results.Ok(fact);
|
||||
}).WithName("SignalsFactsGet");
|
||||
|
||||
app.Run();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user