forked from xiongyuxing/tiku-backend.net
feat: add pocketbase import audit persistence
This commit is contained in:
45
Tiku.Domain/Import/PocketBaseImportEntities.cs
Normal file
45
Tiku.Domain/Import/PocketBaseImportEntities.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Text.Json;
|
||||
using Tiku.Domain.Common;
|
||||
|
||||
namespace Tiku.Domain.Import;
|
||||
|
||||
public sealed class PocketBaseImportRun : Entity
|
||||
{
|
||||
public Guid TenantId { get; set; }
|
||||
public string SourceName { get; set; } = string.Empty;
|
||||
public PocketBaseImportSourceKind SourceKind { get; set; } = PocketBaseImportSourceKind.Json;
|
||||
public PocketBaseImportRunStatus Status { get; set; } = PocketBaseImportRunStatus.Running;
|
||||
public JsonElement Stats { get; set; } = JsonDefaults.Object();
|
||||
public DateTimeOffset StartedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
public DateTimeOffset? FinishedAt { get; set; }
|
||||
}
|
||||
|
||||
public sealed class PocketBaseRawRecord
|
||||
{
|
||||
public Guid RunId { get; set; }
|
||||
public Guid TenantId { get; set; }
|
||||
public string CollectionName { get; set; } = string.Empty;
|
||||
public string LegacyId { get; set; } = string.Empty;
|
||||
public JsonElement Record { get; set; } = JsonDefaults.Object();
|
||||
public bool Normalized { get; set; }
|
||||
public JsonElement Errors { get; set; } = JsonDefaults.Array();
|
||||
public DateTimeOffset ImportedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
public sealed class PocketBaseImportIssue : Entity
|
||||
{
|
||||
public Guid? RunId { get; set; }
|
||||
public Guid TenantId { get; set; }
|
||||
public string CollectionName { get; set; } = string.Empty;
|
||||
public string? LegacyId { get; set; }
|
||||
public PocketBaseImportIssueSeverity Severity { get; set; } = PocketBaseImportIssueSeverity.Warning;
|
||||
public string IssueCode { get; set; } = string.Empty;
|
||||
public string Message { get; set; } = string.Empty;
|
||||
public string? FieldPath { get; set; }
|
||||
public string? RawValueSample { get; set; }
|
||||
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
public enum PocketBaseImportSourceKind { Json, Sqlite, Api, Archive }
|
||||
public enum PocketBaseImportRunStatus { Running, Completed, Failed }
|
||||
public enum PocketBaseImportIssueSeverity { Info, Warning, Error, Critical }
|
||||
@@ -0,0 +1,65 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Tiku.Domain.Import;
|
||||
using Tiku.Domain.Tenancy;
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Configurations;
|
||||
|
||||
internal sealed class PocketBaseImportRunConfiguration : IEntityTypeConfiguration<PocketBaseImportRun>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PocketBaseImportRun> builder)
|
||||
{
|
||||
builder.ConfigureEntity("pb_import_runs");
|
||||
builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id });
|
||||
builder.Property(entity => entity.SourceName).HasMaxLength(500);
|
||||
builder.Property(entity => entity.SourceKind).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Stats).IsJson("{}");
|
||||
builder.Property(entity => entity.StartedAt).HasDefaultValueSql("now()");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.StartedAt });
|
||||
builder.HasOne<Tenant>().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PocketBaseRawRecordConfiguration : IEntityTypeConfiguration<PocketBaseRawRecord>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PocketBaseRawRecord> builder)
|
||||
{
|
||||
builder.ToTable("pb_raw_records");
|
||||
builder.HasKey(entity => new { entity.RunId, entity.CollectionName, entity.LegacyId });
|
||||
builder.Property(entity => entity.CollectionName).HasMaxLength(100);
|
||||
builder.Property(entity => entity.LegacyId).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Record).IsJson("{}");
|
||||
builder.Property(entity => entity.Errors).IsJson("[]");
|
||||
builder.Property(entity => entity.ImportedAt).HasDefaultValueSql("now()");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.CollectionName, entity.LegacyId });
|
||||
builder.HasOne<PocketBaseImportRun>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.RunId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<Tenant>().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PocketBaseImportIssueConfiguration : IEntityTypeConfiguration<PocketBaseImportIssue>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PocketBaseImportIssue> builder)
|
||||
{
|
||||
builder.ConfigureEntity("pb_import_issues");
|
||||
builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id });
|
||||
builder.Property(entity => entity.CollectionName).HasMaxLength(100);
|
||||
builder.Property(entity => entity.LegacyId).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Severity).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.IssueCode).HasMaxLength(100);
|
||||
builder.Property(entity => entity.FieldPath).HasMaxLength(500);
|
||||
builder.Property(entity => entity.RawValueSample).HasMaxLength(2000);
|
||||
builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
|
||||
builder.HasIndex(entity => new { entity.RunId, entity.Severity, entity.CollectionName });
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.CollectionName, entity.LegacyId });
|
||||
builder.HasOne<PocketBaseImportRun>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.RunId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<Tenant>().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
15302
Tiku.Infrastructure/Persistence/Migrations/20260725220429_AddPocketBaseImportAuditPersistence.Designer.cs
generated
Normal file
15302
Tiku.Infrastructure/Persistence/Migrations/20260725220429_AddPocketBaseImportAuditPersistence.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPocketBaseImportAuditPersistence : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "pb_import_runs",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
source_name = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
|
||||
source_kind = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
stats = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
started_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
finished_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_pb_import_runs", x => x.id);
|
||||
table.UniqueConstraint("ak_pb_import_runs_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.ForeignKey(
|
||||
name: "fk_pb_import_runs_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "pb_import_issues",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
run_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
collection_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
legacy_id = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||
severity = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
issue_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
message = table.Column<string>(type: "text", nullable: false),
|
||||
field_path = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||
raw_value_sample = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_pb_import_issues", x => x.id);
|
||||
table.UniqueConstraint("ak_pb_import_issues_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.ForeignKey(
|
||||
name: "fk_pb_import_issues_pb_import_runs_tenant_id_run_id",
|
||||
columns: x => new { x.tenant_id, x.run_id },
|
||||
principalTable: "pb_import_runs",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_pb_import_issues_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "pb_raw_records",
|
||||
columns: table => new
|
||||
{
|
||||
run_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
collection_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
legacy_id = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
record = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
normalized = table.Column<bool>(type: "boolean", nullable: false),
|
||||
errors = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'[]'::jsonb"),
|
||||
imported_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_pb_raw_records", x => new { x.run_id, x.collection_name, x.legacy_id });
|
||||
table.ForeignKey(
|
||||
name: "fk_pb_raw_records_pb_import_runs_tenant_id_run_id",
|
||||
columns: x => new { x.tenant_id, x.run_id },
|
||||
principalTable: "pb_import_runs",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_pb_raw_records_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_pb_import_issues_run_id_severity_collection_name",
|
||||
table: "pb_import_issues",
|
||||
columns: new[] { "run_id", "severity", "collection_name" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_pb_import_issues_tenant_id_collection_name_legacy_id",
|
||||
table: "pb_import_issues",
|
||||
columns: new[] { "tenant_id", "collection_name", "legacy_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_pb_import_issues_tenant_id_run_id",
|
||||
table: "pb_import_issues",
|
||||
columns: new[] { "tenant_id", "run_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_pb_import_runs_tenant_id_status_started_at",
|
||||
table: "pb_import_runs",
|
||||
columns: new[] { "tenant_id", "status", "started_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_pb_raw_records_tenant_id_collection_name_legacy_id",
|
||||
table: "pb_raw_records",
|
||||
columns: new[] { "tenant_id", "collection_name", "legacy_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_pb_raw_records_tenant_id_run_id",
|
||||
table: "pb_raw_records",
|
||||
columns: new[] { "tenant_id", "run_id" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "pb_import_issues");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "pb_raw_records");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "pb_import_runs");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7262,6 +7262,196 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("user_identities", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<string>("CollectionName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("collection_name");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("created_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("FieldPath")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("field_path");
|
||||
|
||||
b.Property<string>("IssueCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("issue_code");
|
||||
|
||||
b.Property<string>("LegacyId")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("legacy_id");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("message");
|
||||
|
||||
b.Property<string>("RawValueSample")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("character varying(2000)")
|
||||
.HasColumnName("raw_value_sample");
|
||||
|
||||
b.Property<Guid?>("RunId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("run_id");
|
||||
|
||||
b.Property<string>("Severity")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("severity");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_pb_import_issues");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_pb_import_issues_tenant_id_id");
|
||||
|
||||
b.HasIndex("TenantId", "RunId")
|
||||
.HasDatabaseName("ix_pb_import_issues_tenant_id_run_id");
|
||||
|
||||
b.HasIndex("RunId", "Severity", "CollectionName")
|
||||
.HasDatabaseName("ix_pb_import_issues_run_id_severity_collection_name");
|
||||
|
||||
b.HasIndex("TenantId", "CollectionName", "LegacyId")
|
||||
.HasDatabaseName("ix_pb_import_issues_tenant_id_collection_name_legacy_id");
|
||||
|
||||
b.ToTable("pb_import_issues", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id")
|
||||
.HasDefaultValueSql("gen_random_uuid()");
|
||||
|
||||
b.Property<DateTimeOffset?>("FinishedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("finished_at");
|
||||
|
||||
b.Property<string>("SourceKind")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("source_kind");
|
||||
|
||||
b.Property<string>("SourceName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)")
|
||||
.HasColumnName("source_name");
|
||||
|
||||
b.Property<DateTimeOffset>("StartedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("started_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<JsonElement>("Stats")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("stats")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32)
|
||||
.HasColumnType("character varying(32)")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_pb_import_runs");
|
||||
|
||||
b.HasAlternateKey("TenantId", "Id")
|
||||
.HasName("ak_pb_import_runs_tenant_id_id");
|
||||
|
||||
b.HasIndex("TenantId", "Status", "StartedAt")
|
||||
.HasDatabaseName("ix_pb_import_runs_tenant_id_status_started_at");
|
||||
|
||||
b.ToTable("pb_import_runs", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b =>
|
||||
{
|
||||
b.Property<Guid>("RunId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("run_id");
|
||||
|
||||
b.Property<string>("CollectionName")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("collection_name");
|
||||
|
||||
b.Property<string>("LegacyId")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("legacy_id");
|
||||
|
||||
b.Property<JsonElement>("Errors")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("errors")
|
||||
.HasDefaultValueSql("'[]'::jsonb");
|
||||
|
||||
b.Property<DateTimeOffset>("ImportedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("imported_at")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<bool>("Normalized")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("normalized");
|
||||
|
||||
b.Property<JsonElement>("Record")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("record")
|
||||
.HasDefaultValueSql("'{}'::jsonb");
|
||||
|
||||
b.Property<Guid>("TenantId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.HasKey("RunId", "CollectionName", "LegacyId")
|
||||
.HasName("pk_pb_raw_records");
|
||||
|
||||
b.HasIndex("TenantId", "RunId")
|
||||
.HasDatabaseName("ix_pb_raw_records_tenant_id_run_id");
|
||||
|
||||
b.HasIndex("TenantId", "CollectionName", "LegacyId")
|
||||
.HasDatabaseName("ix_pb_raw_records_tenant_id_collection_name_legacy_id");
|
||||
|
||||
b.ToTable("pb_raw_records", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -13955,6 +14145,51 @@ namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
.HasConstraintName("fk_user_identities_users_user_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportIssue", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_pb_import_issues_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "RunId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.HasConstraintName("fk_pb_import_issues_pb_import_runs_tenant_id_run_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Import.PocketBaseImportRun", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_pb_import_runs_tenants_tenant_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Import.PocketBaseRawRecord", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_pb_raw_records_tenants_tenant_id");
|
||||
|
||||
b.HasOne("Tiku.Domain.Import.PocketBaseImportRun", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId", "RunId")
|
||||
.HasPrincipalKey("TenantId", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_pb_raw_records_pb_import_runs_tenant_id_run_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tiku.Domain.Learning.AnswerRecord", b =>
|
||||
{
|
||||
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
|
||||
|
||||
@@ -5,6 +5,7 @@ using Tiku.Domain.Common;
|
||||
using Tiku.Domain.Content;
|
||||
using Tiku.Domain.Growth;
|
||||
using Tiku.Domain.Identity;
|
||||
using Tiku.Domain.Import;
|
||||
using Tiku.Domain.Learning;
|
||||
using Tiku.Domain.Operations;
|
||||
using Tiku.Domain.Platform;
|
||||
@@ -136,6 +137,9 @@ public sealed class TikuDbContext(DbContextOptions<TikuDbContext> options) : DbC
|
||||
public DbSet<PlatformAuditAlert> PlatformAuditAlerts => Set<PlatformAuditAlert>();
|
||||
public DbSet<PlatformDunningNotificationChannel> PlatformDunningNotificationChannels => Set<PlatformDunningNotificationChannel>();
|
||||
public DbSet<PlatformDunningNotificationEvent> PlatformDunningNotificationEvents => Set<PlatformDunningNotificationEvent>();
|
||||
public DbSet<PocketBaseImportRun> PocketBaseImportRuns => Set<PocketBaseImportRun>();
|
||||
public DbSet<PocketBaseRawRecord> PocketBaseRawRecords => Set<PocketBaseRawRecord>();
|
||||
public DbSet<PocketBaseImportIssue> PocketBaseImportIssues => Set<PocketBaseImportIssue>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Tiku.Domain.Commerce;
|
||||
using Tiku.Domain.Content;
|
||||
using Tiku.Domain.Growth;
|
||||
using Tiku.Domain.Import;
|
||||
using Tiku.Domain.Learning;
|
||||
using Tiku.Domain.Operations;
|
||||
using Tiku.Domain.Platform;
|
||||
@@ -29,7 +30,7 @@ public sealed class PersistenceModelTests
|
||||
.Select(entity => entity.GetTableName())
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
Assert.Equal(121, tableNames.Count);
|
||||
Assert.Equal(124, tableNames.Count);
|
||||
Assert.Contains("tenants", tableNames);
|
||||
Assert.Contains("tenant_settings", tableNames);
|
||||
Assert.Contains("content_entries", tableNames);
|
||||
@@ -129,6 +130,9 @@ public sealed class PersistenceModelTests
|
||||
Assert.Contains("platform_audit_alerts", tableNames);
|
||||
Assert.Contains("platform_dunning_notification_channels", tableNames);
|
||||
Assert.Contains("platform_dunning_notification_events", tableNames);
|
||||
Assert.Contains("pb_import_runs", tableNames);
|
||||
Assert.Contains("pb_raw_records", tableNames);
|
||||
Assert.Contains("pb_import_issues", tableNames);
|
||||
Assert.Contains("questions", tableNames);
|
||||
Assert.Contains("question_versions", tableNames);
|
||||
Assert.Contains("answer_records", tableNames);
|
||||
@@ -807,4 +811,43 @@ public sealed class PersistenceModelTests
|
||||
.SequenceEqual(propertyNames));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(PocketBaseImportRun), nameof(PocketBaseImportRun.Stats), "'{}'::jsonb")]
|
||||
[InlineData(typeof(PocketBaseRawRecord), nameof(PocketBaseRawRecord.Record), "'{}'::jsonb")]
|
||||
[InlineData(typeof(PocketBaseRawRecord), nameof(PocketBaseRawRecord.Errors), "'[]'::jsonb")]
|
||||
public void PocketBase_import_json_properties_are_mapped_to_jsonb(
|
||||
Type entityType,
|
||||
string propertyName,
|
||||
string expectedDefaultValueSql)
|
||||
{
|
||||
using var context = new TikuDbContext(Options);
|
||||
|
||||
var property = context.Model.FindEntityType(entityType)!
|
||||
.FindProperty(propertyName)!;
|
||||
|
||||
Assert.Equal("jsonb", property.GetColumnType());
|
||||
Assert.Equal(expectedDefaultValueSql, property.GetDefaultValueSql());
|
||||
Assert.Equal(typeof(System.Text.Json.JsonElement), property.ClrType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PocketBase_import_raw_records_keep_legacy_identity_in_primary_key()
|
||||
{
|
||||
using var context = new TikuDbContext(Options);
|
||||
|
||||
var keyProperties = context.Model.FindEntityType(typeof(PocketBaseRawRecord))!
|
||||
.FindPrimaryKey()!
|
||||
.Properties
|
||||
.Select(property => property.Name)
|
||||
.ToArray();
|
||||
|
||||
Assert.Equal(
|
||||
[
|
||||
nameof(PocketBaseRawRecord.RunId),
|
||||
nameof(PocketBaseRawRecord.CollectionName),
|
||||
nameof(PocketBaseRawRecord.LegacyId)
|
||||
],
|
||||
keyProperties);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user