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.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.Empty); } internal static ImmutableArray NormalizeStrings(IEnumerable? values) { if (values is null) { return ImmutableArray.Empty; } var builder = ImmutableArray.CreateBuilder(); foreach (var value in values) { if (string.IsNullOrWhiteSpace(value)) { continue; } builder.Add(value.Trim()); } return builder.Count == 0 ? ImmutableArray.Empty : builder.ToImmutable(); } internal static ImmutableDictionary NormalizeDictionary(IDictionary? values) { if (values is null || values.Count == 0) { return ImmutableDictionary.Empty; } var builder = ImmutableDictionary.CreateBuilder(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 NormalizeReferences(IEnumerable? references) { if (references is null) { return ImmutableArray.Empty; } var builder = ImmutableArray.CreateBuilder(); 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.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(); } }