< Summary - Heimdall API

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

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_PublicId()100%11100%
get_GoogleId()100%11100%
get_Name()100%11100%
get_Email()100%11100%
get_EmailVerified()100%11100%
get_ProfilePictureUrl()100%11100%
get_IsDeleted()100%11100%
get_DeletedAt()100%11100%
get_DeletionKind()100%11100%
get_AnonymisedAt()100%11100%
get_ErasureRequestedAt()100%11100%
get_ErasureDueAt()100%11100%
get_ErasureBlockedReason()100%11100%
get_ScopeId()100%11100%
get_LegalBasis()100%11100%
get_PrivacyNoticeVersion()100%11100%
get_BasisRecordedAt()100%11100%
get_ProcessingRestrictedAt()100%11100%
get_RestrictionGround()100%11100%
get_RestrictionLiftNotifiedAt()100%11100%
get_CreatedAt()100%11100%
get_UpdatedAt()100%11100%
get_Scope()100%11100%

File(s)

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

#LineLine coverage
 1using System.ComponentModel.DataAnnotations;
 2using ArturRios.Data.Relational.Core.Entities;
 3
 4namespace ArturRios.Heimdall.Domain.Entities;
 5
 6/// <summary>
 7///     A registered identity authenticated via Google Sign-In rather than a password. Always
 8///     equivalent to the <c>User</c> role and belongs to exactly one <see cref="Scope" />. Stored
 9///     in its own table with a direct <see cref="ScopeId" /> instead of going through the
 10///     owner/user join tables, since it never needs ownership or multi-scope semantics. Has no
 11///     <c>PasswordHash</c>, <c>Salt</c>, or <c>RoleId</c> — authentication is delegated to Google.
 12/// </summary>
 13public class GoogleUser : Entity<long>
 14{
 15    /// <summary>
 16    ///     External identifier, generated on creation and used everywhere the Google User is
 17    ///     addressed from outside the database (API paths, response bodies, token claims). The
 18    ///     internal <see cref="Entity{TKey}.Id" /> is never exposed to callers.
 19    /// </summary>
 73220    public Guid PublicId { get; set; } = Guid.NewGuid();
 21
 22    /// <summary>Google's stable <c>sub</c> claim. Required, unique within the scope.</summary>
 23    [MaxLength(255)]
 49724    public string GoogleId { get; set; } = string.Empty;
 25
 26    /// <summary>Full name, from Google's <c>name</c> claim. Max 200 characters.</summary>
 27    [MaxLength(200)]
 48228    public string Name { get; set; } = string.Empty;
 29
 30    /// <summary>
 31    ///     Email address, from Google's <c>email</c> claim. Required, unique within the scope,
 32    ///     considered jointly with <c>User</c> persons' emails in that scope.
 33    /// </summary>
 34    [MaxLength(256)]
 49635    public string Email { get; set; } = string.Empty;
 36
 37    /// <summary>Email verification status, from Google's <c>email_verified</c> claim.</summary>
 24038    public bool EmailVerified { get; set; }
 39
 40    /// <summary>Optional profile picture URL, from Google's <c>picture</c> claim.</summary>
 41    [MaxLength(2048)]
 10642    public string? ProfilePictureUrl { get; set; }
 43
 44    /// <summary>Logical deletion flag. Logically deleted Google Users are excluded from default queries.</summary>
 27645    public bool IsDeleted { get; set; }
 46
 47    /// <summary>
 48    ///     When the record was logically deleted, or <c>null</c> while it is live. The retention
 49    ///     window that ends in anonymisation is measured from here (NFR-20).
 50    /// </summary>
 51    /// <remarks>
 52    ///     <see cref="UpdatedAt" /> cannot serve: it moves on any later write, so a record touched
 53    ///     after its deletion would have its window silently restarted. This one is written once, by
 54    ///     the logical deletion, and never again.
 55    /// </remarks>
 4756    public DateTime? DeletedAt { get; set; }
 57
 58    /// <summary>
 59    ///     Why the record was deleted (see <c>DeletionKinds</c>), or <c>null</c> while it is live.
 60    ///     Decides which of the schedule's two deadlines applies.
 61    /// </summary>
 4562    public int? DeletionKind { get; set; }
 63
 64    /// <summary>
 65    ///     When the record was anonymised, or <c>null</c> if it has not been. Set by the scheduled
 66    ///     pass, and the reason a second pass over the same row is a no-op rather than a repeated
 67    ///     overwrite.
 68    /// </summary>
 6669    public DateTime? AnonymisedAt { get; set; }
 70
 71    /// <summary>
 72    ///     When the data subject asked to be erased (UC-42), or <c>null</c> if they have not. Set
 73    ///     once, by the request, and never moved: the statutory deadline runs from the request, not
 74    ///     from any later event.
 75    /// </summary>
 4276    public DateTime? ErasureRequestedAt { get; set; }
 77
 78    /// <summary>
 79    ///     The deadline the erasure must not outlive, computed when the request is made.
 80    /// </summary>
 81    /// <remarks>
 82    ///     Stored rather than derived on read, and that is the point. GDPR Art. 12(3) attaches an
 83    ///     obligation at the moment of the request; recomputing it from the current configuration
 84    ///     would let a later change to the deadline move an obligation that had already attached —
 85    ///     forward, which is unlawful, or backward, which would silently mark existing requests
 86    ///     overdue.
 87    /// </remarks>
 2988    public DateTime? ErasureDueAt { get; set; }
 89
 90    /// <summary>
 91    ///     Why the erasure could not be carried out yet, or <c>null</c> if nothing blocks it. One of
 92    ///     the application's own canonical messages, never caller input.
 93    /// </summary>
 94    /// <remarks>
 95    ///     A blocked request is still a request: the deadline keeps running, and this is what makes
 96    ///     the reason visible to the administrator who has to clear it, rather than the request
 97    ///     failing and leaving no trace.
 98    /// </remarks>
 99    [MaxLength(500)]
 24100    public string? ErasureBlockedReason { get; set; }
 101
 102    /// <summary>Foreign key to the associated <see cref="Scope" /> (internal Id). Required.</summary>
 273103    public long ScopeId { get; set; }
 104
 105
 106    /// <summary>
 107    ///     The lawful basis this identity's data is processed on (see <c>LegalBases</c>), recorded
 108    ///     when the identity was created (GDPR Art. 5(2), LGPD Art. 8 §2).
 109    /// </summary>
 45110    public int LegalBasis { get; set; }
 111
 112    /// <summary>
 113    ///     The version of the privacy notice in force when the identity was created, or <c>null</c>
 114    ///     for identities predating the mechanism.
 115    /// </summary>
 116    /// <remarks>
 117    ///     Stored rather than looked up, for the same reason the erasure deadline is: what the
 118    ///     person was told is a fact about a moment, and reading the current version later would
 119    ///     answer a different question — what they would be told today.
 120    /// </remarks>
 121    [MaxLength(50)]
 43122    public string? PrivacyNoticeVersion { get; set; }
 123
 124    /// <summary>When the basis and notice version were recorded.</summary>
 44125    public DateTime? BasisRecordedAt { get; set; }
 126
 127    /// <summary>
 128    ///     When processing of this identity was restricted (GDPR Art. 18, LGPD Art. 18 III–IV), or
 129    ///     <c>null</c> if it is not restricted.
 130    /// </summary>
 131    /// <remarks>
 132    ///     Independent of <see cref="IsDeleted" />, and necessarily so. Deletion means the identity
 133    ///     is on its way out; restriction means it is disputed and must be preserved exactly as it
 134    ///     is while that is resolved. Overloading the deletion flag would make a restricted record
 135    ///     disappear from the administrator's view that has to resolve the dispute.
 136    /// </remarks>
 83137    public DateTime? ProcessingRestrictedAt { get; set; }
 138
 139    /// <summary>
 140    ///     Which of Art. 18(1)'s four grounds the restriction rests on (see
 141    ///     <c>RestrictionGrounds</c>), or <c>null</c> if not restricted.
 142    /// </summary>
 29143    public int? RestrictionGround { get; set; }
 144
 145    /// <summary>
 146    ///     When the subject was told the restriction was about to be lifted, or <c>null</c>.
 147    /// </summary>
 148    /// <remarks>
 149    ///     GDPR Art. 18(3) requires the subject be informed <em>before</em> a restriction imposed at
 150    ///     their request is lifted. This records that it happened, and the lift refuses to proceed
 151    ///     without it.
 152    /// </remarks>
 24153    public DateTime? RestrictionLiftNotifiedAt { get; set; }
 154
 155
 156    /// <summary>Creation timestamp.</summary>
 41157    public DateTime CreatedAt { get; set; }
 158
 159    /// <summary>Last update timestamp.</summary>
 85160    public DateTime UpdatedAt { get; set; }
 161
 162    // Navigation properties
 163
 164    /// <summary>The scope this Google User belongs to.</summary>
 98165    public Scope Scope { get; set; } = null!;
 166}