Restructure solution layout by module

This commit is contained in:
master
2025-10-28 15:10:40 +02:00
parent 95daa159c4
commit d870da18ce
4103 changed files with 192899 additions and 187024 deletions

View File

@@ -0,0 +1,198 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using StellaOps.Scanner.WebService.Contracts;
namespace StellaOps.Scanner.WebService.Serialization;
internal static class OrchestratorEventSerializer
{
private static readonly JsonSerializerOptions CompactOptions = CreateOptions(writeIndented: false);
private static readonly JsonSerializerOptions PrettyOptions = CreateOptions(writeIndented: true);
public static string Serialize(OrchestratorEvent @event)
=> JsonSerializer.Serialize(@event, CompactOptions);
public static string SerializeIndented(OrchestratorEvent @event)
=> JsonSerializer.Serialize(@event, PrettyOptions);
private static JsonSerializerOptions CreateOptions(bool writeIndented)
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
WriteIndented = writeIndented,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var baselineResolver = options.TypeInfoResolver ?? new DefaultJsonTypeInfoResolver();
options.TypeInfoResolver = new DeterministicTypeInfoResolver(baselineResolver);
return options;
}
private sealed class DeterministicTypeInfoResolver : IJsonTypeInfoResolver
{
private static readonly ImmutableDictionary<Type, string[]> PropertyOrder = new Dictionary<Type, string[]>
{
[typeof(OrchestratorEvent)] = new[]
{
"eventId",
"kind",
"version",
"tenant",
"occurredAt",
"recordedAt",
"source",
"idempotencyKey",
"correlationId",
"traceId",
"spanId",
"scope",
"payload",
"attributes"
},
[typeof(OrchestratorEventScope)] = new[]
{
"namespace",
"repo",
"digest",
"component",
"image"
},
[typeof(ReportReadyEventPayload)] = new[]
{
"reportId",
"scanId",
"imageDigest",
"generatedAt",
"verdict",
"summary",
"delta",
"quietedFindingCount",
"policy",
"links",
"dsse",
"report"
},
[typeof(ScanCompletedEventPayload)] = new[]
{
"reportId",
"scanId",
"imageDigest",
"verdict",
"summary",
"delta",
"policy",
"findings",
"links",
"dsse",
"report"
},
[typeof(ReportDeltaPayload)] = new[]
{
"newCritical",
"newHigh",
"kev"
},
[typeof(ReportLinksPayload)] = new[]
{
"ui",
"report",
"policy",
"attestation"
},
[typeof(FindingSummaryPayload)] = new[]
{
"id",
"severity",
"cve",
"purl",
"reachability"
},
[typeof(ReportPolicyDto)] = new[]
{
"revisionId",
"digest"
},
[typeof(ReportSummaryDto)] = new[]
{
"total",
"blocked",
"warned",
"ignored",
"quieted"
},
[typeof(ReportDocumentDto)] = new[]
{
"reportId",
"imageDigest",
"generatedAt",
"verdict",
"policy",
"summary",
"verdicts",
"issues"
},
[typeof(DsseEnvelopeDto)] = new[]
{
"payloadType",
"payload",
"signatures"
},
[typeof(DsseSignatureDto)] = new[]
{
"keyId",
"algorithm",
"signature"
}
}.ToImmutableDictionary();
private readonly IJsonTypeInfoResolver _inner;
public DeterministicTypeInfoResolver(IJsonTypeInfoResolver inner)
{
_inner = inner ?? throw new ArgumentNullException(nameof(inner));
}
public JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
{
var info = _inner.GetTypeInfo(type, options)
?? throw new InvalidOperationException($"Unable to resolve JsonTypeInfo for '{type}'.");
if (info.Kind is JsonTypeInfoKind.Object && info.Properties is { Count: > 1 })
{
var ordered = info.Properties
.OrderBy(property => GetOrder(type, property.Name))
.ThenBy(property => property.Name, StringComparer.Ordinal)
.ToArray();
info.Properties.Clear();
foreach (var property in ordered)
{
info.Properties.Add(property);
}
}
return info;
}
private static int GetOrder(Type type, string propertyName)
{
if (PropertyOrder.TryGetValue(type, out var order) && Array.IndexOf(order, propertyName) is { } index and >= 0)
{
return index;
}
if (type.BaseType is not null)
{
return GetOrder(type.BaseType, propertyName);
}
return int.MaxValue;
}
}
}