|
| 1 | +namespace Sigtran.NET.Core.Utilities; |
| 2 | + |
| 3 | +/// <summary> |
| 4 | +/// Describes one retained commercial evidence artifact digest. |
| 5 | +/// </summary> |
| 6 | +public sealed class SigtranCommercialEvidenceArtifactDigest |
| 7 | +{ |
| 8 | + /// <summary>Creates a retained commercial evidence artifact digest.</summary> |
| 9 | + /// <param name="stageId">The execution stage that produced the artifact.</param> |
| 10 | + /// <param name="kind">The checklist artifact kind.</param> |
| 11 | + /// <param name="sourcePath">The received artifact source path.</param> |
| 12 | + /// <param name="retainedPath">The retained dossier artifact path.</param> |
| 13 | + /// <param name="sha256">The SHA-256 digest.</param> |
| 14 | + public SigtranCommercialEvidenceArtifactDigest( |
| 15 | + string stageId, |
| 16 | + SigtranCommercialEvidenceChecklistKind kind, |
| 17 | + string sourcePath, |
| 18 | + string retainedPath, |
| 19 | + string sha256) |
| 20 | + { |
| 21 | + StageId = string.IsNullOrWhiteSpace(stageId) ? throw new ArgumentException("Stage id is required.", nameof(stageId)) : stageId; |
| 22 | + Kind = kind; |
| 23 | + SourcePath = string.IsNullOrWhiteSpace(sourcePath) ? throw new ArgumentException("Source path is required.", nameof(sourcePath)) : sourcePath; |
| 24 | + RetainedPath = string.IsNullOrWhiteSpace(retainedPath) ? throw new ArgumentException("Retained path is required.", nameof(retainedPath)) : retainedPath; |
| 25 | + Sha256 = string.IsNullOrWhiteSpace(sha256) ? throw new ArgumentException("SHA-256 digest is required.", nameof(sha256)) : sha256; |
| 26 | + } |
| 27 | + |
| 28 | + /// <summary>The execution stage that produced the artifact.</summary> |
| 29 | + public string StageId { get; } |
| 30 | + |
| 31 | + /// <summary>The checklist artifact kind.</summary> |
| 32 | + public SigtranCommercialEvidenceChecklistKind Kind { get; } |
| 33 | + |
| 34 | + /// <summary>The received artifact source path.</summary> |
| 35 | + public string SourcePath { get; } |
| 36 | + |
| 37 | + /// <summary>The retained dossier artifact path.</summary> |
| 38 | + public string RetainedPath { get; } |
| 39 | + |
| 40 | + /// <summary>The SHA-256 digest.</summary> |
| 41 | + public string Sha256 { get; } |
| 42 | + |
| 43 | + /// <summary>Whether the digest is a valid SHA-256 hex value.</summary> |
| 44 | + public bool HasValidSha256 => Sha256.Length == 64 |
| 45 | + && Sha256.All(Uri.IsHexDigit); |
| 46 | +} |
| 47 | + |
| 48 | +/// <summary> |
| 49 | +/// Describes the digest manifest for retained commercial evidence artifacts. |
| 50 | +/// </summary> |
| 51 | +public sealed class SigtranCommercialEvidenceArtifactDigestManifest |
| 52 | +{ |
| 53 | + /// <summary>Creates a retained commercial evidence artifact digest manifest.</summary> |
| 54 | + /// <param name="sourceManifest">The artifact source manifest.</param> |
| 55 | + /// <param name="digests">The artifact digest entries.</param> |
| 56 | + public SigtranCommercialEvidenceArtifactDigestManifest( |
| 57 | + SigtranCommercialEvidenceArtifactSourceManifest sourceManifest, |
| 58 | + IReadOnlyList<SigtranCommercialEvidenceArtifactDigest> digests) |
| 59 | + { |
| 60 | + SourceManifest = sourceManifest ?? throw new ArgumentNullException(nameof(sourceManifest)); |
| 61 | + ArgumentNullException.ThrowIfNull(digests); |
| 62 | + Digests = digests.Count == 0 ? throw new ArgumentException("At least one artifact digest is required.", nameof(digests)) : digests.ToArray(); |
| 63 | + } |
| 64 | + |
| 65 | + /// <summary>The artifact source manifest.</summary> |
| 66 | + public SigtranCommercialEvidenceArtifactSourceManifest SourceManifest { get; } |
| 67 | + |
| 68 | + /// <summary>The artifact digest entries.</summary> |
| 69 | + public IReadOnlyList<SigtranCommercialEvidenceArtifactDigest> Digests { get; } |
| 70 | + |
| 71 | + /// <summary>Whether every source has a matching digest entry.</summary> |
| 72 | + public bool CoversSources => SourceManifest.Sources.All(source => Digests.Any(digest => |
| 73 | + digest.StageId == source.StageId |
| 74 | + && digest.Kind == source.Kind |
| 75 | + && digest.SourcePath == source.SourcePath |
| 76 | + && digest.RetainedPath == source.RetainedPath)); |
| 77 | + |
| 78 | + /// <summary>Whether every digest entry contains a valid SHA-256 value.</summary> |
| 79 | + public bool HasValidDigests => Digests.All(static digest => digest.HasValidSha256); |
| 80 | + |
| 81 | + /// <summary>Whether retained digest paths are unique.</summary> |
| 82 | + public bool UsesUniqueRetainedPaths => Digests.Select(static digest => digest.RetainedPath).Distinct(StringComparer.OrdinalIgnoreCase).Count() == Digests.Count; |
| 83 | + |
| 84 | + /// <summary>Whether the digest manifest is ready for redaction review.</summary> |
| 85 | + public bool IsReady => SourceManifest.IsReady |
| 86 | + && CoversSources |
| 87 | + && HasValidDigests |
| 88 | + && UsesUniqueRetainedPaths; |
| 89 | + |
| 90 | + /// <summary>Formats a compact artifact digest manifest summary.</summary> |
| 91 | + /// <returns>The artifact digest manifest summary.</returns> |
| 92 | + public string Describe() |
| 93 | + { |
| 94 | + return $"commercialEvidenceArtifactDigestsReady={IsReady} digests={Digests.Count} intake={SourceManifest.Target.IntakeId}"; |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +/// <summary> |
| 99 | +/// Provides commercial evidence artifact digest manifest helpers. |
| 100 | +/// </summary> |
| 101 | +public static class SigtranCommercialEvidenceArtifactDigests |
| 102 | +{ |
| 103 | + /// <summary>Creates a digest manifest using the same digest value for every source.</summary> |
| 104 | + /// <param name="sourceManifest">The artifact source manifest.</param> |
| 105 | + /// <param name="sha256">The SHA-256 digest assigned to every artifact.</param> |
| 106 | + /// <returns>The digest manifest.</returns> |
| 107 | + public static SigtranCommercialEvidenceArtifactDigestManifest CreateCovered( |
| 108 | + SigtranCommercialEvidenceArtifactSourceManifest sourceManifest, |
| 109 | + string sha256) |
| 110 | + { |
| 111 | + ArgumentNullException.ThrowIfNull(sourceManifest); |
| 112 | + if (string.IsNullOrWhiteSpace(sha256)) |
| 113 | + { |
| 114 | + throw new ArgumentException("SHA-256 digest is required.", nameof(sha256)); |
| 115 | + } |
| 116 | + |
| 117 | + return new( |
| 118 | + sourceManifest, |
| 119 | + sourceManifest.Sources |
| 120 | + .Select(source => new SigtranCommercialEvidenceArtifactDigest( |
| 121 | + source.StageId, |
| 122 | + source.Kind, |
| 123 | + source.SourcePath, |
| 124 | + source.RetainedPath, |
| 125 | + sha256)) |
| 126 | + .ToArray()); |
| 127 | + } |
| 128 | +} |
0 commit comments