< Summary

Information
Class: AsiBackbone.Core.Audit.AuditResidueLifecycleEvent
Assembly: AsiBackbone.Core
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Audit/AuditResidueLifecycleEvent.cs
Line coverage
100%
Covered lines: 78
Uncovered lines: 0
Coverable lines: 78
Total lines: 224
Line coverage: 100%
Branch coverage
91%
Covered branches: 31
Total branches: 34
Branch coverage: 91.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%22100%
get_EventId()100%11100%
get_Stage()100%11100%
get_StageSequence()100%11100%
get_OccurredUtc()100%11100%
get_CorrelationId()100%11100%
get_AuditResidueId()100%11100%
get_TraceId()100%11100%
get_OperationName()100%11100%
get_Outcome()100%11100%
get_Metadata()100%11100%
get_HasAuditResidueId()100%11100%
get_HasMetadata()100%11100%
Create(...)100%22100%
FromResidue(...)90%1010100%
NormalizeIdentifier(...)100%22100%
NormalizeOptional(...)100%22100%
NormalizeMetadata(...)87.5%1616100%

File(s)

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

#LineLine coverage
 1using System.Collections.ObjectModel;
 2
 3namespace AsiBackbone.Core.Audit;
 4
 5/// <summary>
 6/// Represents a framework-neutral lifecycle event linked to a governed audit residue flow.
 7/// </summary>
 8/// <remarks>
 9/// Lifecycle events are append-only progress records. They allow acknowledgment, capability token, gateway, outbox, and
 10/// </remarks>
 11public sealed class AuditResidueLifecycleEvent
 12{
 213    private static readonly IReadOnlyDictionary<string, string> EmptyMetadata =
 214        new ReadOnlyDictionary<string, string>(
 215            new Dictionary<string, string>(StringComparer.Ordinal));
 16
 16017    private AuditResidueLifecycleEvent(
 16018        string eventId,
 16019        AuditResidueLifecycleStage stage,
 16020        DateTimeOffset occurredUtc,
 16021        string correlationId,
 16022        string? auditResidueId,
 16023        string? traceId,
 16024        string? operationName,
 16025        string? outcome,
 16026        IReadOnlyDictionary<string, string> metadata)
 27    {
 16028        ArgumentException.ThrowIfNullOrWhiteSpace(eventId);
 16029        ArgumentException.ThrowIfNullOrWhiteSpace(correlationId);
 30
 15431        if (!Enum.IsDefined(stage))
 32        {
 233            throw new ArgumentOutOfRangeException(nameof(stage), stage, "Lifecycle stage must be a defined audit residue
 34        }
 35
 15236        EventId = eventId.Trim();
 15237        Stage = stage;
 15238        OccurredUtc = occurredUtc.ToUniversalTime();
 15239        CorrelationId = correlationId.Trim();
 15240        AuditResidueId = NormalizeOptional(auditResidueId);
 15241        TraceId = NormalizeOptional(traceId);
 15242        OperationName = NormalizeOptional(operationName);
 15243        Outcome = NormalizeOptional(outcome);
 15244        Metadata = metadata;
 15245    }
 46
 47    /// <summary>
 48    /// Gets the stable identifier for this lifecycle event.
 49    /// </summary>
 22450    public string EventId { get; }
 51
 52    /// <summary>
 53    /// Gets the lifecycle stage represented by this event.
 54    /// </summary>
 17655    public AuditResidueLifecycleStage Stage { get; }
 56
 57    /// <summary>
 58    /// Gets the stable sequence value for this lifecycle stage.
 59    /// </summary>
 7260    public int StageSequence => (int)Stage;
 61
 62    /// <summary>
 63    /// Gets the UTC timestamp when the lifecycle event occurred.
 64    /// </summary>
 10665    public DateTimeOffset OccurredUtc { get; }
 66
 67    /// <summary>
 68    /// Gets the correlation identifier that links this lifecycle event to the original decision context.
 69    /// </summary>
 13670    public string CorrelationId { get; }
 71
 72    /// <summary>
 73    /// Gets the related audit residue identifier when the original decision residue is available.
 74    /// </summary>
 10475    public string? AuditResidueId { get; }
 76
 77    /// <summary>
 78    /// Gets the trace identifier associated with the lifecycle event, when supplied by the host or original residue.
 79    /// </summary>
 6880    public string? TraceId { get; }
 81
 82    /// <summary>
 83    /// Gets the operation name associated with the lifecycle event, when supplied by the host or original residue.
 84    /// </summary>
 6485    public string? OperationName { get; }
 86
 87    /// <summary>
 88    /// Gets the decision, gateway, emission, or host-defined outcome associated with this lifecycle event, when supplie
 89    /// </summary>
 6490    public string? Outcome { get; }
 91
 92    /// <summary>
 93    /// Gets additional framework-neutral lifecycle metadata supplied by the host.
 94    /// </summary>
 11695    public IReadOnlyDictionary<string, string> Metadata { get; }
 96
 97    /// <summary>
 98    /// Gets a value indicating whether this lifecycle event is linked to an audit residue identifier.
 99    /// </summary>
 2100    public bool HasAuditResidueId => AuditResidueId is not null;
 101
 102    /// <summary>
 103    /// Gets a value indicating whether this lifecycle event contains metadata.
 104    /// </summary>
 2105    public bool HasMetadata => Metadata.Count > 0;
 106
 107    /// <summary>
 108    /// Creates an audit residue lifecycle event.
 109    /// </summary>
 110    /// <param name="stage">The lifecycle stage represented by this event.</param>
 111    /// <param name="correlationId">The correlation identifier linking the event to the original decision context.</para
 112    /// <param name="auditResidueId">Optional audit residue identifier when the original decision residue is available.<
 113    /// <param name="eventId">Optional lifecycle event identifier. When omitted, a new identifier is generated.</param>
 114    /// <param name="occurredUtc">Optional lifecycle timestamp. When omitted, the current UTC timestamp is used.</param>
 115    /// <param name="traceId">Optional trace identifier.</param>
 116    /// <param name="operationName">Optional operation name.</param>
 117    /// <param name="outcome">Optional lifecycle or host-defined outcome.</param>
 118    /// <param name="metadata">Optional host-provided lifecycle metadata.</param>
 119    /// <returns>An audit residue lifecycle event.</returns>
 120    public static AuditResidueLifecycleEvent Create(
 121        AuditResidueLifecycleStage stage,
 122        string correlationId,
 123        string? auditResidueId = null,
 124        string? eventId = null,
 125        DateTimeOffset? occurredUtc = null,
 126        string? traceId = null,
 127        string? operationName = null,
 128        string? outcome = null,
 129        IReadOnlyDictionary<string, string>? metadata = null)
 130    {
 136131        return new AuditResidueLifecycleEvent(
 136132            NormalizeIdentifier(eventId),
 136133            stage,
 136134            occurredUtc ?? DateTimeOffset.UtcNow,
 136135            correlationId,
 136136            auditResidueId,
 136137            traceId,
 136138            operationName,
 136139            outcome,
 136140            NormalizeMetadata(metadata));
 141    }
 142
 143    /// <summary>
 144    /// Creates an audit residue lifecycle event by copying correlation context from existing audit residue.
 145    /// </summary>
 146    /// <param name="stage">The lifecycle stage represented by this event.</param>
 147    /// <param name="residue">The original audit residue to correlate with the lifecycle event.</param>
 148    /// <param name="correlationId">Optional correlation identifier override. When omitted, the residue correlation iden
 149    /// <param name="auditResidueId">Optional audit residue identifier override. When omitted, the residue event identif
 150    /// <param name="eventId">Optional lifecycle event identifier. When omitted, a new identifier is generated.</param>
 151    /// <param name="occurredUtc">Optional lifecycle timestamp. When omitted, the current UTC timestamp is used.</param>
 152    /// <param name="outcome">Optional lifecycle or host-defined outcome. When omitted, the residue outcome is used.</pa
 153    /// <param name="metadata">Optional host-provided lifecycle metadata merged after residue metadata.</param>
 154    /// <returns>An audit residue lifecycle event.</returns>
 155    public static AuditResidueLifecycleEvent FromResidue(
 156        AuditResidueLifecycleStage stage,
 157        IAsiBackboneAuditResidue residue,
 158        string? correlationId = null,
 159        string? auditResidueId = null,
 160        string? eventId = null,
 161        DateTimeOffset? occurredUtc = null,
 162        string? outcome = null,
 163        IReadOnlyDictionary<string, string>? metadata = null)
 164    {
 26165        ArgumentNullException.ThrowIfNull(residue);
 166
 26167        string? effectiveCorrelationId = string.IsNullOrWhiteSpace(correlationId)
 26168            ? residue.CorrelationId
 26169            : correlationId;
 170
 26171        return new AuditResidueLifecycleEvent(
 26172            NormalizeIdentifier(eventId),
 26173            stage,
 26174            occurredUtc ?? DateTimeOffset.UtcNow,
 26175            effectiveCorrelationId ?? throw new ArgumentException("A lifecycle event requires a correlation identifier f
 26176            string.IsNullOrWhiteSpace(auditResidueId) ? residue.EventId : auditResidueId,
 26177            residue.TraceId,
 26178            residue.OperationName,
 26179            string.IsNullOrWhiteSpace(outcome) ? residue.Outcome : outcome,
 26180            NormalizeMetadata(residue.Metadata, metadata));
 181    }
 182
 183    private static string NormalizeIdentifier(string? identifier)
 184    {
 162185        return string.IsNullOrWhiteSpace(identifier)
 162186            ? Guid.NewGuid().ToString("N")
 162187            : identifier.Trim();
 188    }
 189
 190    private static string? NormalizeOptional(string? value)
 191    {
 608192        return string.IsNullOrWhiteSpace(value)
 608193            ? null
 608194            : value.Trim();
 195    }
 196
 197    private static IReadOnlyDictionary<string, string> NormalizeMetadata(
 198        params IReadOnlyDictionary<string, string>?[] metadataSets)
 199    {
 160200        Dictionary<string, string> normalizedMetadata = new(StringComparer.Ordinal);
 201
 688202        foreach (IReadOnlyDictionary<string, string>? metadata in metadataSets)
 203        {
 184204            if (metadata is null || metadata.Count == 0)
 205            {
 206                continue;
 207            }
 208
 980209            foreach (KeyValuePair<string, string> item in metadata)
 210            {
 376211                if (string.IsNullOrWhiteSpace(item.Key))
 212                {
 213                    continue;
 214                }
 215
 374216                normalizedMetadata[item.Key.Trim()] = item.Value?.Trim() ?? string.Empty;
 217            }
 218        }
 219
 160220        return normalizedMetadata.Count == 0
 160221            ? EmptyMetadata
 160222            : new ReadOnlyDictionary<string, string>(normalizedMetadata);
 223    }
 224}