Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Added AdvisoryCanonicalizer for canonicalizing advisory identifiers. - Created EnsureAdvisoryCanonicalKeyBackfillMigration to populate advisory_key and links in advisory_raw documents. - Introduced FileSurfaceManifestStore for managing surface manifests with file system backing. - Developed ISurfaceManifestReader and ISurfaceManifestWriter interfaces for reading and writing manifests. - Implemented SurfaceManifestPathBuilder for constructing paths and URIs for surface manifests. - Added tests for FileSurfaceManifestStore to ensure correct functionality and deterministic behavior. - Updated documentation for new features and migration steps.
160 lines
6.3 KiB
C#
160 lines
6.3 KiB
C#
using System.Collections.Immutable;
|
|
using System.Text.Json;
|
|
using StellaOps.Concelier.RawModels;
|
|
using StellaOps.Concelier.WebService.Contracts;
|
|
|
|
namespace StellaOps.Concelier.WebService.Extensions;
|
|
|
|
internal static class AdvisoryRawRequestMapper
|
|
{
|
|
internal static AdvisoryRawDocument Map(AdvisoryIngestRequest request, string tenant, TimeProvider timeProvider)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(request);
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(tenant);
|
|
ArgumentNullException.ThrowIfNull(timeProvider);
|
|
|
|
var sourceRequest = request.Source ?? throw new ArgumentException("source section is required.", nameof(request));
|
|
var upstreamRequest = request.Upstream ?? throw new ArgumentException("upstream section is required.", nameof(request));
|
|
var contentRequest = request.Content ?? throw new ArgumentException("content section is required.", nameof(request));
|
|
var identifiersRequest = request.Identifiers ?? throw new ArgumentException("identifiers section is required.", nameof(request));
|
|
|
|
var source = new RawSourceMetadata(
|
|
sourceRequest.Vendor,
|
|
sourceRequest.Connector,
|
|
sourceRequest.Version,
|
|
string.IsNullOrWhiteSpace(sourceRequest.Stream) ? null : sourceRequest.Stream);
|
|
|
|
var signatureRequest = upstreamRequest.Signature ?? new AdvisorySignatureRequest(false, null, null, null, null, null);
|
|
var signature = new RawSignatureMetadata(
|
|
signatureRequest.Present,
|
|
string.IsNullOrWhiteSpace(signatureRequest.Format) ? null : signatureRequest.Format,
|
|
string.IsNullOrWhiteSpace(signatureRequest.KeyId) ? null : signatureRequest.KeyId,
|
|
string.IsNullOrWhiteSpace(signatureRequest.Signature) ? null : signatureRequest.Signature,
|
|
string.IsNullOrWhiteSpace(signatureRequest.Certificate) ? null : signatureRequest.Certificate,
|
|
string.IsNullOrWhiteSpace(signatureRequest.Digest) ? null : signatureRequest.Digest);
|
|
|
|
var retrievedAt = upstreamRequest.RetrievedAt ?? timeProvider.GetUtcNow();
|
|
|
|
var upstream = new RawUpstreamMetadata(
|
|
upstreamRequest.UpstreamId,
|
|
string.IsNullOrWhiteSpace(upstreamRequest.DocumentVersion) ? null : upstreamRequest.DocumentVersion,
|
|
retrievedAt,
|
|
upstreamRequest.ContentHash,
|
|
signature,
|
|
NormalizeDictionary(upstreamRequest.Provenance));
|
|
|
|
var rawContent = NormalizeRawContent(contentRequest.Raw);
|
|
var content = new RawContent(
|
|
contentRequest.Format,
|
|
string.IsNullOrWhiteSpace(contentRequest.SpecVersion) ? null : contentRequest.SpecVersion,
|
|
rawContent,
|
|
string.IsNullOrWhiteSpace(contentRequest.Encoding) ? null : contentRequest.Encoding);
|
|
|
|
var aliases = NormalizeStrings(identifiersRequest.Aliases);
|
|
if (aliases.IsDefault)
|
|
{
|
|
aliases = ImmutableArray<string>.Empty;
|
|
}
|
|
|
|
var identifiers = new RawIdentifiers(
|
|
aliases,
|
|
identifiersRequest.Primary);
|
|
|
|
var linksetRequest = request.Linkset;
|
|
var linkset = new RawLinkset
|
|
{
|
|
Aliases = NormalizeStrings(linksetRequest?.Aliases),
|
|
PackageUrls = NormalizeStrings(linksetRequest?.PackageUrls),
|
|
Cpes = NormalizeStrings(linksetRequest?.Cpes),
|
|
References = NormalizeReferences(linksetRequest?.References),
|
|
ReconciledFrom = NormalizeStrings(linksetRequest?.ReconciledFrom),
|
|
Notes = NormalizeDictionary(linksetRequest?.Notes)
|
|
};
|
|
|
|
return new AdvisoryRawDocument(
|
|
tenant.Trim().ToLowerInvariant(),
|
|
source,
|
|
upstream,
|
|
content,
|
|
identifiers,
|
|
linkset,
|
|
AdvisoryKey: string.Empty,
|
|
Links: ImmutableArray<RawLink>.Empty);
|
|
}
|
|
|
|
internal static ImmutableArray<string> NormalizeStrings(IEnumerable<string>? values)
|
|
{
|
|
if (values is null)
|
|
{
|
|
return ImmutableArray<string>.Empty;
|
|
}
|
|
|
|
var builder = ImmutableArray.CreateBuilder<string>();
|
|
foreach (var value in values)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
builder.Add(value.Trim());
|
|
}
|
|
|
|
return builder.Count == 0 ? ImmutableArray<string>.Empty : builder.ToImmutable();
|
|
}
|
|
|
|
internal static ImmutableDictionary<string, string> NormalizeDictionary(IDictionary<string, string>? values)
|
|
{
|
|
if (values is null || values.Count == 0)
|
|
{
|
|
return ImmutableDictionary<string, string>.Empty;
|
|
}
|
|
|
|
var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.Ordinal);
|
|
foreach (var kv in values)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(kv.Key))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
builder[kv.Key.Trim()] = kv.Value?.Trim() ?? string.Empty;
|
|
}
|
|
|
|
return builder.ToImmutable();
|
|
}
|
|
|
|
private static ImmutableArray<RawReference> NormalizeReferences(IEnumerable<AdvisoryLinksetReferenceRequest>? references)
|
|
{
|
|
if (references is null)
|
|
{
|
|
return ImmutableArray<RawReference>.Empty;
|
|
}
|
|
|
|
var builder = ImmutableArray.CreateBuilder<RawReference>();
|
|
foreach (var reference in references)
|
|
{
|
|
if (reference is null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(reference.Type) || string.IsNullOrWhiteSpace(reference.Url))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
builder.Add(new RawReference(reference.Type.Trim(), reference.Url.Trim(), string.IsNullOrWhiteSpace(reference.Source) ? null : reference.Source.Trim()));
|
|
}
|
|
|
|
return builder.Count == 0 ? ImmutableArray<RawReference>.Empty : builder.ToImmutable();
|
|
}
|
|
|
|
private static JsonElement NormalizeRawContent(JsonElement element)
|
|
{
|
|
var json = element.ValueKind == JsonValueKind.Undefined ? "{}" : element.GetRawText();
|
|
using var document = JsonDocument.Parse(string.IsNullOrWhiteSpace(json) ? "{}" : json);
|
|
return document.RootElement.Clone();
|
|
}
|
|
}
|