< Summary

Information
Class: AsiBackbone.Core.Audit.AuditLedgerRecord
Assembly: AsiBackbone.Core
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Audit/AuditLedgerRecord.cs
Line coverage
100%
Covered lines: 187
Uncovered lines: 0
Coverable lines: 187
Total lines: 360
Line coverage: 100%
Branch coverage
89%
Covered branches: 41
Total branches: 46
Branch coverage: 89.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%44100%
get_HasReasonCodes()100%11100%
get_HasMetadata()100%11100%
FromDecisionReceipt(...)100%44100%
NormalizeIdentifier(...)100%22100%
NormalizeOptional(...)100%22100%
NormalizeNonNegative(...)50%22100%
NormalizeNonNegative(...)50%22100%
NormalizeRiskScore(...)62.5%88100%
NormalizeReasonCodes(...)100%66100%
NormalizeMetadata(...)100%1616100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Audit/AuditLedgerRecord.cs

#LineLine coverage
 1using System.Collections.ObjectModel;
 2using AsiBackbone.Core.Actors;
 3using AsiBackbone.Core.Serialization;
 4using SigningMetadataValue = AsiBackbone.Core.Signing.SigningMetadata;
 5
 6namespace AsiBackbone.Core.Audit;
 7
 8/// <summary>
 9/// Represents a persistence-ready audit ledger record captured from AsiBackbone decision receipt.
 10/// </summary>
 11public sealed class AuditLedgerRecord : IDecisionReceipt
 12{
 313    private static readonly ReadOnlyCollection<string> EmptyReasonCodes =
 314        Array.AsReadOnly(Array.Empty<string>());
 15
 316    private static readonly IReadOnlyDictionary<string, string> EmptyMetadata =
 317        new ReadOnlyDictionary<string, string>(
 318            new Dictionary<string, string>(StringComparer.Ordinal));
 19
 8220    private AuditLedgerRecord(
 8221        string recordId,
 8222        string? schemaVersion,
 8223        string eventId,
 8224        string? decisionReceiptId,
 8225        DateTimeOffset occurredUtc,
 8226        DateTimeOffset recordedUtc,
 8227        string actorId,
 8228        GovernanceActorType actorType,
 8229        string? actorDisplayName,
 8230        string operationName,
 8231        string outcome,
 8232        IReadOnlyList<string> reasonCodes,
 8233        string? correlationId,
 8234        string? traceId,
 8235        string? spanId,
 8236        string? parentSpanId,
 8237        long? decisionLatencyMs,
 8238        string? constraintSetHash,
 8239        int? constraintCount,
 8240        double? riskScore,
 8241        string? policyScope,
 8242        string? tenantHash,
 8243        string? organizationHash,
 8244        string? emitterStatus,
 8245        string? emitterProvider,
 8246        long? outboxSequence,
 8247        string? gatewayExecutionId,
 8248        string? decisionStage,
 8249        string? policyVersion,
 8250        string? policyHash,
 8251        string? handshakeId,
 8252        string? acknowledgmentId,
 8253        string? capabilityTokenId,
 8254        string? previousRecordHash,
 8255        string? recordHash,
 8256        string? signatureKeyId,
 8257        string? signatureAlgorithm,
 8258        string? signatureValue,
 8259        string? signingHash,
 8260        string? signatureKeyVersion,
 8261        string? signatureProvider,
 8262        DateTimeOffset? signedUtc,
 8263        IReadOnlyDictionary<string, string> metadata)
 64    {
 8265        ArgumentException.ThrowIfNullOrWhiteSpace(recordId);
 8266        ArgumentException.ThrowIfNullOrWhiteSpace(eventId);
 8167        ArgumentException.ThrowIfNullOrWhiteSpace(actorId);
 8068        ArgumentException.ThrowIfNullOrWhiteSpace(operationName);
 7969        ArgumentException.ThrowIfNullOrWhiteSpace(outcome);
 70
 7871        RecordId = recordId.Trim();
 7872        SchemaVersion = GovernanceSchemaVersions.Normalize(schemaVersion);
 7873        EventId = eventId.Trim();
 7874        DecisionReceiptId = NormalizeOptional(decisionReceiptId) ?? EventId;
 7875        OccurredUtc = occurredUtc.ToUniversalTime();
 7876        RecordedUtc = recordedUtc.ToUniversalTime();
 7877        ActorId = actorId.Trim();
 7878        ActorType = actorType;
 7879        ActorDisplayName = NormalizeOptional(actorDisplayName);
 7880        OperationName = operationName.Trim();
 7881        Outcome = outcome.Trim();
 7882        ReasonCodes = reasonCodes;
 7883        CorrelationId = NormalizeOptional(correlationId);
 7884        TraceId = NormalizeOptional(traceId);
 7885        SpanId = NormalizeOptional(spanId);
 7886        ParentSpanId = NormalizeOptional(parentSpanId);
 7887        DecisionLatencyMs = NormalizeNonNegative(decisionLatencyMs, nameof(decisionLatencyMs));
 7888        ConstraintSetHash = NormalizeOptional(constraintSetHash);
 7889        ConstraintCount = NormalizeNonNegative(constraintCount, nameof(constraintCount));
 7890        RiskScore = NormalizeRiskScore(riskScore);
 7891        PolicyScope = NormalizeOptional(policyScope);
 7892        TenantHash = NormalizeOptional(tenantHash);
 7893        OrganizationHash = NormalizeOptional(organizationHash);
 7894        EmitterStatus = NormalizeOptional(emitterStatus);
 7895        EmitterProvider = NormalizeOptional(emitterProvider);
 7896        OutboxSequence = NormalizeNonNegative(outboxSequence, nameof(outboxSequence));
 7897        GatewayExecutionId = NormalizeOptional(gatewayExecutionId);
 7898        DecisionStage = NormalizeOptional(decisionStage);
 7899        PolicyVersion = NormalizeOptional(policyVersion);
 78100        PolicyHash = NormalizeOptional(policyHash);
 78101        HandshakeId = NormalizeOptional(handshakeId);
 78102        AcknowledgmentId = NormalizeOptional(acknowledgmentId);
 78103        CapabilityTokenId = NormalizeOptional(capabilityTokenId);
 78104        PreviousRecordHash = NormalizeOptional(previousRecordHash);
 78105        RecordHash = NormalizeOptional(recordHash);
 78106        SignatureKeyId = NormalizeOptional(signatureKeyId);
 78107        SignatureAlgorithm = NormalizeOptional(signatureAlgorithm);
 78108        SignatureValue = NormalizeOptional(signatureValue);
 78109        SigningHash = NormalizeOptional(signingHash);
 78110        SignatureKeyVersion = NormalizeOptional(signatureKeyVersion);
 78111        SignatureProvider = NormalizeOptional(signatureProvider);
 78112        SignedUtc = signedUtc?.ToUniversalTime();
 78113        SigningMetadata = SigningMetadataValue.Create(
 78114            SigningHash,
 78115            null,
 78116            SignatureValue,
 78117            SignatureAlgorithm,
 78118            SignatureKeyId,
 78119            SignatureKeyVersion,
 78120            SignatureProvider,
 78121            SignedUtc);
 78122        Metadata = metadata;
 78123    }
 124
 125    public string RecordId { get; }
 126
 127    public string SchemaVersion { get; }
 128
 129    public string EventId { get; }
 130
 131    public string DecisionReceiptId { get; }
 132
 133    public DateTimeOffset OccurredUtc { get; }
 134
 135    public DateTimeOffset RecordedUtc { get; }
 136
 137    public string ActorId { get; }
 138
 139    public GovernanceActorType ActorType { get; }
 140
 141    public string? ActorDisplayName { get; }
 142
 143    public string OperationName { get; }
 144
 145    public string Outcome { get; }
 146
 147    public IReadOnlyList<string> ReasonCodes { get; }
 148
 149    public string? CorrelationId { get; }
 150
 151    public string? TraceId { get; }
 152
 153    public string? SpanId { get; }
 154
 155    public string? ParentSpanId { get; }
 156
 157    public long? DecisionLatencyMs { get; }
 158
 159    public string? ConstraintSetHash { get; }
 160
 161    public int? ConstraintCount { get; }
 162
 163    public double? RiskScore { get; }
 164
 165    public string? PolicyScope { get; }
 166
 167    public string? TenantHash { get; }
 168
 169    public string? OrganizationHash { get; }
 170
 171    public string? EmitterStatus { get; }
 172
 173    public string? EmitterProvider { get; }
 174
 175    public long? OutboxSequence { get; }
 176
 177    public string? GatewayExecutionId { get; }
 178
 179    public string? DecisionStage { get; }
 180
 181    public string? PolicyVersion { get; }
 182
 183    public string? PolicyHash { get; }
 184
 185    public string? HandshakeId { get; }
 186
 187    public string? AcknowledgmentId { get; }
 188
 189    public string? CapabilityTokenId { get; }
 190
 191    public string? PreviousRecordHash { get; }
 192
 193    public string? RecordHash { get; }
 194
 195    public string? SigningHash { get; }
 196
 197    public string? SignatureKeyId { get; }
 198
 199    public string? SignatureKeyVersion { get; }
 200
 201    public string? SignatureAlgorithm { get; }
 202
 203    public string? SignatureValue { get; }
 204
 205    public string? SignatureProvider { get; }
 206
 207    public DateTimeOffset? SignedUtc { get; }
 208
 209    public SigningMetadataValue SigningMetadata { get; }
 210
 211    public IReadOnlyDictionary<string, string> Metadata { get; }
 212
 6213    public bool HasReasonCodes => ReasonCodes.Count > 0;
 214
 10215    public bool HasMetadata => Metadata.Count > 0;
 216
 217    public static AuditLedgerRecord FromDecisionReceipt(
 218        IDecisionReceipt receipt,
 219        string? recordId = null,
 220        DateTimeOffset? recordedUtc = null,
 221        string? handshakeId = null,
 222        string? acknowledgmentId = null,
 223        string? capabilityTokenId = null,
 224        string? previousRecordHash = null,
 225        string? recordHash = null,
 226        string? signatureKeyId = null,
 227        string? signatureAlgorithm = null,
 228        string? signatureValue = null,
 229        string? signingHash = null,
 230        string? signatureKeyVersion = null,
 231        string? signatureProvider = null,
 232        DateTimeOffset? signedUtc = null,
 233        IReadOnlyDictionary<string, string>? metadata = null,
 234        string? schemaVersion = null)
 235    {
 83236        ArgumentNullException.ThrowIfNull(receipt);
 237
 82238        return new AuditLedgerRecord(
 82239            NormalizeIdentifier(recordId),
 82240            schemaVersion ?? receipt.SchemaVersion,
 82241            receipt.EventId,
 82242            receipt.DecisionReceiptId,
 82243            receipt.OccurredUtc,
 82244            recordedUtc ?? DateTimeOffset.UtcNow,
 82245            receipt.ActorId,
 82246            receipt.ActorType,
 82247            receipt.ActorDisplayName,
 82248            receipt.OperationName,
 82249            receipt.Outcome,
 82250            NormalizeReasonCodes(receipt.ReasonCodes),
 82251            receipt.CorrelationId,
 82252            receipt.TraceId,
 82253            receipt.SpanId,
 82254            receipt.ParentSpanId,
 82255            receipt.DecisionLatencyMs,
 82256            receipt.ConstraintSetHash,
 82257            receipt.ConstraintCount,
 82258            receipt.RiskScore,
 82259            receipt.PolicyScope,
 82260            receipt.TenantHash,
 82261            receipt.OrganizationHash,
 82262            receipt.EmitterStatus,
 82263            receipt.EmitterProvider,
 82264            receipt.OutboxSequence,
 82265            receipt.GatewayExecutionId,
 82266            receipt.DecisionStage,
 82267            receipt.PolicyVersion,
 82268            receipt.PolicyHash,
 82269            handshakeId,
 82270            acknowledgmentId,
 82271            capabilityTokenId,
 82272            previousRecordHash,
 82273            recordHash,
 82274            signatureKeyId,
 82275            signatureAlgorithm,
 82276            signatureValue,
 82277            signingHash,
 82278            signatureKeyVersion,
 82279            signatureProvider,
 82280            signedUtc,
 82281            NormalizeMetadata(receipt.Metadata, metadata));
 282    }
 283
 284    private static string NormalizeIdentifier(string? identifier)
 285    {
 82286        return string.IsNullOrWhiteSpace(identifier)
 82287            ? Guid.NewGuid().ToString("N")
 82288            : identifier.Trim();
 289    }
 290
 291    private static string? NormalizeOptional(string? value)
 292    {
 2106293        return string.IsNullOrWhiteSpace(value)
 2106294            ? null
 2106295            : value.Trim();
 296    }
 297
 298    private static long? NormalizeNonNegative(long? value, string parameterName)
 299    {
 156300        return value < 0
 156301            ? throw new ArgumentOutOfRangeException(parameterName, value, "Value must be greater than or equal to zero."
 156302            : value;
 303    }
 304
 305    private static int? NormalizeNonNegative(int? value, string parameterName)
 306    {
 78307        return value < 0
 78308            ? throw new ArgumentOutOfRangeException(parameterName, value, "Value must be greater than or equal to zero."
 78309            : value;
 310    }
 311
 312    private static double? NormalizeRiskScore(double? riskScore)
 313    {
 78314        return riskScore is null
 78315            ? null
 78316            : double.IsNaN(riskScore.Value) || double.IsInfinity(riskScore.Value) || riskScore.Value < 0
 78317            ? throw new ArgumentOutOfRangeException(nameof(riskScore), riskScore, "Risk score must be a finite value gre
 78318            : riskScore;
 319    }
 320
 321    private static ReadOnlyCollection<string> NormalizeReasonCodes(IEnumerable<string>? reasonCodes)
 322    {
 82323        string[] normalizedReasonCodes = reasonCodes?
 82324            .Where(reasonCode => !string.IsNullOrWhiteSpace(reasonCode))
 82325            .Select(reasonCode => reasonCode.Trim())
 82326            .ToArray() ?? [];
 327
 82328        return normalizedReasonCodes.Length == 0
 82329            ? EmptyReasonCodes
 82330            : Array.AsReadOnly(normalizedReasonCodes);
 331    }
 332
 333    private static IReadOnlyDictionary<string, string> NormalizeMetadata(
 334        params IReadOnlyDictionary<string, string>?[] metadataSets)
 335    {
 82336        Dictionary<string, string> normalizedMetadata = new(StringComparer.Ordinal);
 337
 492338        foreach (IReadOnlyDictionary<string, string>? metadata in metadataSets)
 339        {
 164340            if (metadata is null || metadata.Count == 0)
 341            {
 342                continue;
 343            }
 344
 168345            foreach (KeyValuePair<string, string> item in metadata)
 346            {
 48347                if (string.IsNullOrWhiteSpace(item.Key))
 348                {
 349                    continue;
 350                }
 351
 45352                normalizedMetadata[item.Key.Trim()] = item.Value?.Trim() ?? string.Empty;
 353            }
 354        }
 355
 82356        return normalizedMetadata.Count == 0
 82357            ? EmptyMetadata
 82358            : new ReadOnlyDictionary<string, string>(normalizedMetadata);
 359    }
 360}

Methods/Properties

.cctor()
.ctor(System.String,System.String,System.String,System.String,System.DateTimeOffset,System.DateTimeOffset,System.String,AsiBackbone.Core.Actors.GovernanceActorType,System.String,System.String,System.String,System.Collections.Generic.IReadOnlyList`1<System.String>,System.String,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.Nullable`1<System.Int32>,System.Nullable`1<System.Double>,System.String,System.String,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.Nullable`1<System.DateTimeOffset>,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.String>)
get_HasReasonCodes()
get_HasMetadata()
FromDecisionReceipt(AsiBackbone.Core.Audit.IDecisionReceipt,System.String,System.Nullable`1<System.DateTimeOffset>,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.Nullable`1<System.DateTimeOffset>,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.String>,System.String)
NormalizeIdentifier(System.String)
NormalizeOptional(System.String)
NormalizeNonNegative(System.Nullable`1<System.Int64>,System.String)
NormalizeNonNegative(System.Nullable`1<System.Int32>,System.String)
NormalizeRiskScore(System.Nullable`1<System.Double>)
NormalizeReasonCodes(System.Collections.Generic.IEnumerable`1<System.String>)
NormalizeMetadata(System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.String>[])