< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Domain.Entities.ScopeOwnerInvitation
Assembly: ArturRios.Heimdall.Domain
File(s): /home/ci/agent/workspace/heimdall-api-docs_main/app/src/Domain/ArturRios.Heimdall.Domain/Entities/ScopeOwnerInvitation.cs
Line coverage
100%
Covered lines: 15
Uncovered lines: 0
Coverable lines: 15
Total lines: 71
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 14
Fully covered methods: 14
Total methods: 14
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_PublicId()100%11100%
get_ScopeId()100%11100%
get_InviteePersonId()100%11100%
get_InvitedByPersonId()100%11100%
get_CreatedAt()100%11100%
get_ExpiresAt()100%11100%
get_EndedAt()100%11100%
get_Outcome()100%11100%
get_Scope()100%11100%
get_Invitee()100%11100%
get_InvitedBy()100%11100%
OpenAt(...)100%11100%
get_EligibleInvitee()100%11100%
.cctor()100%11100%

File(s)

/home/ci/agent/workspace/heimdall-api-docs_main/app/src/Domain/ArturRios.Heimdall.Domain/Entities/ScopeOwnerInvitation.cs

#LineLine coverage
 1using System.Linq.Expressions;
 2using ArturRios.Data.Relational.Core.Entities;
 3using ArturRios.Heimdall.Domain.Enums;
 4
 5namespace ArturRios.Heimdall.Domain.Entities;
 6
 7/// <summary>
 8///     An invitation to co-own a scope (UC-21, FR-SC-14): a Scope Admin asked for an existing administrator
 9///     to become an owner, and the ownership starts only when that person accepts it, signed in as
 10///     themselves (UC-60). It ends exactly once — accepted, declined, withdrawn or voided — or runs out at
 11///     <see cref="ExpiresAt" />.
 12/// </summary>
 13/// <remarks>
 14///     At most one invitation per scope and invitee is unended at a time, which a partial unique index
 15///     holds. An expired row still counts until something closes it, so opening a new one first closes an
 16///     expired one with <see cref="EndedAt" /> at its expiry and no <see cref="Outcome" />.
 17/// </remarks>
 18public class ScopeOwnerInvitation : Entity<long>
 19{
 20    /// <summary>
 21    ///     How the scope's owners withdraw it (UC-58) and the invitee answers it (UC-60, UC-61). Random, so it
 22    ///     discloses nothing about the people involved.
 23    /// </summary>
 59624    public Guid PublicId { get; set; } = Guid.NewGuid();
 25
 26    /// <summary>Foreign key to the scope the invitee is asked to co-own (internal Id).</summary>
 26627    public long ScopeId { get; set; }
 28
 29    /// <summary>Foreign key to the invited administrator (internal Id); only they may answer.</summary>
 29430    public long InviteePersonId { get; set; }
 31
 32    /// <summary>
 33    ///     Foreign key to the administrator who invited (internal Id). Cleared when they are anonymised, and
 34    ///     set to null by the database when they are hard-deleted.
 35    /// </summary>
 19936    public long? InvitedByPersonId { get; set; }
 37
 19638    public DateTime CreatedAt { get; set; }
 39
 40    /// <summary>When it stops being acceptable (<c>HEIMDALL_SCOPE_INVITATION_LIFETIME_IN_SECONDS</c> after creation).</
 29841    public DateTime ExpiresAt { get; set; }
 42
 43    /// <summary>When it ended; <c>null</c> while it has not.</summary>
 30344    public DateTime? EndedAt { get; set; }
 45
 46    /// <summary>How it ended; <c>null</c> while open, and for one that ended by expiry.</summary>
 18547    public ScopeOwnerInvitationOutcomes? Outcome { get; set; }
 48
 8849    public Scope Scope { get; set; } = null!;
 50
 12551    public Person Invitee { get; set; } = null!;
 52
 11453    public Person? InvitedBy { get; set; }
 54
 55    /// <summary>
 56    ///     Open at <paramref name="now" />: not ended, and its expiry still ahead. An expression, so a query
 57    ///     and a conditional write ask exactly the same question.
 58    /// </summary>
 59    public static Expression<Func<ScopeOwnerInvitation, bool>> OpenAt(DateTime now) =>
 13660        invitation => invitation.EndedAt == null && invitation.ExpiresAt > now;
 61
 62    /// <summary>
 63    ///     Whether a person may still accept an invitation (UC-60): not logically deleted, not
 64    ///     processing-restricted, not anonymised, and a Scope Admin or System Admin. The one definition of
 65    ///     the rule: the PostgreSQL acceptance writes it into its conditional <c>UPDATE</c>, and code that
 66    ///     holds a person in memory compiles it. The scope must be live too, which this does not cover.
 67    /// </summary>
 3568    public static Expression<Func<Person, bool>> EligibleInvitee { get; } = person =>
 369        !person.IsDeleted && person.ProcessingRestrictedAt == null && person.AnonymisedAt == null &&
 370        (person.RoleId == (long)Roles.ScopeAdmin || person.RoleId == (long)Roles.SystemAdmin);
 71}