commit
This commit is contained in:
		@@ -3,11 +3,13 @@ using System.Collections.Generic;
 | 
			
		||||
using System.Collections.Immutable;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using MongoDB.Bson;
 | 
			
		||||
using MongoDB.Bson.Serialization.Attributes;
 | 
			
		||||
using StellaOps.Vexer.Core;
 | 
			
		||||
 | 
			
		||||
namespace StellaOps.Vexer.Storage.Mongo;
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexRawDocumentRecord
 | 
			
		||||
{
 | 
			
		||||
    [BsonId]
 | 
			
		||||
@@ -26,9 +28,13 @@ internal sealed class VexRawDocumentRecord
 | 
			
		||||
 | 
			
		||||
    public byte[] Content { get; set; } = Array.Empty<byte>();
 | 
			
		||||
 | 
			
		||||
    [BsonRepresentation(BsonType.ObjectId)]
 | 
			
		||||
    public string? GridFsObjectId { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public Dictionary<string, string> Metadata { get; set; } = new(StringComparer.Ordinal);
 | 
			
		||||
 | 
			
		||||
    public static VexRawDocumentRecord FromDomain(VexRawDocument document)
 | 
			
		||||
    public static VexRawDocumentRecord FromDomain(VexRawDocument document, bool includeContent = true)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            Id = document.Digest,
 | 
			
		||||
@@ -37,22 +43,26 @@ internal sealed class VexRawDocumentRecord
 | 
			
		||||
            SourceUri = document.SourceUri.ToString(),
 | 
			
		||||
            RetrievedAt = document.RetrievedAt.UtcDateTime,
 | 
			
		||||
            Digest = document.Digest,
 | 
			
		||||
            Content = document.Content.ToArray(),
 | 
			
		||||
            Content = includeContent ? document.Content.ToArray() : Array.Empty<byte>(),
 | 
			
		||||
            Metadata = document.Metadata.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.Ordinal),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public VexRawDocument ToDomain()
 | 
			
		||||
        => ToDomain(new ReadOnlyMemory<byte>(Content ?? Array.Empty<byte>()));
 | 
			
		||||
 | 
			
		||||
    public VexRawDocument ToDomain(ReadOnlyMemory<byte> content)
 | 
			
		||||
        => new(
 | 
			
		||||
            ProviderId,
 | 
			
		||||
            Enum.Parse<VexDocumentFormat>(Format, ignoreCase: true),
 | 
			
		||||
            new Uri(SourceUri),
 | 
			
		||||
            RetrievedAt,
 | 
			
		||||
            Digest,
 | 
			
		||||
            new ReadOnlyMemory<byte>(Content ?? Array.Empty<byte>()),
 | 
			
		||||
            content,
 | 
			
		||||
            (Metadata ?? new Dictionary<string, string>(StringComparer.Ordinal))
 | 
			
		||||
                .ToImmutableDictionary(StringComparer.Ordinal));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexExportManifestRecord
 | 
			
		||||
{
 | 
			
		||||
    [BsonId]
 | 
			
		||||
@@ -162,5 +172,401 @@ internal sealed class VexExportManifestRecord
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static string CreateId(VexQuerySignature signature, VexExportFormat format)
 | 
			
		||||
        => string.Create(CultureInfo.InvariantCulture, $"{signature.Value}|{format.ToString().ToLowerInvariant()}");
 | 
			
		||||
        => string.Format(CultureInfo.InvariantCulture, "{0}|{1}", signature.Value, format.ToString().ToLowerInvariant());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexProviderRecord
 | 
			
		||||
{
 | 
			
		||||
    [BsonId]
 | 
			
		||||
    public string Id { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string DisplayName { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string Kind { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public List<string> BaseUris { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    public VexProviderDiscoveryDocument? Discovery { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public VexProviderTrustDocument? Trust { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public bool Enabled { get; set; }
 | 
			
		||||
        = true;
 | 
			
		||||
 | 
			
		||||
    public static VexProviderRecord FromDomain(VexProvider provider)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            Id = provider.Id,
 | 
			
		||||
            DisplayName = provider.DisplayName,
 | 
			
		||||
            Kind = provider.Kind.ToString().ToLowerInvariant(),
 | 
			
		||||
            BaseUris = provider.BaseUris.Select(uri => uri.ToString()).ToList(),
 | 
			
		||||
            Discovery = VexProviderDiscoveryDocument.FromDomain(provider.Discovery),
 | 
			
		||||
            Trust = VexProviderTrustDocument.FromDomain(provider.Trust),
 | 
			
		||||
            Enabled = provider.Enabled,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public VexProvider ToDomain()
 | 
			
		||||
    {
 | 
			
		||||
        var uris = BaseUris?.Select(uri => new Uri(uri)) ?? Enumerable.Empty<Uri>();
 | 
			
		||||
        return new VexProvider(
 | 
			
		||||
            Id,
 | 
			
		||||
            DisplayName,
 | 
			
		||||
            Enum.Parse<VexProviderKind>(Kind, ignoreCase: true),
 | 
			
		||||
            uris,
 | 
			
		||||
            Discovery?.ToDomain(),
 | 
			
		||||
            Trust?.ToDomain(),
 | 
			
		||||
            Enabled);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexProviderDiscoveryDocument
 | 
			
		||||
{
 | 
			
		||||
    public string? WellKnownMetadata { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? RolIeService { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public static VexProviderDiscoveryDocument? FromDomain(VexProviderDiscovery? discovery)
 | 
			
		||||
        => discovery is null
 | 
			
		||||
            ? null
 | 
			
		||||
            : new VexProviderDiscoveryDocument
 | 
			
		||||
            {
 | 
			
		||||
                WellKnownMetadata = discovery.WellKnownMetadata?.ToString(),
 | 
			
		||||
                RolIeService = discovery.RolIeService?.ToString(),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
    public VexProviderDiscovery ToDomain()
 | 
			
		||||
        => new(
 | 
			
		||||
            WellKnownMetadata is null ? null : new Uri(WellKnownMetadata),
 | 
			
		||||
            RolIeService is null ? null : new Uri(RolIeService));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexProviderTrustDocument
 | 
			
		||||
{
 | 
			
		||||
    public double Weight { get; set; }
 | 
			
		||||
        = 1.0;
 | 
			
		||||
 | 
			
		||||
    public VexCosignTrustDocument? Cosign { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public List<string> PgpFingerprints { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    public static VexProviderTrustDocument? FromDomain(VexProviderTrust? trust)
 | 
			
		||||
        => trust is null
 | 
			
		||||
            ? null
 | 
			
		||||
            : new VexProviderTrustDocument
 | 
			
		||||
            {
 | 
			
		||||
                Weight = trust.Weight,
 | 
			
		||||
                Cosign = trust.Cosign is null ? null : VexCosignTrustDocument.FromDomain(trust.Cosign),
 | 
			
		||||
                PgpFingerprints = trust.PgpFingerprints.ToList(),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
    public VexProviderTrust ToDomain()
 | 
			
		||||
        => new(
 | 
			
		||||
            Weight,
 | 
			
		||||
            Cosign?.ToDomain(),
 | 
			
		||||
            PgpFingerprints);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexCosignTrustDocument
 | 
			
		||||
{
 | 
			
		||||
    public string Issuer { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string IdentityPattern { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public static VexCosignTrustDocument FromDomain(VexCosignTrust trust)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            Issuer = trust.Issuer,
 | 
			
		||||
            IdentityPattern = trust.IdentityPattern,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public VexCosignTrust ToDomain()
 | 
			
		||||
        => new(Issuer, IdentityPattern);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexConsensusRecord
 | 
			
		||||
{
 | 
			
		||||
    [BsonId]
 | 
			
		||||
    public string Id { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string VulnerabilityId { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public VexProductDocument Product { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string Status { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public DateTime CalculatedAt { get; set; }
 | 
			
		||||
        = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
 | 
			
		||||
 | 
			
		||||
    public List<VexConsensusSourceDocument> Sources { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    public List<VexConsensusConflictDocument> Conflicts { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    public string? PolicyVersion { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? PolicyRevisionId { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? PolicyDigest { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? Summary { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public static string CreateId(string vulnerabilityId, string productKey)
 | 
			
		||||
        => string.Format(CultureInfo.InvariantCulture, "{0}|{1}", vulnerabilityId.Trim(), productKey.Trim());
 | 
			
		||||
 | 
			
		||||
    public static VexConsensusRecord FromDomain(VexConsensus consensus)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            Id = CreateId(consensus.VulnerabilityId, consensus.Product.Key),
 | 
			
		||||
            VulnerabilityId = consensus.VulnerabilityId,
 | 
			
		||||
            Product = VexProductDocument.FromDomain(consensus.Product),
 | 
			
		||||
            Status = consensus.Status.ToString().ToLowerInvariant(),
 | 
			
		||||
            CalculatedAt = consensus.CalculatedAt.UtcDateTime,
 | 
			
		||||
            Sources = consensus.Sources.Select(VexConsensusSourceDocument.FromDomain).ToList(),
 | 
			
		||||
            Conflicts = consensus.Conflicts.Select(VexConsensusConflictDocument.FromDomain).ToList(),
 | 
			
		||||
            PolicyVersion = consensus.PolicyVersion,
 | 
			
		||||
            PolicyRevisionId = consensus.PolicyRevisionId,
 | 
			
		||||
            PolicyDigest = consensus.PolicyDigest,
 | 
			
		||||
            Summary = consensus.Summary,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public VexConsensus ToDomain()
 | 
			
		||||
        => new(
 | 
			
		||||
            VulnerabilityId,
 | 
			
		||||
            Product.ToDomain(),
 | 
			
		||||
            Enum.Parse<VexConsensusStatus>(Status, ignoreCase: true),
 | 
			
		||||
            new DateTimeOffset(CalculatedAt, TimeSpan.Zero),
 | 
			
		||||
            Sources.Select(static source => source.ToDomain()),
 | 
			
		||||
            Conflicts.Select(static conflict => conflict.ToDomain()),
 | 
			
		||||
            PolicyVersion,
 | 
			
		||||
            Summary,
 | 
			
		||||
            PolicyRevisionId,
 | 
			
		||||
            PolicyDigest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexProductDocument
 | 
			
		||||
{
 | 
			
		||||
    public string Key { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string? Name { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? Version { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? Purl { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? Cpe { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public List<string> ComponentIdentifiers { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    public static VexProductDocument FromDomain(VexProduct product)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            Key = product.Key,
 | 
			
		||||
            Name = product.Name,
 | 
			
		||||
            Version = product.Version,
 | 
			
		||||
            Purl = product.Purl,
 | 
			
		||||
            Cpe = product.Cpe,
 | 
			
		||||
            ComponentIdentifiers = product.ComponentIdentifiers.ToList(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public VexProduct ToDomain()
 | 
			
		||||
        => new(
 | 
			
		||||
            Key,
 | 
			
		||||
            Name,
 | 
			
		||||
            Version,
 | 
			
		||||
            Purl,
 | 
			
		||||
            Cpe,
 | 
			
		||||
            ComponentIdentifiers);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexConsensusSourceDocument
 | 
			
		||||
{
 | 
			
		||||
    public string ProviderId { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string Status { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string DocumentDigest { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public double Weight { get; set; }
 | 
			
		||||
        = 0;
 | 
			
		||||
 | 
			
		||||
    public string? Justification { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? Detail { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public VexConfidenceDocument? Confidence { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public static VexConsensusSourceDocument FromDomain(VexConsensusSource source)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            ProviderId = source.ProviderId,
 | 
			
		||||
            Status = source.Status.ToString().ToLowerInvariant(),
 | 
			
		||||
            DocumentDigest = source.DocumentDigest,
 | 
			
		||||
            Weight = source.Weight,
 | 
			
		||||
            Justification = source.Justification?.ToString().ToLowerInvariant(),
 | 
			
		||||
            Detail = source.Detail,
 | 
			
		||||
            Confidence = source.Confidence is null ? null : VexConfidenceDocument.FromDomain(source.Confidence),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public VexConsensusSource ToDomain()
 | 
			
		||||
        => new(
 | 
			
		||||
            ProviderId,
 | 
			
		||||
            Enum.Parse<VexClaimStatus>(Status, ignoreCase: true),
 | 
			
		||||
            DocumentDigest,
 | 
			
		||||
            Weight,
 | 
			
		||||
            string.IsNullOrWhiteSpace(Justification) ? null : Enum.Parse<VexJustification>(Justification, ignoreCase: true),
 | 
			
		||||
            Detail,
 | 
			
		||||
            Confidence?.ToDomain());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexConsensusConflictDocument
 | 
			
		||||
{
 | 
			
		||||
    public string ProviderId { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string Status { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string DocumentDigest { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string? Justification { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? Detail { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? Reason { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public static VexConsensusConflictDocument FromDomain(VexConsensusConflict conflict)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            ProviderId = conflict.ProviderId,
 | 
			
		||||
            Status = conflict.Status.ToString().ToLowerInvariant(),
 | 
			
		||||
            DocumentDigest = conflict.DocumentDigest,
 | 
			
		||||
            Justification = conflict.Justification?.ToString().ToLowerInvariant(),
 | 
			
		||||
            Detail = conflict.Detail,
 | 
			
		||||
            Reason = conflict.Reason,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public VexConsensusConflict ToDomain()
 | 
			
		||||
        => new(
 | 
			
		||||
            ProviderId,
 | 
			
		||||
            Enum.Parse<VexClaimStatus>(Status, ignoreCase: true),
 | 
			
		||||
            DocumentDigest,
 | 
			
		||||
            string.IsNullOrWhiteSpace(Justification) ? null : Enum.Parse<VexJustification>(Justification, ignoreCase: true),
 | 
			
		||||
            Detail,
 | 
			
		||||
            Reason);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexConfidenceDocument
 | 
			
		||||
{
 | 
			
		||||
    public string Level { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public double? Score { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public string? Method { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public static VexConfidenceDocument FromDomain(VexConfidence confidence)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            Level = confidence.Level,
 | 
			
		||||
            Score = confidence.Score,
 | 
			
		||||
            Method = confidence.Method,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public VexConfidence ToDomain()
 | 
			
		||||
        => new(Level, Score, Method);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[BsonIgnoreExtraElements]
 | 
			
		||||
internal sealed class VexCacheEntryRecord
 | 
			
		||||
{
 | 
			
		||||
    [BsonId]
 | 
			
		||||
    public string Id { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string QuerySignature { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string Format { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string ArtifactAlgorithm { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public string ArtifactDigest { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    public DateTime CreatedAt { get; set; }
 | 
			
		||||
        = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
 | 
			
		||||
 | 
			
		||||
    public long SizeBytes { get; set; }
 | 
			
		||||
        = 0;
 | 
			
		||||
 | 
			
		||||
    public string? ManifestId { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    [BsonRepresentation(BsonType.ObjectId)]
 | 
			
		||||
    public string? GridFsObjectId { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public DateTime? ExpiresAt { get; set; }
 | 
			
		||||
        = null;
 | 
			
		||||
 | 
			
		||||
    public static string CreateId(VexQuerySignature signature, VexExportFormat format)
 | 
			
		||||
        => string.Format(CultureInfo.InvariantCulture, "{0}|{1}", signature.Value, format.ToString().ToLowerInvariant());
 | 
			
		||||
 | 
			
		||||
    public static VexCacheEntryRecord FromDomain(VexCacheEntry entry)
 | 
			
		||||
        => new()
 | 
			
		||||
        {
 | 
			
		||||
            Id = CreateId(entry.QuerySignature, entry.Format),
 | 
			
		||||
            QuerySignature = entry.QuerySignature.Value,
 | 
			
		||||
            Format = entry.Format.ToString().ToLowerInvariant(),
 | 
			
		||||
            ArtifactAlgorithm = entry.Artifact.Algorithm,
 | 
			
		||||
            ArtifactDigest = entry.Artifact.Digest,
 | 
			
		||||
            CreatedAt = entry.CreatedAt.UtcDateTime,
 | 
			
		||||
            SizeBytes = entry.SizeBytes,
 | 
			
		||||
            ManifestId = entry.ManifestId,
 | 
			
		||||
            GridFsObjectId = entry.GridFsObjectId,
 | 
			
		||||
            ExpiresAt = entry.ExpiresAt?.UtcDateTime,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    public VexCacheEntry ToDomain()
 | 
			
		||||
    {
 | 
			
		||||
        var signature = new VexQuerySignature(QuerySignature);
 | 
			
		||||
        var artifact = new VexContentAddress(ArtifactAlgorithm, ArtifactDigest);
 | 
			
		||||
        var createdAt = new DateTimeOffset(DateTime.SpecifyKind(CreatedAt, DateTimeKind.Utc));
 | 
			
		||||
        var expires = ExpiresAt.HasValue
 | 
			
		||||
            ? new DateTimeOffset(DateTime.SpecifyKind(ExpiresAt.Value, DateTimeKind.Utc))
 | 
			
		||||
            : (DateTimeOffset?)null;
 | 
			
		||||
 | 
			
		||||
        return new VexCacheEntry(
 | 
			
		||||
            signature,
 | 
			
		||||
            Enum.Parse<VexExportFormat>(Format, ignoreCase: true),
 | 
			
		||||
            artifact,
 | 
			
		||||
            createdAt,
 | 
			
		||||
            SizeBytes,
 | 
			
		||||
            ManifestId,
 | 
			
		||||
            GridFsObjectId,
 | 
			
		||||
            expires);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user