From 29a4aa22123329d3ae02ce312f943c46035fccd9 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Tue, 30 Jun 2026 16:08:26 +0200 Subject: [PATCH] scaffold: EF design ref + initial migration --- src/InboxIntel.Api/InboxIntel.Api.csproj | 5 + .../20260630140733_InitialCreate.Designer.cs | 733 ++++++++++++++++++ .../20260630140733_InitialCreate.cs | 498 ++++++++++++ .../Migrations/AppDbContextModelSnapshot.cs | 730 +++++++++++++++++ .../AuthEndpointsTests.cs | 6 +- 5 files changed, 1971 insertions(+), 1 deletion(-) create mode 100644 src/InboxIntel.Infrastructure/Migrations/20260630140733_InitialCreate.Designer.cs create mode 100644 src/InboxIntel.Infrastructure/Migrations/20260630140733_InitialCreate.cs create mode 100644 src/InboxIntel.Infrastructure/Migrations/AppDbContextModelSnapshot.cs diff --git a/src/InboxIntel.Api/InboxIntel.Api.csproj b/src/InboxIntel.Api/InboxIntel.Api.csproj index 5e91cd3..a0a8fd5 100644 --- a/src/InboxIntel.Api/InboxIntel.Api.csproj +++ b/src/InboxIntel.Api/InboxIntel.Api.csproj @@ -6,6 +6,11 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/InboxIntel.Infrastructure/Migrations/20260630140733_InitialCreate.Designer.cs b/src/InboxIntel.Infrastructure/Migrations/20260630140733_InitialCreate.Designer.cs new file mode 100644 index 0000000..fa742ee --- /dev/null +++ b/src/InboxIntel.Infrastructure/Migrations/20260630140733_InitialCreate.Designer.cs @@ -0,0 +1,733 @@ +// +using System; +using InboxIntel.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace InboxIntel.Infrastructure.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260630140733_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("InboxIntel.Domain.Entities.AnalyticsAggregate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Day") + .HasColumnType("date"); + + b.Property("HourHistogramJson") + .HasColumnType("text"); + + b.Property("NewsletterCount") + .HasColumnType("integer"); + + b.Property("TotalReceived") + .HasColumnType("integer"); + + b.Property("TotalSizeBytes") + .HasColumnType("bigint"); + + b.Property("TotalUnread") + .HasColumnType("integer"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("WithAttachments") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Day") + .IsUnique(); + + b.ToTable("analytics_aggregates", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Attachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("EmailId") + .HasColumnType("uuid"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("GmailAttachmentId") + .HasColumnType("text"); + + b.Property("MimeType") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("EmailId"); + + b.HasIndex("UserId", "MimeType"); + + b.ToTable("attachments", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Email", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BodyText") + .HasColumnType("text"); + + b.Property("Category") + .HasColumnType("integer"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("GmailMessageId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("HasAttachments") + .HasColumnType("boolean"); + + b.Property("HasListUnsubscribe") + .HasColumnType("boolean"); + + b.Property("IsImportant") + .HasColumnType("boolean"); + + b.Property("IsInInbox") + .HasColumnType("boolean"); + + b.Property("IsStarred") + .HasColumnType("boolean"); + + b.Property("IsTrashed") + .HasColumnType("boolean"); + + b.Property("IsUnread") + .HasColumnType("boolean"); + + b.Property("ListUnsubscribeRaw") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ReceivedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("SearchVector") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasComputedColumnSql("to_tsvector('english', coalesce(\"Subject\",'') || ' ' || coalesce(\"BodyText\",''))", true); + + b.Property("SenderId") + .HasColumnType("uuid"); + + b.Property("SentAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("SizeEstimateBytes") + .HasColumnType("bigint"); + + b.Property("Snippet") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Subject") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("SupportsOneClickUnsubscribe") + .HasColumnType("boolean"); + + b.Property("ThreadId") + .HasColumnType("uuid"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SearchVector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("SearchVector"), "GIN"); + + b.HasIndex("SenderId"); + + b.HasIndex("ThreadId"); + + b.HasIndex("UserId", "Category"); + + b.HasIndex("UserId", "GmailMessageId") + .IsUnique(); + + b.HasIndex("UserId", "IsInInbox"); + + b.HasIndex("UserId", "IsUnread"); + + b.HasIndex("UserId", "SenderId"); + + b.HasIndex("UserId", "SentAtUtc"); + + b.ToTable("emails", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.EmailLabel", b => + { + b.Property("EmailId") + .HasColumnType("uuid"); + + b.Property("LabelId") + .HasColumnType("uuid"); + + b.HasKey("EmailId", "LabelId"); + + b.HasIndex("LabelId"); + + b.ToTable("email_labels", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Label", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ColorHex") + .HasColumnType("text"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("GmailLabelId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "GmailLabelId") + .IsUnique(); + + b.ToTable("labels", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.MailDomain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("EmailCount") + .HasColumnType("integer"); + + b.Property("IsBulkSender") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Name") + .IsUnique(); + + b.ToTable("domains", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.MailThread", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("FirstMessageUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("GmailThreadId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("LastMessageUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("MessageCount") + .HasColumnType("integer"); + + b.Property("Snippet") + .HasColumnType("text"); + + b.Property("Subject") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "GmailThreadId") + .IsUnique(); + + b.ToTable("threads", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Sender", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("DomainId") + .HasColumnType("uuid"); + + b.Property("EmailCount") + .HasColumnType("integer"); + + b.Property("HasUnsubscribe") + .HasColumnType("boolean"); + + b.Property("LastReceivedUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("TotalSizeBytes") + .HasColumnType("bigint"); + + b.Property("UnreadCount") + .HasColumnType("integer"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DomainId"); + + b.HasIndex("UserId", "Address") + .IsUnique(); + + b.HasIndex("UserId", "EmailCount"); + + b.ToTable("senders", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.SyncState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CompletedUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("ConsecutiveFailures") + .HasColumnType("integer"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("LastError") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("LastHistoryId") + .HasColumnType("text"); + + b.Property("LastSuccessfulSyncUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSyncType") + .HasColumnType("integer"); + + b.Property("MessagesProcessed") + .HasColumnType("integer"); + + b.Property("ResumePageToken") + .HasColumnType("text"); + + b.Property("StartedUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TotalMessagesEstimate") + .HasColumnType("integer"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("sync_states", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.UnsubscribeItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("EmailCount") + .HasColumnType("integer"); + + b.Property("LastAttemptUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Method") + .HasColumnType("integer"); + + b.Property("ResultMessage") + .HasColumnType("text"); + + b.Property("SenderId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UnsubscribeTarget") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SenderId"); + + b.HasIndex("UserId", "SenderId") + .IsUnique(); + + b.ToTable("unsubscribe_items", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessTokenExpiresAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)"); + + b.Property("EncryptedRefreshToken") + .HasColumnType("bytea"); + + b.Property("GoogleSubjectId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("LastLoginUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("PictureUrl") + .HasColumnType("text"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("GoogleSubjectId") + .IsUnique(); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.WidgetLayout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("H") + .HasColumnType("integer"); + + b.Property("SettingsJson") + .HasColumnType("text"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Visible") + .HasColumnType("boolean"); + + b.Property("W") + .HasColumnType("integer"); + + b.Property("WidgetKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("X") + .HasColumnType("integer"); + + b.Property("Y") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "WidgetKey") + .IsUnique(); + + b.ToTable("widget_layouts", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Attachment", b => + { + b.HasOne("InboxIntel.Domain.Entities.Email", "Email") + .WithMany("Attachments") + .HasForeignKey("EmailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Email"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Email", b => + { + b.HasOne("InboxIntel.Domain.Entities.Sender", "Sender") + .WithMany("Emails") + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("InboxIntel.Domain.Entities.MailThread", "Thread") + .WithMany("Emails") + .HasForeignKey("ThreadId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("InboxIntel.Domain.Entities.User", null) + .WithMany("Emails") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Sender"); + + b.Navigation("Thread"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.EmailLabel", b => + { + b.HasOne("InboxIntel.Domain.Entities.Email", "Email") + .WithMany("EmailLabels") + .HasForeignKey("EmailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("InboxIntel.Domain.Entities.Label", "Label") + .WithMany("EmailLabels") + .HasForeignKey("LabelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Email"); + + b.Navigation("Label"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Sender", b => + { + b.HasOne("InboxIntel.Domain.Entities.MailDomain", "Domain") + .WithMany("Senders") + .HasForeignKey("DomainId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Domain"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.UnsubscribeItem", b => + { + b.HasOne("InboxIntel.Domain.Entities.Sender", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.WidgetLayout", b => + { + b.HasOne("InboxIntel.Domain.Entities.User", null) + .WithMany("WidgetLayouts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Email", b => + { + b.Navigation("Attachments"); + + b.Navigation("EmailLabels"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Label", b => + { + b.Navigation("EmailLabels"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.MailDomain", b => + { + b.Navigation("Senders"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.MailThread", b => + { + b.Navigation("Emails"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Sender", b => + { + b.Navigation("Emails"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.User", b => + { + b.Navigation("Emails"); + + b.Navigation("WidgetLayouts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/InboxIntel.Infrastructure/Migrations/20260630140733_InitialCreate.cs b/src/InboxIntel.Infrastructure/Migrations/20260630140733_InitialCreate.cs new file mode 100644 index 0000000..0246dcd --- /dev/null +++ b/src/InboxIntel.Infrastructure/Migrations/20260630140733_InitialCreate.cs @@ -0,0 +1,498 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using NpgsqlTypes; + +#nullable disable + +namespace InboxIntel.Infrastructure.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "analytics_aggregates", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + Day = table.Column(type: "date", nullable: false), + TotalReceived = table.Column(type: "integer", nullable: false), + TotalUnread = table.Column(type: "integer", nullable: false), + NewsletterCount = table.Column(type: "integer", nullable: false), + WithAttachments = table.Column(type: "integer", nullable: false), + TotalSizeBytes = table.Column(type: "bigint", nullable: false), + HourHistogramJson = table.Column(type: "text", nullable: true), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_analytics_aggregates", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "domains", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + EmailCount = table.Column(type: "integer", nullable: false), + IsBulkSender = table.Column(type: "boolean", nullable: false), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_domains", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "labels", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + GmailLabelId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Name = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Type = table.Column(type: "text", nullable: false), + ColorHex = table.Column(type: "text", nullable: true), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_labels", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "sync_states", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + Status = table.Column(type: "integer", nullable: false), + LastSyncType = table.Column(type: "integer", nullable: false), + LastHistoryId = table.Column(type: "text", nullable: true), + ResumePageToken = table.Column(type: "text", nullable: true), + TotalMessagesEstimate = table.Column(type: "integer", nullable: false), + MessagesProcessed = table.Column(type: "integer", nullable: false), + StartedUtc = table.Column(type: "timestamp with time zone", nullable: true), + CompletedUtc = table.Column(type: "timestamp with time zone", nullable: true), + LastSuccessfulSyncUtc = table.Column(type: "timestamp with time zone", nullable: true), + ConsecutiveFailures = table.Column(type: "integer", nullable: false), + LastError = table.Column(type: "character varying(4000)", maxLength: 4000, nullable: true), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_sync_states", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "threads", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + GmailThreadId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Subject = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + Snippet = table.Column(type: "text", nullable: true), + MessageCount = table.Column(type: "integer", nullable: false), + FirstMessageUtc = table.Column(type: "timestamp with time zone", nullable: true), + LastMessageUtc = table.Column(type: "timestamp with time zone", nullable: true), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_threads", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "users", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + GoogleSubjectId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + Email = table.Column(type: "character varying(320)", maxLength: 320, nullable: false), + DisplayName = table.Column(type: "text", nullable: true), + PictureUrl = table.Column(type: "text", nullable: true), + EncryptedRefreshToken = table.Column(type: "bytea", nullable: true), + AccessTokenExpiresAtUtc = table.Column(type: "timestamp with time zone", nullable: true), + LastLoginUtc = table.Column(type: "timestamp with time zone", nullable: true), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "senders", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + DomainId = table.Column(type: "uuid", nullable: false), + Address = table.Column(type: "character varying(320)", maxLength: 320, nullable: false), + DisplayName = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), + EmailCount = table.Column(type: "integer", nullable: false), + UnreadCount = table.Column(type: "integer", nullable: false), + TotalSizeBytes = table.Column(type: "bigint", nullable: false), + LastReceivedUtc = table.Column(type: "timestamp with time zone", nullable: true), + HasUnsubscribe = table.Column(type: "boolean", nullable: false), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_senders", x => x.Id); + table.ForeignKey( + name: "FK_senders_domains_DomainId", + column: x => x.DomainId, + principalTable: "domains", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "widget_layouts", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + WidgetKey = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + X = table.Column(type: "integer", nullable: false), + Y = table.Column(type: "integer", nullable: false), + W = table.Column(type: "integer", nullable: false), + H = table.Column(type: "integer", nullable: false), + Visible = table.Column(type: "boolean", nullable: false), + SortOrder = table.Column(type: "integer", nullable: false), + SettingsJson = table.Column(type: "text", nullable: true), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_widget_layouts", x => x.Id); + table.ForeignKey( + name: "FK_widget_layouts_users_UserId", + column: x => x.UserId, + principalTable: "users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "emails", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + GmailMessageId = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + ThreadId = table.Column(type: "uuid", nullable: false), + SenderId = table.Column(type: "uuid", nullable: false), + Subject = table.Column(type: "character varying(1024)", maxLength: 1024, nullable: true), + Snippet = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + BodyText = table.Column(type: "text", nullable: true), + SentAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + ReceivedAtUtc = table.Column(type: "timestamp with time zone", nullable: true), + SizeEstimateBytes = table.Column(type: "bigint", nullable: false), + IsUnread = table.Column(type: "boolean", nullable: false), + IsStarred = table.Column(type: "boolean", nullable: false), + IsImportant = table.Column(type: "boolean", nullable: false), + IsInInbox = table.Column(type: "boolean", nullable: false), + IsTrashed = table.Column(type: "boolean", nullable: false), + HasAttachments = table.Column(type: "boolean", nullable: false), + HasListUnsubscribe = table.Column(type: "boolean", nullable: false), + ListUnsubscribeRaw = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + SupportsOneClickUnsubscribe = table.Column(type: "boolean", nullable: false), + Category = table.Column(type: "integer", nullable: false), + SearchVector = table.Column(type: "tsvector", nullable: true, computedColumnSql: "to_tsvector('english', coalesce(\"Subject\",'') || ' ' || coalesce(\"BodyText\",''))", stored: true), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_emails", x => x.Id); + table.ForeignKey( + name: "FK_emails_senders_SenderId", + column: x => x.SenderId, + principalTable: "senders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_emails_threads_ThreadId", + column: x => x.ThreadId, + principalTable: "threads", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_emails_users_UserId", + column: x => x.UserId, + principalTable: "users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "unsubscribe_items", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + SenderId = table.Column(type: "uuid", nullable: false), + Method = table.Column(type: "integer", nullable: false), + Status = table.Column(type: "integer", nullable: false), + UnsubscribeTarget = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + EmailCount = table.Column(type: "integer", nullable: false), + LastAttemptUtc = table.Column(type: "timestamp with time zone", nullable: true), + ResultMessage = table.Column(type: "text", nullable: true), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_unsubscribe_items", x => x.Id); + table.ForeignKey( + name: "FK_unsubscribe_items_senders_SenderId", + column: x => x.SenderId, + principalTable: "senders", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "attachments", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + EmailId = table.Column(type: "uuid", nullable: false), + GmailAttachmentId = table.Column(type: "text", nullable: true), + FileName = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + MimeType = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), + SizeBytes = table.Column(type: "bigint", nullable: false), + CreatedAtUtc = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAtUtc = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_attachments", x => x.Id); + table.ForeignKey( + name: "FK_attachments_emails_EmailId", + column: x => x.EmailId, + principalTable: "emails", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "email_labels", + columns: table => new + { + EmailId = table.Column(type: "uuid", nullable: false), + LabelId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_email_labels", x => new { x.EmailId, x.LabelId }); + table.ForeignKey( + name: "FK_email_labels_emails_EmailId", + column: x => x.EmailId, + principalTable: "emails", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_email_labels_labels_LabelId", + column: x => x.LabelId, + principalTable: "labels", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_analytics_aggregates_UserId_Day", + table: "analytics_aggregates", + columns: new[] { "UserId", "Day" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_attachments_EmailId", + table: "attachments", + column: "EmailId"); + + migrationBuilder.CreateIndex( + name: "IX_attachments_UserId_MimeType", + table: "attachments", + columns: new[] { "UserId", "MimeType" }); + + migrationBuilder.CreateIndex( + name: "IX_domains_UserId_Name", + table: "domains", + columns: new[] { "UserId", "Name" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_email_labels_LabelId", + table: "email_labels", + column: "LabelId"); + + migrationBuilder.CreateIndex( + name: "IX_emails_SearchVector", + table: "emails", + column: "SearchVector") + .Annotation("Npgsql:IndexMethod", "GIN"); + + migrationBuilder.CreateIndex( + name: "IX_emails_SenderId", + table: "emails", + column: "SenderId"); + + migrationBuilder.CreateIndex( + name: "IX_emails_ThreadId", + table: "emails", + column: "ThreadId"); + + migrationBuilder.CreateIndex( + name: "IX_emails_UserId_Category", + table: "emails", + columns: new[] { "UserId", "Category" }); + + migrationBuilder.CreateIndex( + name: "IX_emails_UserId_GmailMessageId", + table: "emails", + columns: new[] { "UserId", "GmailMessageId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_emails_UserId_IsInInbox", + table: "emails", + columns: new[] { "UserId", "IsInInbox" }); + + migrationBuilder.CreateIndex( + name: "IX_emails_UserId_IsUnread", + table: "emails", + columns: new[] { "UserId", "IsUnread" }); + + migrationBuilder.CreateIndex( + name: "IX_emails_UserId_SenderId", + table: "emails", + columns: new[] { "UserId", "SenderId" }); + + migrationBuilder.CreateIndex( + name: "IX_emails_UserId_SentAtUtc", + table: "emails", + columns: new[] { "UserId", "SentAtUtc" }); + + migrationBuilder.CreateIndex( + name: "IX_labels_UserId_GmailLabelId", + table: "labels", + columns: new[] { "UserId", "GmailLabelId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_senders_DomainId", + table: "senders", + column: "DomainId"); + + migrationBuilder.CreateIndex( + name: "IX_senders_UserId_Address", + table: "senders", + columns: new[] { "UserId", "Address" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_senders_UserId_EmailCount", + table: "senders", + columns: new[] { "UserId", "EmailCount" }); + + migrationBuilder.CreateIndex( + name: "IX_sync_states_UserId", + table: "sync_states", + column: "UserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_threads_UserId_GmailThreadId", + table: "threads", + columns: new[] { "UserId", "GmailThreadId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_unsubscribe_items_SenderId", + table: "unsubscribe_items", + column: "SenderId"); + + migrationBuilder.CreateIndex( + name: "IX_unsubscribe_items_UserId_SenderId", + table: "unsubscribe_items", + columns: new[] { "UserId", "SenderId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_users_Email", + table: "users", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_users_GoogleSubjectId", + table: "users", + column: "GoogleSubjectId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_widget_layouts_UserId_WidgetKey", + table: "widget_layouts", + columns: new[] { "UserId", "WidgetKey" }, + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "analytics_aggregates"); + + migrationBuilder.DropTable( + name: "attachments"); + + migrationBuilder.DropTable( + name: "email_labels"); + + migrationBuilder.DropTable( + name: "sync_states"); + + migrationBuilder.DropTable( + name: "unsubscribe_items"); + + migrationBuilder.DropTable( + name: "widget_layouts"); + + migrationBuilder.DropTable( + name: "emails"); + + migrationBuilder.DropTable( + name: "labels"); + + migrationBuilder.DropTable( + name: "senders"); + + migrationBuilder.DropTable( + name: "threads"); + + migrationBuilder.DropTable( + name: "users"); + + migrationBuilder.DropTable( + name: "domains"); + } + } +} diff --git a/src/InboxIntel.Infrastructure/Migrations/AppDbContextModelSnapshot.cs b/src/InboxIntel.Infrastructure/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 0000000..e295ba7 --- /dev/null +++ b/src/InboxIntel.Infrastructure/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,730 @@ +// +using System; +using InboxIntel.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace InboxIntel.Infrastructure.Migrations +{ + [DbContext(typeof(AppDbContext))] + partial class AppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("InboxIntel.Domain.Entities.AnalyticsAggregate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Day") + .HasColumnType("date"); + + b.Property("HourHistogramJson") + .HasColumnType("text"); + + b.Property("NewsletterCount") + .HasColumnType("integer"); + + b.Property("TotalReceived") + .HasColumnType("integer"); + + b.Property("TotalSizeBytes") + .HasColumnType("bigint"); + + b.Property("TotalUnread") + .HasColumnType("integer"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("WithAttachments") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Day") + .IsUnique(); + + b.ToTable("analytics_aggregates", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Attachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("EmailId") + .HasColumnType("uuid"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("GmailAttachmentId") + .HasColumnType("text"); + + b.Property("MimeType") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("EmailId"); + + b.HasIndex("UserId", "MimeType"); + + b.ToTable("attachments", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Email", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BodyText") + .HasColumnType("text"); + + b.Property("Category") + .HasColumnType("integer"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("GmailMessageId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("HasAttachments") + .HasColumnType("boolean"); + + b.Property("HasListUnsubscribe") + .HasColumnType("boolean"); + + b.Property("IsImportant") + .HasColumnType("boolean"); + + b.Property("IsInInbox") + .HasColumnType("boolean"); + + b.Property("IsStarred") + .HasColumnType("boolean"); + + b.Property("IsTrashed") + .HasColumnType("boolean"); + + b.Property("IsUnread") + .HasColumnType("boolean"); + + b.Property("ListUnsubscribeRaw") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ReceivedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("SearchVector") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasComputedColumnSql("to_tsvector('english', coalesce(\"Subject\",'') || ' ' || coalesce(\"BodyText\",''))", true); + + b.Property("SenderId") + .HasColumnType("uuid"); + + b.Property("SentAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("SizeEstimateBytes") + .HasColumnType("bigint"); + + b.Property("Snippet") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Subject") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("SupportsOneClickUnsubscribe") + .HasColumnType("boolean"); + + b.Property("ThreadId") + .HasColumnType("uuid"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SearchVector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("SearchVector"), "GIN"); + + b.HasIndex("SenderId"); + + b.HasIndex("ThreadId"); + + b.HasIndex("UserId", "Category"); + + b.HasIndex("UserId", "GmailMessageId") + .IsUnique(); + + b.HasIndex("UserId", "IsInInbox"); + + b.HasIndex("UserId", "IsUnread"); + + b.HasIndex("UserId", "SenderId"); + + b.HasIndex("UserId", "SentAtUtc"); + + b.ToTable("emails", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.EmailLabel", b => + { + b.Property("EmailId") + .HasColumnType("uuid"); + + b.Property("LabelId") + .HasColumnType("uuid"); + + b.HasKey("EmailId", "LabelId"); + + b.HasIndex("LabelId"); + + b.ToTable("email_labels", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Label", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ColorHex") + .HasColumnType("text"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("GmailLabelId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "GmailLabelId") + .IsUnique(); + + b.ToTable("labels", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.MailDomain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("EmailCount") + .HasColumnType("integer"); + + b.Property("IsBulkSender") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Name") + .IsUnique(); + + b.ToTable("domains", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.MailThread", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("FirstMessageUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("GmailThreadId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("LastMessageUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("MessageCount") + .HasColumnType("integer"); + + b.Property("Snippet") + .HasColumnType("text"); + + b.Property("Subject") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "GmailThreadId") + .IsUnique(); + + b.ToTable("threads", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Sender", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("DisplayName") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("DomainId") + .HasColumnType("uuid"); + + b.Property("EmailCount") + .HasColumnType("integer"); + + b.Property("HasUnsubscribe") + .HasColumnType("boolean"); + + b.Property("LastReceivedUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("TotalSizeBytes") + .HasColumnType("bigint"); + + b.Property("UnreadCount") + .HasColumnType("integer"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DomainId"); + + b.HasIndex("UserId", "Address") + .IsUnique(); + + b.HasIndex("UserId", "EmailCount"); + + b.ToTable("senders", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.SyncState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CompletedUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("ConsecutiveFailures") + .HasColumnType("integer"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("LastError") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("LastHistoryId") + .HasColumnType("text"); + + b.Property("LastSuccessfulSyncUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSyncType") + .HasColumnType("integer"); + + b.Property("MessagesProcessed") + .HasColumnType("integer"); + + b.Property("ResumePageToken") + .HasColumnType("text"); + + b.Property("StartedUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TotalMessagesEstimate") + .HasColumnType("integer"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("sync_states", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.UnsubscribeItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("EmailCount") + .HasColumnType("integer"); + + b.Property("LastAttemptUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Method") + .HasColumnType("integer"); + + b.Property("ResultMessage") + .HasColumnType("text"); + + b.Property("SenderId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UnsubscribeTarget") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SenderId"); + + b.HasIndex("UserId", "SenderId") + .IsUnique(); + + b.ToTable("unsubscribe_items", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessTokenExpiresAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("character varying(320)"); + + b.Property("EncryptedRefreshToken") + .HasColumnType("bytea"); + + b.Property("GoogleSubjectId") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("LastLoginUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("PictureUrl") + .HasColumnType("text"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("GoogleSubjectId") + .IsUnique(); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.WidgetLayout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("H") + .HasColumnType("integer"); + + b.Property("SettingsJson") + .HasColumnType("text"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Visible") + .HasColumnType("boolean"); + + b.Property("W") + .HasColumnType("integer"); + + b.Property("WidgetKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("X") + .HasColumnType("integer"); + + b.Property("Y") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "WidgetKey") + .IsUnique(); + + b.ToTable("widget_layouts", (string)null); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Attachment", b => + { + b.HasOne("InboxIntel.Domain.Entities.Email", "Email") + .WithMany("Attachments") + .HasForeignKey("EmailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Email"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Email", b => + { + b.HasOne("InboxIntel.Domain.Entities.Sender", "Sender") + .WithMany("Emails") + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("InboxIntel.Domain.Entities.MailThread", "Thread") + .WithMany("Emails") + .HasForeignKey("ThreadId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("InboxIntel.Domain.Entities.User", null) + .WithMany("Emails") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Sender"); + + b.Navigation("Thread"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.EmailLabel", b => + { + b.HasOne("InboxIntel.Domain.Entities.Email", "Email") + .WithMany("EmailLabels") + .HasForeignKey("EmailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("InboxIntel.Domain.Entities.Label", "Label") + .WithMany("EmailLabels") + .HasForeignKey("LabelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Email"); + + b.Navigation("Label"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Sender", b => + { + b.HasOne("InboxIntel.Domain.Entities.MailDomain", "Domain") + .WithMany("Senders") + .HasForeignKey("DomainId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Domain"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.UnsubscribeItem", b => + { + b.HasOne("InboxIntel.Domain.Entities.Sender", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.WidgetLayout", b => + { + b.HasOne("InboxIntel.Domain.Entities.User", null) + .WithMany("WidgetLayouts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Email", b => + { + b.Navigation("Attachments"); + + b.Navigation("EmailLabels"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Label", b => + { + b.Navigation("EmailLabels"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.MailDomain", b => + { + b.Navigation("Senders"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.MailThread", b => + { + b.Navigation("Emails"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.Sender", b => + { + b.Navigation("Emails"); + }); + + modelBuilder.Entity("InboxIntel.Domain.Entities.User", b => + { + b.Navigation("Emails"); + + b.Navigation("WidgetLayouts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/tests/InboxIntel.IntegrationTests/AuthEndpointsTests.cs b/tests/InboxIntel.IntegrationTests/AuthEndpointsTests.cs index 6a2c0fc..fcd59e2 100644 --- a/tests/InboxIntel.IntegrationTests/AuthEndpointsTests.cs +++ b/tests/InboxIntel.IntegrationTests/AuthEndpointsTests.cs @@ -18,7 +18,11 @@ public class TestAppFactory : WebApplicationFactory { builder.ConfigureHostConfiguration(cfg => cfg.AddInMemoryCollection(new Dictionary { - ["Database:AutoMigrate"] = "false" + ["Database:AutoMigrate"] = "false", + // Dummy OAuth creds so the Google challenge produces a real 302 redirect + // (an empty ClientId can make the handler throw instead of redirecting). + ["GoogleOAuth:ClientId"] = "test-client-id", + ["GoogleOAuth:ClientSecret"] = "test-client-secret" })); return base.CreateHost(builder); }