| | | 1 | | using ArturRios.Heimdall.Domain.Entities; |
| | | 2 | | using Microsoft.EntityFrameworkCore; |
| | | 3 | | using Microsoft.EntityFrameworkCore.Metadata.Builders; |
| | | 4 | | |
| | | 5 | | namespace ArturRios.Heimdall.Data.EntityMaps; |
| | | 6 | | |
| | | 7 | | internal static class ScopeOwnerInvitationDbMap |
| | | 8 | | { |
| | | 9 | | public static void Configure(this EntityTypeBuilder<ScopeOwnerInvitation> invitation) |
| | | 10 | | { |
| | | 11 | | // An outcome is one of the four, and only an ended row has one; an ended row without one ended by |
| | | 12 | | // expiry (ScopeOwnerInvitationOutcomes). |
| | 4 | 13 | | invitation.ToTable("scope_owner_invitation", table => |
| | 4 | 14 | | { |
| | 4 | 15 | | table.HasCheckConstraint("ck_scope_owner_invitation_outcome_known", "outcome IS NULL OR outcome BETWEEN 1 AN |
| | 4 | 16 | | table.HasCheckConstraint("ck_scope_owner_invitation_outcome_ends_it", "outcome IS NULL OR ended_at IS NOT NU |
| | 8 | 17 | | }); |
| | | 18 | | |
| | | 19 | | // Internal bigint Id as the key; PublicId is the reference the API uses (§4.0). |
| | 4 | 20 | | invitation.HasKey(x => x.Id); |
| | | 21 | | |
| | 4 | 22 | | invitation.Property(x => x.PublicId).IsRequired(); |
| | 4 | 23 | | invitation.HasIndex(x => x.PublicId).IsUnique(); |
| | | 24 | | |
| | 4 | 25 | | invitation.Property(x => x.CreatedAt).HasDefaultValueSql("now()"); |
| | 4 | 26 | | invitation.Property(x => x.ExpiresAt).IsRequired(); |
| | 4 | 27 | | invitation.Property(x => x.Outcome).HasColumnType("smallint"); |
| | | 28 | | |
| | | 29 | | // One unended invitation per scope and invitee (FR-SC-18). An expired row still holds the slot until |
| | | 30 | | // ScopeOwnerInvitations.OpenAsync closes it. |
| | 4 | 31 | | invitation.HasIndex(x => new { x.ScopeId, x.InviteePersonId }) |
| | 4 | 32 | | .IsUnique() |
| | 4 | 33 | | .HasFilter("ended_at IS NULL"); |
| | | 34 | | |
| | | 35 | | // An invitation means nothing once its scope or its invitee is gone (UC-05, UC-10). |
| | 4 | 36 | | invitation.HasOne(x => x.Scope) |
| | 4 | 37 | | .WithMany() |
| | 4 | 38 | | .HasForeignKey(x => x.ScopeId) |
| | 4 | 39 | | .IsRequired() |
| | 4 | 40 | | .OnDelete(DeleteBehavior.Cascade); |
| | | 41 | | |
| | 4 | 42 | | invitation.HasOne(x => x.Invitee) |
| | 4 | 43 | | .WithMany() |
| | 4 | 44 | | .HasForeignKey(x => x.InviteePersonId) |
| | 4 | 45 | | .IsRequired() |
| | 4 | 46 | | .OnDelete(DeleteBehavior.Cascade); |
| | | 47 | | |
| | | 48 | | // The inviter's removal does not end it: the invitation stands for the scope, not for them. |
| | 4 | 49 | | invitation.HasOne(x => x.InvitedBy) |
| | 4 | 50 | | .WithMany() |
| | 4 | 51 | | .HasForeignKey(x => x.InvitedByPersonId) |
| | 4 | 52 | | .IsRequired(false) |
| | 4 | 53 | | .OnDelete(DeleteBehavior.SetNull); |
| | 4 | 54 | | } |
| | | 55 | | } |