< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Data.EntityMaps.GoogleUserDbMap
Assembly: ArturRios.Heimdall.Data
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Infrastructure/ArturRios.Heimdall.Data/EntityMaps/GoogleUserDbMap.cs
Line coverage
100%
Covered lines: 33
Uncovered lines: 0
Coverable lines: 33
Total lines: 77
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 1
Fully covered methods: 1
Total methods: 1
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Configure(...)100%11100%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Infrastructure/ArturRios.Heimdall.Data/EntityMaps/GoogleUserDbMap.cs

#LineLine coverage
 1using ArturRios.Heimdall.Domain.Entities;
 2using Microsoft.EntityFrameworkCore;
 3using Microsoft.EntityFrameworkCore.Metadata.Builders;
 4
 5namespace ArturRios.Heimdall.Data.EntityMaps;
 6
 7internal static class GoogleUserDbMap
 8{
 9    public static void Configure(this EntityTypeBuilder<GoogleUser> googleUser)
 10    {
 311        googleUser.ToTable("google_user");
 12
 313        googleUser.HasKey(x => x.Id);
 14
 315        googleUser.Property(x => x.PublicId).IsRequired();
 316        googleUser.HasIndex(x => x.PublicId).IsUnique();
 17
 318        googleUser.Property(x => x.GoogleId).IsRequired();
 19
 320        googleUser.Property(x => x.Email).IsRequired();
 21
 322        googleUser.Property(x => x.EmailVerified).HasDefaultValue(false);
 323        googleUser.Property(x => x.IsDeleted).HasDefaultValue(false);
 24
 25        // NFR-20's retention window is measured from DeletedAt, so the anonymisation pass reads it
 26        // on every run: indexed, and filtered to the rows that can possibly be due, which is a small
 27        // fraction of the table.
 328        googleUser.Property(x => x.DeletedAt);
 329        googleUser.Property(x => x.DeletionKind);
 330        googleUser.Property(x => x.AnonymisedAt);
 31
 32        // NFR-23: the basis is required and defaults to Unrecorded, so a row can never be silent
 33        // about it — a null would be indistinguishable from "nobody has looked at this yet".
 334        googleUser.Property(x => x.LegalBasis).HasDefaultValue(0);
 335        googleUser.Property(x => x.PrivacyNoticeVersion);
 336        googleUser.Property(x => x.BasisRecordedAt);
 37
 38        // NFR-24: indexed because every listing filters on it, and the restricted set is a small
 39        // fraction of the table.
 340        googleUser.Property(x => x.ProcessingRestrictedAt);
 341        googleUser.Property(x => x.RestrictionGround);
 342        googleUser.Property(x => x.RestrictionLiftNotifiedAt);
 43
 344        googleUser.HasIndex(x => x.ProcessingRestrictedAt)
 345            .HasFilter("processing_restricted_at IS NOT NULL");
 46
 347        googleUser.HasIndex(x => x.DeletedAt)
 348            .HasFilter("is_deleted = true AND anonymised_at IS NULL");
 49
 350        googleUser.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
 351        googleUser.Property(x => x.UpdatedAt).HasDefaultValueSql("now()");
 52
 53        // GoogleId (Google's 'sub' claim) is unique within the scope (FR-GO-08).
 354        googleUser.HasIndex(x => new { x.ScopeId, x.GoogleId }).IsUnique();
 55
 56        // Email is unique within the scope among Google Users (FR-GO-07), case-insensitively — the
 57        // application compares addresses with LOWER(), so an index over the raw column would accept
 58        // a pair the handler had already refused and enforce a rule nobody wrote. The expression
 59        // form is applied in the migration's raw SQL: EF cannot model an index over an expression,
 60        // so this call reserves the name and the migration replaces its definition.
 61        //
 62        // The joint uniqueness with User persons' emails spans two tables and cannot be a unique
 63        // index at all; it stays in the application layer (CreateUserCommandHandler,
 64        // UpdatePersonCommandHandler, GoogleSignInCommandHandler).
 365        googleUser.HasIndex(x => new { x.ScopeId, x.Email })
 366            .IsUnique()
 367            .HasDatabaseName("ix_google_user_scope_id_email");
 68
 69        // A Google User belongs to exactly one scope (FR-GO-06); hard-deleting the scope cascades
 70        // to its Google Users (NFR-14).
 371        googleUser.HasOne(x => x.Scope)
 372            .WithMany(s => s.GoogleUsers)
 373            .HasForeignKey(x => x.ScopeId)
 374            .IsRequired()
 375            .OnDelete(DeleteBehavior.Cascade);
 376    }
 77}