Initial import
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MinAttest.Domain.Entities;
|
||||
using MinAttest.Application.Abstractions;
|
||||
|
||||
namespace MinAttest.Infrastructure.Data;
|
||||
|
||||
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options), IAppDbContext
|
||||
{
|
||||
public DbSet<Person> Persons => Set<Person>();
|
||||
public DbSet<Employer> Employers => Set<Employer>();
|
||||
public DbSet<Attest> Attests => Set<Attest>();
|
||||
public DbSet<ShareLink> ShareLinks => Set<ShareLink>();
|
||||
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
|
||||
public DbSet<EmployerUser> EmployerUsers => Set<EmployerUser>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Person>(b =>
|
||||
{
|
||||
b.HasKey(x => x.Id);
|
||||
b.Property(x => x.NationalIdHash).HasMaxLength(200);
|
||||
b.HasIndex(x => x.NationalIdHash);
|
||||
b.ToTable(t => t.IsTemporal(tt => tt
|
||||
.UseHistoryTable("PersonsHistory")
|
||||
));
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Employer>(b =>
|
||||
{
|
||||
b.HasKey(x => x.Id);
|
||||
b.Property(x => x.OrgNumber).HasMaxLength(32);
|
||||
b.HasIndex(x => x.OrgNumber).IsUnique(false);
|
||||
b.ToTable(t => t.IsTemporal(tt => tt
|
||||
.UseHistoryTable("EmployersHistory")
|
||||
));
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Attest>(b =>
|
||||
{
|
||||
b.HasKey(x => x.Id);
|
||||
b.Property(x => x.Title).HasMaxLength(200);
|
||||
b.Property(x => x.Summary).HasMaxLength(2000);
|
||||
b.Property(x => x.BlobPath).HasMaxLength(500);
|
||||
b.Property(x => x.BlobHash).HasMaxLength(128);
|
||||
b.Property(x => x.Content).HasColumnType("varbinary(max)");
|
||||
b.Property(x => x.ContentType).HasMaxLength(255);
|
||||
b.Property(x => x.ContentLength).HasColumnType("bigint");
|
||||
b.HasOne(x => x.Person)
|
||||
.WithMany(x => x.Attests)
|
||||
.HasForeignKey(x => x.PersonId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
b.HasOne(x => x.Employer)
|
||||
.WithMany(x => x.Attests)
|
||||
.HasForeignKey(x => x.EmployerId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
// Consistency: EmployerId null <=> Unverified, EmployerId set => Issued/Revoked
|
||||
b.ToTable(t =>
|
||||
{
|
||||
t.HasCheckConstraint(
|
||||
"CK_Attests_Verification",
|
||||
"(([EmployerId] IS NULL AND [Status] = 2) OR ([EmployerId] IS NOT NULL AND [Status] IN (1,3)))");
|
||||
t.IsTemporal(tt => tt.UseHistoryTable("AttestsHistory"));
|
||||
});
|
||||
|
||||
// Helpful composite indexes for common queries
|
||||
b.HasIndex(x => new { x.PersonId, x.Status });
|
||||
b.HasIndex(x => new { x.EmployerId, x.Status });
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ShareLink>(b =>
|
||||
{
|
||||
b.HasKey(x => x.Id);
|
||||
b.Property(x => x.Code).HasMaxLength(200);
|
||||
b.HasIndex(x => x.Code).IsUnique();
|
||||
b.HasOne(x => x.Attest)
|
||||
.WithMany(x => x.ShareLinks)
|
||||
.HasForeignKey(x => x.AttestId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
b.ToTable(t => t.IsTemporal(tt => tt
|
||||
.UseHistoryTable("ShareLinksHistory")
|
||||
));
|
||||
});
|
||||
|
||||
modelBuilder.Entity<EmployerUser>(b =>
|
||||
{
|
||||
b.HasKey(x => x.Id);
|
||||
b.Property(x => x.ExternalObjectId).HasMaxLength(128);
|
||||
b.Property(x => x.Email).HasMaxLength(256);
|
||||
b.Property(x => x.Name).HasMaxLength(200);
|
||||
b.HasIndex(x => x.ExternalObjectId).IsUnique();
|
||||
b.HasIndex(x => x.EmployerId);
|
||||
b.HasOne(x => x.Employer)
|
||||
.WithMany(e => e.Users)
|
||||
.HasForeignKey(x => x.EmployerId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
b.ToTable(t => t.IsTemporal(tt => tt
|
||||
.UseHistoryTable("EmployerUsersHistory")
|
||||
));
|
||||
});
|
||||
|
||||
modelBuilder.Entity<AuditLog>(b =>
|
||||
{
|
||||
b.HasKey(x => x.Id);
|
||||
b.Property(x => x.Action).HasMaxLength(100);
|
||||
b.Property(x => x.TargetType).HasMaxLength(100);
|
||||
b.Property(x => x.Ip).HasMaxLength(64);
|
||||
b.HasIndex(x => x.Timestamp);
|
||||
});
|
||||
}
|
||||
|
||||
public override int SaveChanges(bool acceptAllChangesOnSuccess)
|
||||
{
|
||||
NormalizeAttestStatuses();
|
||||
return base.SaveChanges(acceptAllChangesOnSuccess);
|
||||
}
|
||||
|
||||
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
|
||||
{
|
||||
NormalizeAttestStatuses();
|
||||
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
|
||||
}
|
||||
|
||||
private void NormalizeAttestStatuses()
|
||||
{
|
||||
foreach (var entry in ChangeTracker.Entries<Attest>())
|
||||
{
|
||||
if (entry.State is not (EntityState.Added or EntityState.Modified)) continue;
|
||||
var a = entry.Entity;
|
||||
|
||||
if (a.EmployerId is null)
|
||||
{
|
||||
// No employer -> always Unverified
|
||||
a.Status = AttestStatus.Unverified;
|
||||
}
|
||||
else if (a.Status == AttestStatus.Unverified)
|
||||
{
|
||||
// Has employer but Unverified -> default to Issued
|
||||
a.Status = AttestStatus.Issued;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user