< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Domain.Entities.AuditLog
Assembly: ArturRios.Heimdall.Domain
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Domain/ArturRios.Heimdall.Domain/Entities/AuditLog.cs
Line coverage
100%
Covered lines: 8
Uncovered lines: 0
Coverable lines: 8
Total lines: 95
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 8
Fully covered methods: 8
Total methods: 8
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_PublicId()100%11100%
get_ActorPersonId()100%11100%
get_ActorRole()100%11100%
get_Action()100%11100%
get_TargetId()100%11100%
get_Succeeded()100%11100%
get_FailureReason()100%11100%
get_CreatedAt()100%11100%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Domain/ArturRios.Heimdall.Domain/Entities/AuditLog.cs

#LineLine coverage
 1using System.ComponentModel.DataAnnotations;
 2using ArturRios.Data.Relational.Core.Entities;
 3
 4namespace ArturRios.Heimdall.Domain.Entities;
 5
 6/// <summary>
 7///     One audit trail entry per attempted write operation (NFR-09), whether it succeeded or not.
 8///     <see cref="ActorPersonId" /> is a bare <c>PublicId</c>, not a foreign key, so an entry
 9///     survives a hard-deleted person.
 10/// </summary>
 11/// <remarks>
 12///     <para>
 13///         <b>Append-only, with exactly two exceptions.</b> Database triggers refuse every
 14///         <c>DELETE</c> and <c>TRUNCATE</c>, and refuse any <c>UPDATE</c> other than clearing
 15///         <see cref="ActorPersonId" /> and <see cref="ActorRole" />, or clearing a
 16///         <see cref="TargetId" /> that names a person or Google User. Each may be set to
 17///         <c>null</c>, never to somebody else, and only once due (NFR-21). The resulting guarantee
 18///         is precise: an action can never be denied, and who took it or whom it was taken on can
 19///         never be reassigned — only forgotten, on schedule or on erasure.
 20///     </para>
 21///     <para>
 22///         That exception exists because the trail holds personal data. An indefinitely retained,
 23///         unalterable row naming a person who has exercised their right to erasure is personal data
 24///         processed with no remaining basis. Clearing the attribution leaves what happened, when and
 25///         how often intact while the entry stops relating to an identifiable person at all.
 26///     </para>
 27/// </remarks>
 28public class AuditLog : Entity<long>
 29{
 30    /// <summary>External identifier of this entry.</summary>
 1741831    public Guid PublicId { get; set; } = Guid.NewGuid();
 32
 33    /// <summary>
 34    ///     The acting person's <c>PublicId</c>; <c>null</c> for an anonymous write, and <c>null</c>
 35    ///     again once the attribution has been cleared (NFR-21).
 36    /// </summary>
 37    /// <remarks>
 38    ///     <para>
 39    ///         This and <see cref="TargetId" /> are the columns on this entity that may change after
 40    ///         creation, and they may only ever change to <c>null</c>. Everything else is immutable,
 41    ///         and the row can never be deleted — see the remarks on the class.
 42    ///     </para>
 43    ///     <para>
 44    ///         Two grounds make a clearing due: the entry is past the configured attribution period,
 45    ///         or the identity it names has been anonymised. Both are enforced by the database, not
 46    ///         here.
 47    ///     </para>
 48    /// </remarks>
 1143149    public Guid? ActorPersonId { get; set; }
 50
 51    /// <summary>
 52    ///     The acting person's role value (see <c>Roles</c>); <c>null</c> for an anonymous write, and
 53    ///     cleared alongside <see cref="ActorPersonId" />.
 54    /// </summary>
 117555    public int? ActorRole { get; set; }
 56
 57    /// <summary>The command's CLR type name, e.g. <c>"CreateApplicationCommand"</c>.</summary>
 58    [MaxLength(200)]
 1741259    public string Action { get; set; } = string.Empty;
 60
 61    /// <summary>
 62    ///     Best-effort public identifier of the entity the write affected; <c>null</c> if none could be
 63    ///     resolved, and <c>null</c> again once a person target has been cleared (NFR-21).
 64    /// </summary>
 65    /// <remarks>
 66    ///     When this names a person or Google User the entry is personal data about them, whoever
 67    ///     acted, so it is cleared on the actor's two grounds: past the attribution period, or once
 68    ///     that identity is anonymised. It names one still after a hard delete, which tombstones the
 69    ///     id first (<see cref="ErasedSubject" />, #195), so a tombstoned id with no row left is
 70    ///     cleared too, at any age. A target naming anything else is not personal data and never
 71    ///     changes. The database enforces all of it.
 72    /// </remarks>
 629573    public Guid? TargetId { get; set; }
 74
 75    /// <summary>
 76    ///     Whether the operation succeeded. The trail records refusals as well as writes, because a
 77    ///     refused attempt is usually the more interesting one: a caller repeatedly denied a scope
 78    ///     they do not own, or repeatedly failing a password, leaves no other trace.
 79    /// </summary>
 1130480    public bool Succeeded { get; set; }
 81
 82    /// <summary>
 83    ///     The first error the operation reported, when it failed; <c>null</c> on success.
 84    /// </summary>
 85    /// <remarks>
 86    ///     One of the application's own canonical messages, or one of the persistence layer's
 87    ///     classified failures — never a caller-supplied value and never provider text, so the trail
 88    ///     cannot become a place where a submitted password or an address ends up recorded verbatim.
 89    /// </remarks>
 90    [MaxLength(500)]
 622791    public string? FailureReason { get; set; }
 92
 93    /// <summary>When the entry was written.</summary>
 1650694    public DateTime CreatedAt { get; set; }
 95}