prep docs and service updates
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

This commit is contained in:
master
2025-11-21 06:56:36 +00:00
parent ca35db9ef4
commit d519782a8f
242 changed files with 17293 additions and 13367 deletions

View File

@@ -59,6 +59,10 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
var artifactBytes = Convert.FromBase64String(request.ArtifactContentBase64);
await using var parseStream = new MemoryStream(artifactBytes, writable: false);
var parseResult = await parser.ParseAsync(parseStream, cancellationToken).ConfigureAwait(false);
var schemaVersion = !string.IsNullOrWhiteSpace(request.SchemaVersion)
? request.SchemaVersion!
: parseResult.SchemaVersion;
var analyzerMeta = request.Analyzer ?? parseResult.Analyzer;
parseStream.Position = 0;
var artifactHash = ComputeSha256(artifactBytes);
@@ -71,8 +75,10 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
Version = request.Version,
ArtifactHash = artifactHash,
GraphHash = graphHash,
SchemaVersion = schemaVersion,
NodeCount = parseResult.Nodes.Count,
EdgeCount = parseResult.Edges.Count,
RootCount = parseResult.Roots.Count,
CreatedAt = timeProvider.GetUtcNow()
};
@@ -95,14 +101,15 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
var document = new CallgraphDocument
{
Language = parser.Language,
Component = request.Component,
Version = request.Version,
Nodes = new List<CallgraphNode>(parseResult.Nodes),
Edges = new List<CallgraphEdge>(parseResult.Edges),
Metadata = request.Metadata is null
? null
: new Dictionary<string, string?>(request.Metadata, StringComparer.OrdinalIgnoreCase),
Language = parser.Language,
Component = request.Component,
Version = request.Version,
Nodes = new List<CallgraphNode>(parseResult.Nodes),
Edges = new List<CallgraphEdge>(parseResult.Edges),
Roots = new List<CallgraphRoot>(parseResult.Roots),
Metadata = request.Metadata is null
? null
: new Dictionary<string, string?>(request.Metadata, StringComparer.OrdinalIgnoreCase),
Artifact = new CallgraphArtifactMetadata
{
Path = artifactMetadata.Path,
@@ -119,7 +126,16 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
document.Metadata ??= new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
document.Metadata["formatVersion"] = parseResult.FormatVersion;
document.Metadata["schemaVersion"] = schemaVersion;
if (analyzerMeta is not null)
{
foreach (var kv in analyzerMeta)
{
document.Metadata[$"analyzer.{kv.Key}"] = kv.Value;
}
}
document.GraphHash = graphHash;
document.SchemaVersion = schemaVersion;
document = await repository.UpsertAsync(document, cancellationToken).ConfigureAwait(false);
@@ -138,7 +154,11 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
document.Artifact.Hash,
document.Artifact.CasUri,
graphHash,
document.Artifact.ManifestCasUri);
document.Artifact.ManifestCasUri,
schemaVersion,
document.Nodes.Count,
document.Edges.Count,
document.Roots?.Count ?? 0);
}
private static void ValidateRequest(CallgraphIngestRequest request)
@@ -186,18 +206,73 @@ internal sealed class CallgraphIngestionService : ICallgraphIngestionService
private static string ComputeGraphHash(CallgraphParseResult result)
{
var builder = new StringBuilder();
builder.Append("schema|").Append(result.SchemaVersion).AppendLine();
foreach (var node in result.Nodes.OrderBy(n => n.Id, StringComparer.Ordinal))
{
builder.Append(node.Id).Append('|').Append(node.Name).AppendLine();
builder
.Append(node.Id).Append('|')
.Append(node.Name).Append('|')
.Append(node.Kind).Append('|')
.Append(node.Namespace).Append('|')
.Append(node.File).Append('|')
.Append(node.Line?.ToString() ?? string.Empty).Append('|')
.Append(node.Purl).Append('|')
.Append(node.SymbolDigest).Append('|')
.Append(node.BuildId).Append('|')
.Append(node.CodeId).Append('|')
.Append(node.Language).Append('|')
.Append(Join(node.Evidence)).Append('|')
.Append(JoinDict(node.Analyzer))
.AppendLine();
}
foreach (var edge in result.Edges.OrderBy(e => e.SourceId, StringComparer.Ordinal).ThenBy(e => e.TargetId, StringComparer.Ordinal))
{
builder.Append(edge.SourceId).Append("->").Append(edge.TargetId).AppendLine();
builder
.Append(edge.SourceId).Append("->").Append(edge.TargetId).Append('|')
.Append(edge.Type).Append('|')
.Append(edge.Purl).Append('|')
.Append(edge.SymbolDigest).Append('|')
.Append(edge.Confidence?.ToString() ?? string.Empty).Append('|')
.Append(Join(edge.Candidates)).Append('|')
.Append(Join(edge.Evidence))
.AppendLine();
}
foreach (var root in result.Roots.OrderBy(r => r.Id, StringComparer.Ordinal))
{
builder.Append("root|").Append(root.Id).Append('|').Append(root.Phase).Append('|').Append(root.Source).AppendLine();
}
return ComputeSha256(Encoding.UTF8.GetBytes(builder.ToString()));
}
private static string Join(IEnumerable<string>? values)
{
if (values is null)
{
return string.Empty;
}
return string.Join(',', values.OrderBy(v => v, StringComparer.Ordinal));
}
private static string JoinDict(IReadOnlyDictionary<string, string?>? values)
{
if (values is null)
{
return string.Empty;
}
var ordered = new StringBuilder();
foreach (var kv in values.OrderBy(k => k.Key, StringComparer.Ordinal))
{
ordered.Append(kv.Key).Append('=').Append(kv.Value).Append(';');
}
return ordered.ToString();
}
}
/// <summary>

View File

@@ -125,7 +125,13 @@ public sealed class RuntimeFactsIngestionService : IRuntimeFactsIngestionService
continue;
}
var key = new RuntimeFactKey(evt.SymbolId.Trim(), evt.CodeId?.Trim(), evt.LoaderBase?.Trim());
var key = new RuntimeFactKey(
evt.SymbolId.Trim(),
evt.CodeId?.Trim(),
evt.LoaderBase?.Trim(),
evt.Purl?.Trim(),
evt.SymbolDigest?.Trim(),
evt.BuildId?.Trim());
if (!map.TryGetValue(key, out var document))
{
document = new RuntimeFactDocument
@@ -133,11 +139,15 @@ public sealed class RuntimeFactsIngestionService : IRuntimeFactsIngestionService
SymbolId = key.SymbolId,
CodeId = key.CodeId,
LoaderBase = key.LoaderBase,
Purl = key.Purl,
SymbolDigest = key.SymbolDigest,
BuildId = key.BuildId,
ProcessId = evt.ProcessId,
ProcessName = Normalize(evt.ProcessName),
SocketAddress = Normalize(evt.SocketAddress),
ContainerId = Normalize(evt.ContainerId),
EvidenceUri = Normalize(evt.EvidenceUri),
ObservedAt = evt.ObservedAt,
Metadata = evt.Metadata != null
? new Dictionary<string, string?>(evt.Metadata, StringComparer.Ordinal)
: null
@@ -155,6 +165,10 @@ public sealed class RuntimeFactsIngestionService : IRuntimeFactsIngestionService
}
document.HitCount = Math.Clamp(document.HitCount + Math.Max(evt.HitCount, 1), 1, int.MaxValue);
document.Purl ??= Normalize(evt.Purl);
document.SymbolDigest ??= Normalize(evt.SymbolDigest);
document.BuildId ??= Normalize(evt.BuildId);
document.ObservedAt ??= evt.ObservedAt;
}
return map.Values.ToList();
@@ -194,18 +208,22 @@ public sealed class RuntimeFactsIngestionService : IRuntimeFactsIngestionService
{
foreach (var fact in existing)
{
var key = new RuntimeFactKey(fact.SymbolId, fact.CodeId, fact.LoaderBase);
var key = new RuntimeFactKey(fact.SymbolId, fact.CodeId, fact.LoaderBase, fact.Purl, fact.SymbolDigest, fact.BuildId);
map[key] = new RuntimeFactDocument
{
SymbolId = fact.SymbolId,
CodeId = fact.CodeId,
LoaderBase = fact.LoaderBase,
Purl = fact.Purl,
SymbolDigest = fact.SymbolDigest,
BuildId = fact.BuildId,
ProcessId = fact.ProcessId,
ProcessName = fact.ProcessName,
SocketAddress = fact.SocketAddress,
ContainerId = fact.ContainerId,
EvidenceUri = fact.EvidenceUri,
HitCount = fact.HitCount,
ObservedAt = fact.ObservedAt,
Metadata = fact.Metadata is null
? null
: new Dictionary<string, string?>(fact.Metadata, StringComparer.Ordinal)
@@ -217,7 +235,7 @@ public sealed class RuntimeFactsIngestionService : IRuntimeFactsIngestionService
{
foreach (var fact in incoming)
{
var key = new RuntimeFactKey(fact.SymbolId, fact.CodeId, fact.LoaderBase);
var key = new RuntimeFactKey(fact.SymbolId, fact.CodeId, fact.LoaderBase, fact.Purl, fact.SymbolDigest, fact.BuildId);
if (!map.TryGetValue(key, out var existingFact))
{
map[key] = new RuntimeFactDocument
@@ -225,12 +243,16 @@ public sealed class RuntimeFactsIngestionService : IRuntimeFactsIngestionService
SymbolId = fact.SymbolId,
CodeId = fact.CodeId,
LoaderBase = fact.LoaderBase,
Purl = fact.Purl,
SymbolDigest = fact.SymbolDigest,
BuildId = fact.BuildId,
ProcessId = fact.ProcessId,
ProcessName = fact.ProcessName,
SocketAddress = fact.SocketAddress,
ContainerId = fact.ContainerId,
EvidenceUri = fact.EvidenceUri,
HitCount = fact.HitCount,
ObservedAt = fact.ObservedAt,
Metadata = fact.Metadata is null
? null
: new Dictionary<string, string?>(fact.Metadata, StringComparer.Ordinal)
@@ -244,6 +266,10 @@ public sealed class RuntimeFactsIngestionService : IRuntimeFactsIngestionService
existingFact.SocketAddress ??= fact.SocketAddress;
existingFact.ContainerId ??= fact.ContainerId;
existingFact.EvidenceUri ??= fact.EvidenceUri;
existingFact.Purl ??= fact.Purl;
existingFact.SymbolDigest ??= fact.SymbolDigest;
existingFact.BuildId ??= fact.BuildId;
existingFact.ObservedAt ??= fact.ObservedAt;
if (fact.Metadata != null && fact.Metadata.Count > 0)
{
existingFact.Metadata ??= new Dictionary<string, string?>(StringComparer.Ordinal);
@@ -326,7 +352,7 @@ public sealed class RuntimeFactsIngestionService : IRuntimeFactsIngestionService
private static string? Normalize(string? value) =>
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
private readonly record struct RuntimeFactKey(string SymbolId, string? CodeId, string? LoaderBase);
private readonly record struct RuntimeFactKey(string SymbolId, string? CodeId, string? LoaderBase, string? Purl, string? SymbolDigest, string? BuildId);
private sealed class RuntimeFactKeyComparer : IEqualityComparer<RuntimeFactKey>
{
@@ -335,7 +361,10 @@ public sealed class RuntimeFactsIngestionService : IRuntimeFactsIngestionService
public bool Equals(RuntimeFactKey x, RuntimeFactKey y) =>
string.Equals(x.SymbolId, y.SymbolId, StringComparison.Ordinal) &&
string.Equals(x.CodeId, y.CodeId, StringComparison.Ordinal) &&
string.Equals(x.LoaderBase, y.LoaderBase, StringComparison.Ordinal);
string.Equals(x.LoaderBase, y.LoaderBase, StringComparison.Ordinal) &&
string.Equals(x.Purl, y.Purl, StringComparison.Ordinal) &&
string.Equals(x.SymbolDigest, y.SymbolDigest, StringComparison.Ordinal) &&
string.Equals(x.BuildId, y.BuildId, StringComparison.Ordinal);
public int GetHashCode(RuntimeFactKey obj)
{
@@ -351,6 +380,21 @@ public sealed class RuntimeFactsIngestionService : IRuntimeFactsIngestionService
hash.Add(obj.LoaderBase, StringComparer.Ordinal);
}
if (obj.Purl is not null)
{
hash.Add(obj.Purl, StringComparer.Ordinal);
}
if (obj.SymbolDigest is not null)
{
hash.Add(obj.SymbolDigest, StringComparer.Ordinal);
}
if (obj.BuildId is not null)
{
hash.Add(obj.BuildId, StringComparer.Ordinal);
}
return hash.ToHashCode();
}
}