feat: Add UI benchmark driver and scenarios for graph interactions
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
- Introduced `ui_bench_driver.mjs` to read scenarios and fixture manifest, generating a deterministic run plan. - Created `ui_bench_plan.md` outlining the purpose, scope, and next steps for the benchmark. - Added `ui_bench_scenarios.json` containing various scenarios for graph UI interactions. - Implemented tests for CLI commands, ensuring bundle verification and telemetry defaults. - Developed schemas for orchestrator components, including replay manifests and event envelopes. - Added mock API for risk management, including listing and statistics functionalities. - Implemented models for risk profiles and query options to support the new API.
This commit is contained in:
@@ -24,11 +24,11 @@ public sealed class CycloneDxComposer
|
||||
private const string InventoryMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.6";
|
||||
private const string UsageMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.6; view=usage";
|
||||
|
||||
public SbomCompositionResult Compose(SbomCompositionRequest request)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
if (request.LayerFragments.IsDefaultOrEmpty)
|
||||
{
|
||||
public SbomCompositionResult Compose(SbomCompositionRequest request)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
if (request.LayerFragments.IsDefaultOrEmpty)
|
||||
{
|
||||
throw new ArgumentException("At least one layer fragment is required.", nameof(request));
|
||||
}
|
||||
|
||||
@@ -48,9 +48,9 @@ public sealed class CycloneDxComposer
|
||||
.Where(static component => component.Usage.UsedByEntrypoint)
|
||||
.ToImmutableArray();
|
||||
|
||||
CycloneDxArtifact? usageArtifact = null;
|
||||
if (!usageComponents.IsEmpty)
|
||||
{
|
||||
CycloneDxArtifact? usageArtifact = null;
|
||||
if (!usageComponents.IsEmpty)
|
||||
{
|
||||
usageArtifact = BuildArtifact(
|
||||
request,
|
||||
graph,
|
||||
@@ -59,15 +59,36 @@ public sealed class CycloneDxComposer
|
||||
generatedAt,
|
||||
UsageMediaTypeJson,
|
||||
UsageMediaTypeProtobuf);
|
||||
}
|
||||
|
||||
return new SbomCompositionResult
|
||||
{
|
||||
Inventory = inventoryArtifact,
|
||||
Usage = usageArtifact,
|
||||
Graph = graph,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var compositionRecipeJson = BuildCompositionRecipeJson(graph, generatedAt);
|
||||
var compositionRecipeSha = ComputeSha256(compositionRecipeJson);
|
||||
var compositionRecipeUri = $"cas://sbom/composition/{compositionRecipeSha}.json";
|
||||
|
||||
inventoryArtifact = inventoryArtifact with
|
||||
{
|
||||
MerkleRoot = compositionRecipeSha,
|
||||
CompositionRecipeUri = compositionRecipeUri,
|
||||
};
|
||||
|
||||
if (usageArtifact is not null)
|
||||
{
|
||||
usageArtifact = usageArtifact with
|
||||
{
|
||||
MerkleRoot = compositionRecipeSha,
|
||||
CompositionRecipeUri = compositionRecipeUri,
|
||||
};
|
||||
}
|
||||
|
||||
return new SbomCompositionResult
|
||||
{
|
||||
Inventory = inventoryArtifact,
|
||||
Usage = usageArtifact,
|
||||
Graph = graph,
|
||||
CompositionRecipeJson = compositionRecipeJson,
|
||||
CompositionRecipeSha256 = compositionRecipeSha,
|
||||
};
|
||||
}
|
||||
|
||||
private CycloneDxArtifact BuildArtifact(
|
||||
SbomCompositionRequest request,
|
||||
@@ -92,6 +113,7 @@ public sealed class CycloneDxComposer
|
||||
: null;
|
||||
|
||||
request.AdditionalProperties?.TryGetValue("stellaops:composition.manifest", out var compositionUri);
|
||||
request.AdditionalProperties?.TryGetValue("stellaops:composition.recipe", out var compositionRecipeUri);
|
||||
|
||||
return new CycloneDxArtifact
|
||||
{
|
||||
@@ -104,12 +126,38 @@ public sealed class CycloneDxComposer
|
||||
ContentHash = jsonHash,
|
||||
MerkleRoot = merkleRoot,
|
||||
CompositionUri = compositionUri,
|
||||
CompositionRecipeUri = compositionRecipeUri,
|
||||
JsonMediaType = jsonMediaType,
|
||||
ProtobufBytes = protobufBytes,
|
||||
ProtobufSha256 = protobufHash,
|
||||
ProtobufMediaType = protobufMediaType,
|
||||
};
|
||||
}
|
||||
|
||||
private static byte[] BuildCompositionRecipeJson(ComponentGraph graph, DateTimeOffset generatedAt)
|
||||
{
|
||||
var recipe = new
|
||||
{
|
||||
schema = "stellaops.composition.recipe@1",
|
||||
generatedAt = ScannerTimestamps.ToIso8601(generatedAt),
|
||||
layers = graph.Layers.Select(layer => new
|
||||
{
|
||||
layer.LayerDigest,
|
||||
components = layer.Components
|
||||
.Select(component => component.Identity.Key)
|
||||
.OrderBy(key => key, StringComparer.Ordinal)
|
||||
.ToArray(),
|
||||
}).OrderBy(entry => entry.LayerDigest, StringComparer.Ordinal).ToArray(),
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(recipe, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
WriteIndented = false,
|
||||
});
|
||||
|
||||
return Encoding.UTF8.GetBytes(json);
|
||||
}
|
||||
|
||||
private Bom BuildBom(
|
||||
SbomCompositionRequest request,
|
||||
|
||||
Reference in New Issue
Block a user