From 49e2ca1774b5a6f9a6faaae0bcdbb348e233c58a Mon Sep 17 00:00:00 2001 From: Jonathan Bourdon Date: Thu, 7 May 2026 15:48:12 -0400 Subject: [PATCH] fix(backend): add missing domain foreign keys --- ...52_AddMissingDomainForeignKeys.Designer.cs | 2173 +++++++++++++++++ ...60507185052_AddMissingDomainForeignKeys.cs | 405 +++ .../Migrations/AppDbContextModelSnapshot.cs | 227 ++ .../Data/ApprovalModelConfiguration.cs | 30 + .../Assets/Data/AssetModelConfiguration.cs | 14 + .../Data/CampaignModelConfiguration.cs | 10 + .../Data/ChannelModelConfiguration.cs | 5 + .../Clients/Data/ClientModelConfiguration.cs | 5 + .../Data/CommentModelConfiguration.cs | 14 + .../Data/ContentItemModelConfiguration.cs | 27 + .../Data/FeedbackModelConfiguration.cs | 20 + .../Data/NotificationModelConfiguration.cs | 10 + .../Data/WorkspaceModelConfiguration.cs | 4 + scripts/generate-db-diagram.sh | 191 +- 14 files changed, 3109 insertions(+), 26 deletions(-) create mode 100644 backend/src/Socialize.Api/Migrations/20260507185052_AddMissingDomainForeignKeys.Designer.cs create mode 100644 backend/src/Socialize.Api/Migrations/20260507185052_AddMissingDomainForeignKeys.cs diff --git a/backend/src/Socialize.Api/Migrations/20260507185052_AddMissingDomainForeignKeys.Designer.cs b/backend/src/Socialize.Api/Migrations/20260507185052_AddMissingDomainForeignKeys.Designer.cs new file mode 100644 index 00000000..4a61e777 --- /dev/null +++ b/backend/src/Socialize.Api/Migrations/20260507185052_AddMissingDomainForeignKeys.Designer.cs @@ -0,0 +1,2173 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Socialize.Api.Data; + +#nullable disable + +namespace Socialize.Api.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260507185052_AddMissingDomainForeignKeys")] + partial class AddMissingDomainForeignKeys + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.ApprovalDecision", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApprovalRequestId") + .HasColumnType("uuid"); + + b.Property("Comment") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("DecidedByEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("DecidedByName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("DecidedByUserId") + .HasColumnType("uuid"); + + b.Property("Decision") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("ApprovalRequestId"); + + b.ToTable("ApprovalDecisions", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.ApprovalRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessToken") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ContentItemId") + .HasColumnType("uuid"); + + b.Property("DueAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RequestedByUserId") + .HasColumnType("uuid"); + + b.Property("ReviewerEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ReviewerName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SentAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Stage") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("WorkflowInstanceId") + .HasColumnType("uuid"); + + b.Property("WorkflowStepRequiredApproverCount") + .HasColumnType("integer"); + + b.Property("WorkflowStepSortOrder") + .HasColumnType("integer"); + + b.Property("WorkflowStepTargetType") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("WorkflowStepTargetValue") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ContentItemId"); + + b.HasIndex("ReviewerEmail"); + + b.HasIndex("WorkflowInstanceId"); + + b.HasIndex("WorkspaceId"); + + b.ToTable("ApprovalRequests", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.ApprovalWorkflowInstance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApprovalMode") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ContentItemId") + .HasColumnType("uuid"); + + b.Property("StartedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("State") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ContentItemId"); + + b.HasIndex("WorkspaceId"); + + b.HasIndex("ContentItemId", "State") + .IsUnique() + .HasFilter("\"State\" = 'Pending'"); + + b.ToTable("ApprovalWorkflowInstances", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.WorkspaceApprovalStepConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("RequiredApproverCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("TargetType") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("TargetValue") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("WorkspaceId"); + + b.HasIndex("WorkspaceId", "SortOrder") + .IsUnique(); + + b.ToTable("WorkspaceApprovalStepConfigurations", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Assets.Data.Asset", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AssetType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ContentItemId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CurrentRevisionNumber") + .HasColumnType("integer"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GoogleDriveFileId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GoogleDriveLink") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("PreviewUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ContentItemId"); + + b.HasIndex("WorkspaceId"); + + b.ToTable("Assets", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Assets.Data.AssetRevision", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AssetId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CreatedByUserId") + .HasColumnType("uuid"); + + b.Property("Notes") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("PreviewUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("RevisionNumber") + .HasColumnType("integer"); + + b.Property("SourceReference") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.HasKey("Id"); + + b.HasIndex("AssetId"); + + b.HasIndex("AssetId", "RevisionNumber") + .IsUnique(); + + b.ToTable("AssetRevisions", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.CalendarIntegrations.Data.CalendarCatalogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Country") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CultureOrReligion") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("DefaultColor") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Region") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("SourceUrl") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TrustLevel") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("Category"); + + b.HasIndex("Country"); + + b.HasIndex("ProviderName"); + + b.ToTable("CalendarCatalogEntries", (string)null); + + b.HasData( + new + { + Id = new Guid("10000000-0000-0000-0000-000000000001"), + Category = "public-holiday", + Country = "US", + CreatedAt = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), + DefaultColor = "#2F80ED", + Description = "Federal public holiday calendar for the United States.", + Language = "en", + ProviderName = "Nager.Date", + SourceUrl = "https://date.nager.at/api/v3/PublicHolidays/2026/US", + Title = "United States Public Holidays", + TrustLevel = "Verified" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000002"), + Category = "public-holiday", + Country = "CA", + CreatedAt = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), + DefaultColor = "#2F80ED", + Description = "Public holiday calendar for Canada.", + Language = "en", + ProviderName = "Nager.Date", + SourceUrl = "https://date.nager.at/api/v3/PublicHolidays/2026/CA", + Title = "Canada Public Holidays", + TrustLevel = "Verified" + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000003"), + Category = "marketing-moment", + CreatedAt = new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), + DefaultColor = "#9B51E0", + Description = "Common retail, awareness, and social planning moments.", + Language = "en", + ProviderName = "Socialize", + SourceUrl = "https://example.com/socialize/marketing-moments.ics", + Title = "Common Marketing Moments", + TrustLevel = "Maintained" + }); + }); + + modelBuilder.Entity("Socialize.Api.Modules.CalendarIntegrations.Data.CalendarEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CalendarSourceId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("EndLocalDateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("EndUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("ImportedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsAllDay") + .HasColumnType("boolean"); + + b.Property("IsFloatingTime") + .HasColumnType("boolean"); + + b.Property("Location") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("RecurrenceId") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("SourceEventUid") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("SourceLastModifiedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SourceUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("StartLocalDateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("StartUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeZoneId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.HasKey("Id"); + + b.HasIndex("CalendarSourceId"); + + b.HasIndex("CalendarSourceId", "SourceEventUid", "StartDate") + .IsUnique(); + + b.ToTable("CalendarEvents", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.CalendarIntegrations.Data.CalendarSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CatalogSourceReference") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Color") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("DisplayTitle") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("InheritanceMode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LastAttemptedSyncAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSuccessfulSyncAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSyncError") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("SourceUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("Scope"); + + b.HasIndex("UserId"); + + b.HasIndex("WorkspaceId"); + + b.ToTable("CalendarSources", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.CalendarIntegrations.Data.UserCalendarExportFeed", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Token") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("TokenHash") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("UserCalendarExportFeeds", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Campaigns.Data.Campaign", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Description") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Notes") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.HasIndex("WorkspaceId"); + + b.HasIndex("ClientId", "Name") + .IsUnique(); + + b.ToTable("Campaigns", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Channels.Data.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("ExternalUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Handle") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Network") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("WorkspaceId"); + + b.HasIndex("WorkspaceId", "Network", "Name") + .IsUnique(); + + b.ToTable("Channels", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Clients.Data.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PortraitUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("PrimaryContactEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PrimaryContactName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PrimaryContactPortraitUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("WorkspaceId"); + + b.HasIndex("WorkspaceId", "Name") + .IsUnique(); + + b.ToTable("Clients", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Comments.Data.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttachmentBlobContainerName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("AttachmentBlobName") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("AttachmentBlobUrl") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("AttachmentContentType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("AttachmentFileName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("AttachmentSizeBytes") + .HasColumnType("bigint"); + + b.Property("AuthorDisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("AuthorEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("AuthorUserId") + .HasColumnType("uuid"); + + b.Property("Body") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ContentItemId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("ParentCommentId") + .HasColumnType("uuid"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ContentItemId"); + + b.HasIndex("ParentCommentId"); + + b.HasIndex("WorkspaceId"); + + b.ToTable("Comments", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.ContentItems.Data.ContentItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CampaignId") + .HasColumnType("uuid"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CurrentRevisionLabel") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("CurrentRevisionNumber") + .HasColumnType("integer"); + + b.Property("DueDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Hashtags") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("PublicationMessage") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("PublicationTargets") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CampaignId"); + + b.HasIndex("ClientId"); + + b.HasIndex("WorkspaceId"); + + b.ToTable("ContentItems", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.ContentItems.Data.ContentItemActivityEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ActorEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ActorUserId") + .HasColumnType("uuid"); + + b.Property("ContentItemId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("MetadataJson") + .HasColumnType("jsonb"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ContentItemId"); + + b.HasIndex("WorkspaceId"); + + b.HasIndex("ContentItemId", "CreatedAt"); + + b.ToTable("ContentItemActivityEntries", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.ContentItems.Data.ContentItemRevision", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChangeSummary") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ContentItemId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CreatedByUserId") + .HasColumnType("uuid"); + + b.Property("Hashtags") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("PublicationMessage") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("PublicationTargets") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("RevisionLabel") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("RevisionNumber") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ContentItemId"); + + b.HasIndex("ContentItemId", "RevisionNumber") + .IsUnique(); + + b.ToTable("ContentItemRevisions", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackActivityEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ActivityType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ActorDisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ActorEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ActorUserId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("FeedbackReportId") + .HasColumnType("uuid"); + + b.Property("FromValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Note") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ToValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.HasKey("Id"); + + b.HasIndex("ActorUserId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("FeedbackReportId"); + + b.ToTable("FeedbackActivityEntries", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackComment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AuthorDisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("AuthorEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("AuthorRole") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("AuthorUserId") + .HasColumnType("uuid"); + + b.Property("Body") + .IsRequired() + .HasMaxLength(8000) + .HasColumnType("character varying(8000)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("FeedbackReportId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("AuthorUserId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("FeedbackReportId"); + + b.ToTable("FeedbackComments", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppVersion") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("BrowserUserAgent") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("CampaignId") + .HasColumnType("uuid"); + + b.Property("CampaignName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CancellationReason") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("CancelledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CancelledByUserId") + .HasColumnType("uuid"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("ClientName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ContentItemId") + .HasColumnType("uuid"); + + b.Property("ContentItemTitle") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(8000) + .HasColumnType("character varying(8000)"); + + b.Property("LastActivityAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReporterDisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ReporterEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ReporterUserId") + .HasColumnType("uuid"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("SubmittedPath") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("ViewportHeight") + .HasColumnType("integer"); + + b.Property("ViewportWidth") + .HasColumnType("integer"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.Property("WorkspaceName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("CampaignId"); + + b.HasIndex("ClientId"); + + b.HasIndex("ContentItemId"); + + b.HasIndex("LastActivityAt"); + + b.HasIndex("ReporterUserId"); + + b.HasIndex("Status"); + + b.HasIndex("Type"); + + b.HasIndex("WorkspaceId"); + + b.ToTable("FeedbackReports", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackScreenshot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BlobContainerName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("BlobName") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("FeedbackReportId") + .HasColumnType("uuid"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("FeedbackReportId") + .IsUnique(); + + b.ToTable("FeedbackScreenshots", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FeedbackReportId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.HasIndex("FeedbackReportId", "NormalizedName") + .IsUnique(); + + b.ToTable("FeedbackTags", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Identity.Data.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Identity.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("Address") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Alias") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BirthDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FacebookId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Firstname") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GoogleId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Lastname") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("PortraitUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("RefreshToken") + .HasMaxLength(44) + .HasColumnType("character varying(44)"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Notifications.Data.NotificationEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ContentItemId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("EntityId") + .HasColumnType("uuid"); + + b.Property("EntityType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("MetadataJson") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ReadAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RecipientEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RecipientUserId") + .HasColumnType("uuid"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ContentItemId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("RecipientUserId"); + + b.HasIndex("WorkspaceId"); + + b.ToTable("NotificationEvents", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Organizations.Data.Organization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("LogoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("OwnerUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OwnerUserId"); + + b.ToTable("Organizations", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Organizations.Data.OrganizationMembership", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "UserId") + .IsUnique(); + + b.ToTable("OrganizationMemberships", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Workspaces.Data.Workspace", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApprovalMode") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(32) + .HasColumnType("character varying(32)") + .HasDefaultValue("Required"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("LockContentAfterApproval") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("LogoUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OwnerUserId") + .HasColumnType("uuid"); + + b.Property("SchedulePostsAutomaticallyOnApproval") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("SendAutomaticApprovalReminders") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("TimeZone") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("OwnerUserId"); + + b.ToTable("Workspaces", (string)null); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Workspaces.Data.WorkspaceInvite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("InvitedByUserId") + .HasColumnType("uuid"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("WorkspaceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("WorkspaceId"); + + b.HasIndex("WorkspaceId", "Email", "Status"); + + b.ToTable("WorkspaceInvites", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Socialize.Api.Modules.Identity.Data.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Socialize.Api.Modules.Identity.Data.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Socialize.Api.Modules.Identity.Data.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Socialize.Api.Modules.Identity.Data.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Identity.Data.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Socialize.Api.Modules.Identity.Data.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.ApprovalDecision", b => + { + b.HasOne("Socialize.Api.Modules.Approvals.Data.ApprovalRequest", null) + .WithMany() + .HasForeignKey("ApprovalRequestId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.ApprovalRequest", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Approvals.Data.ApprovalWorkflowInstance", null) + .WithMany() + .HasForeignKey("WorkflowInstanceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.ApprovalWorkflowInstance", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.WorkspaceApprovalStepConfiguration", b => + { + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Assets.Data.Asset", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Assets.Data.AssetRevision", b => + { + b.HasOne("Socialize.Api.Modules.Assets.Data.Asset", null) + .WithMany() + .HasForeignKey("AssetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.CalendarIntegrations.Data.CalendarEvent", b => + { + b.HasOne("Socialize.Api.Modules.CalendarIntegrations.Data.CalendarSource", null) + .WithMany() + .HasForeignKey("CalendarSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Campaigns.Data.Campaign", b => + { + b.HasOne("Socialize.Api.Modules.Clients.Data.Client", null) + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Channels.Data.Channel", b => + { + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Clients.Data.Client", b => + { + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Comments.Data.Comment", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Comments.Data.Comment", null) + .WithMany() + .HasForeignKey("ParentCommentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.ContentItems.Data.ContentItem", b => + { + b.HasOne("Socialize.Api.Modules.Campaigns.Data.Campaign", null) + .WithMany() + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Clients.Data.Client", null) + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.ContentItems.Data.ContentItemActivityEntry", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.ContentItems.Data.ContentItemRevision", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackActivityEntry", b => + { + b.HasOne("Socialize.Api.Modules.Feedback.Data.FeedbackReport", "FeedbackReport") + .WithMany("ActivityEntries") + .HasForeignKey("FeedbackReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FeedbackReport"); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackComment", b => + { + b.HasOne("Socialize.Api.Modules.Feedback.Data.FeedbackReport", "FeedbackReport") + .WithMany("Comments") + .HasForeignKey("FeedbackReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FeedbackReport"); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackReport", b => + { + b.HasOne("Socialize.Api.Modules.Campaigns.Data.Campaign", null) + .WithMany() + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Socialize.Api.Modules.Clients.Data.Client", null) + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackScreenshot", b => + { + b.HasOne("Socialize.Api.Modules.Feedback.Data.FeedbackReport", "FeedbackReport") + .WithOne("Screenshot") + .HasForeignKey("Socialize.Api.Modules.Feedback.Data.FeedbackScreenshot", "FeedbackReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FeedbackReport"); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackTag", b => + { + b.HasOne("Socialize.Api.Modules.Feedback.Data.FeedbackReport", "FeedbackReport") + .WithMany("Tags") + .HasForeignKey("FeedbackReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FeedbackReport"); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Notifications.Data.NotificationEvent", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Organizations.Data.OrganizationMembership", b => + { + b.HasOne("Socialize.Api.Modules.Organizations.Data.Organization", null) + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Workspaces.Data.Workspace", b => + { + b.HasOne("Socialize.Api.Modules.Organizations.Data.Organization", null) + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Workspaces.Data.WorkspaceInvite", b => + { + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackReport", b => + { + b.Navigation("ActivityEntries"); + + b.Navigation("Comments"); + + b.Navigation("Screenshot"); + + b.Navigation("Tags"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Socialize.Api/Migrations/20260507185052_AddMissingDomainForeignKeys.cs b/backend/src/Socialize.Api/Migrations/20260507185052_AddMissingDomainForeignKeys.cs new file mode 100644 index 00000000..2c78d000 --- /dev/null +++ b/backend/src/Socialize.Api/Migrations/20260507185052_AddMissingDomainForeignKeys.cs @@ -0,0 +1,405 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Socialize.Api.Migrations +{ + /// + internal partial class AddMissingDomainForeignKeys : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_FeedbackReports_CampaignId", + table: "FeedbackReports", + column: "CampaignId"); + + migrationBuilder.CreateIndex( + name: "IX_FeedbackReports_ClientId", + table: "FeedbackReports", + column: "ClientId"); + + migrationBuilder.CreateIndex( + name: "IX_FeedbackReports_ContentItemId", + table: "FeedbackReports", + column: "ContentItemId"); + + migrationBuilder.AddForeignKey( + name: "FK_ApprovalDecisions_ApprovalRequests_ApprovalRequestId", + table: "ApprovalDecisions", + column: "ApprovalRequestId", + principalTable: "ApprovalRequests", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ApprovalRequests_ApprovalWorkflowInstances_WorkflowInstance~", + table: "ApprovalRequests", + column: "WorkflowInstanceId", + principalTable: "ApprovalWorkflowInstances", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ApprovalRequests_ContentItems_ContentItemId", + table: "ApprovalRequests", + column: "ContentItemId", + principalTable: "ContentItems", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ApprovalRequests_Workspaces_WorkspaceId", + table: "ApprovalRequests", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ApprovalWorkflowInstances_ContentItems_ContentItemId", + table: "ApprovalWorkflowInstances", + column: "ContentItemId", + principalTable: "ContentItems", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ApprovalWorkflowInstances_Workspaces_WorkspaceId", + table: "ApprovalWorkflowInstances", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_AssetRevisions_Assets_AssetId", + table: "AssetRevisions", + column: "AssetId", + principalTable: "Assets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Assets_ContentItems_ContentItemId", + table: "Assets", + column: "ContentItemId", + principalTable: "ContentItems", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Assets_Workspaces_WorkspaceId", + table: "Assets", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Campaigns_Clients_ClientId", + table: "Campaigns", + column: "ClientId", + principalTable: "Clients", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Campaigns_Workspaces_WorkspaceId", + table: "Campaigns", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Channels_Workspaces_WorkspaceId", + table: "Channels", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Clients_Workspaces_WorkspaceId", + table: "Clients", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Comments_Comments_ParentCommentId", + table: "Comments", + column: "ParentCommentId", + principalTable: "Comments", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Comments_ContentItems_ContentItemId", + table: "Comments", + column: "ContentItemId", + principalTable: "ContentItems", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Comments_Workspaces_WorkspaceId", + table: "Comments", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ContentItemActivityEntries_ContentItems_ContentItemId", + table: "ContentItemActivityEntries", + column: "ContentItemId", + principalTable: "ContentItems", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ContentItemActivityEntries_Workspaces_WorkspaceId", + table: "ContentItemActivityEntries", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ContentItemRevisions_ContentItems_ContentItemId", + table: "ContentItemRevisions", + column: "ContentItemId", + principalTable: "ContentItems", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_ContentItems_Campaigns_CampaignId", + table: "ContentItems", + column: "CampaignId", + principalTable: "Campaigns", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ContentItems_Clients_ClientId", + table: "ContentItems", + column: "ClientId", + principalTable: "Clients", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ContentItems_Workspaces_WorkspaceId", + table: "ContentItems", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_FeedbackReports_Campaigns_CampaignId", + table: "FeedbackReports", + column: "CampaignId", + principalTable: "Campaigns", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + + migrationBuilder.AddForeignKey( + name: "FK_FeedbackReports_Clients_ClientId", + table: "FeedbackReports", + column: "ClientId", + principalTable: "Clients", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + + migrationBuilder.AddForeignKey( + name: "FK_FeedbackReports_ContentItems_ContentItemId", + table: "FeedbackReports", + column: "ContentItemId", + principalTable: "ContentItems", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + + migrationBuilder.AddForeignKey( + name: "FK_FeedbackReports_Workspaces_WorkspaceId", + table: "FeedbackReports", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + + migrationBuilder.AddForeignKey( + name: "FK_NotificationEvents_ContentItems_ContentItemId", + table: "NotificationEvents", + column: "ContentItemId", + principalTable: "ContentItems", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + + migrationBuilder.AddForeignKey( + name: "FK_NotificationEvents_Workspaces_WorkspaceId", + table: "NotificationEvents", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_WorkspaceApprovalStepConfigurations_Workspaces_WorkspaceId", + table: "WorkspaceApprovalStepConfigurations", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_WorkspaceInvites_Workspaces_WorkspaceId", + table: "WorkspaceInvites", + column: "WorkspaceId", + principalTable: "Workspaces", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ApprovalDecisions_ApprovalRequests_ApprovalRequestId", + table: "ApprovalDecisions"); + + migrationBuilder.DropForeignKey( + name: "FK_ApprovalRequests_ApprovalWorkflowInstances_WorkflowInstance~", + table: "ApprovalRequests"); + + migrationBuilder.DropForeignKey( + name: "FK_ApprovalRequests_ContentItems_ContentItemId", + table: "ApprovalRequests"); + + migrationBuilder.DropForeignKey( + name: "FK_ApprovalRequests_Workspaces_WorkspaceId", + table: "ApprovalRequests"); + + migrationBuilder.DropForeignKey( + name: "FK_ApprovalWorkflowInstances_ContentItems_ContentItemId", + table: "ApprovalWorkflowInstances"); + + migrationBuilder.DropForeignKey( + name: "FK_ApprovalWorkflowInstances_Workspaces_WorkspaceId", + table: "ApprovalWorkflowInstances"); + + migrationBuilder.DropForeignKey( + name: "FK_AssetRevisions_Assets_AssetId", + table: "AssetRevisions"); + + migrationBuilder.DropForeignKey( + name: "FK_Assets_ContentItems_ContentItemId", + table: "Assets"); + + migrationBuilder.DropForeignKey( + name: "FK_Assets_Workspaces_WorkspaceId", + table: "Assets"); + + migrationBuilder.DropForeignKey( + name: "FK_Campaigns_Clients_ClientId", + table: "Campaigns"); + + migrationBuilder.DropForeignKey( + name: "FK_Campaigns_Workspaces_WorkspaceId", + table: "Campaigns"); + + migrationBuilder.DropForeignKey( + name: "FK_Channels_Workspaces_WorkspaceId", + table: "Channels"); + + migrationBuilder.DropForeignKey( + name: "FK_Clients_Workspaces_WorkspaceId", + table: "Clients"); + + migrationBuilder.DropForeignKey( + name: "FK_Comments_Comments_ParentCommentId", + table: "Comments"); + + migrationBuilder.DropForeignKey( + name: "FK_Comments_ContentItems_ContentItemId", + table: "Comments"); + + migrationBuilder.DropForeignKey( + name: "FK_Comments_Workspaces_WorkspaceId", + table: "Comments"); + + migrationBuilder.DropForeignKey( + name: "FK_ContentItemActivityEntries_ContentItems_ContentItemId", + table: "ContentItemActivityEntries"); + + migrationBuilder.DropForeignKey( + name: "FK_ContentItemActivityEntries_Workspaces_WorkspaceId", + table: "ContentItemActivityEntries"); + + migrationBuilder.DropForeignKey( + name: "FK_ContentItemRevisions_ContentItems_ContentItemId", + table: "ContentItemRevisions"); + + migrationBuilder.DropForeignKey( + name: "FK_ContentItems_Campaigns_CampaignId", + table: "ContentItems"); + + migrationBuilder.DropForeignKey( + name: "FK_ContentItems_Clients_ClientId", + table: "ContentItems"); + + migrationBuilder.DropForeignKey( + name: "FK_ContentItems_Workspaces_WorkspaceId", + table: "ContentItems"); + + migrationBuilder.DropForeignKey( + name: "FK_FeedbackReports_Campaigns_CampaignId", + table: "FeedbackReports"); + + migrationBuilder.DropForeignKey( + name: "FK_FeedbackReports_Clients_ClientId", + table: "FeedbackReports"); + + migrationBuilder.DropForeignKey( + name: "FK_FeedbackReports_ContentItems_ContentItemId", + table: "FeedbackReports"); + + migrationBuilder.DropForeignKey( + name: "FK_FeedbackReports_Workspaces_WorkspaceId", + table: "FeedbackReports"); + + migrationBuilder.DropForeignKey( + name: "FK_NotificationEvents_ContentItems_ContentItemId", + table: "NotificationEvents"); + + migrationBuilder.DropForeignKey( + name: "FK_NotificationEvents_Workspaces_WorkspaceId", + table: "NotificationEvents"); + + migrationBuilder.DropForeignKey( + name: "FK_WorkspaceApprovalStepConfigurations_Workspaces_WorkspaceId", + table: "WorkspaceApprovalStepConfigurations"); + + migrationBuilder.DropForeignKey( + name: "FK_WorkspaceInvites_Workspaces_WorkspaceId", + table: "WorkspaceInvites"); + + migrationBuilder.DropIndex( + name: "IX_FeedbackReports_CampaignId", + table: "FeedbackReports"); + + migrationBuilder.DropIndex( + name: "IX_FeedbackReports_ClientId", + table: "FeedbackReports"); + + migrationBuilder.DropIndex( + name: "IX_FeedbackReports_ContentItemId", + table: "FeedbackReports"); + } + } +} diff --git a/backend/src/Socialize.Api/Migrations/AppDbContextModelSnapshot.cs b/backend/src/Socialize.Api/Migrations/AppDbContextModelSnapshot.cs index c0d4088e..0d575cf6 100644 --- a/backend/src/Socialize.Api/Migrations/AppDbContextModelSnapshot.cs +++ b/backend/src/Socialize.Api/Migrations/AppDbContextModelSnapshot.cs @@ -1359,6 +1359,12 @@ namespace Socialize.Api.Migrations b.HasKey("Id"); + b.HasIndex("CampaignId"); + + b.HasIndex("ClientId"); + + b.HasIndex("ContentItemId"); + b.HasIndex("LastActivityAt"); b.HasIndex("ReporterUserId"); @@ -1856,6 +1862,83 @@ namespace Socialize.Api.Migrations .IsRequired(); }); + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.ApprovalDecision", b => + { + b.HasOne("Socialize.Api.Modules.Approvals.Data.ApprovalRequest", null) + .WithMany() + .HasForeignKey("ApprovalRequestId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.ApprovalRequest", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Approvals.Data.ApprovalWorkflowInstance", null) + .WithMany() + .HasForeignKey("WorkflowInstanceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.ApprovalWorkflowInstance", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Approvals.Data.WorkspaceApprovalStepConfiguration", b => + { + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Assets.Data.Asset", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Assets.Data.AssetRevision", b => + { + b.HasOne("Socialize.Api.Modules.Assets.Data.Asset", null) + .WithMany() + .HasForeignKey("AssetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Socialize.Api.Modules.CalendarIntegrations.Data.CalendarEvent", b => { b.HasOne("Socialize.Api.Modules.CalendarIntegrations.Data.CalendarSource", null) @@ -1865,6 +1948,104 @@ namespace Socialize.Api.Migrations .IsRequired(); }); + modelBuilder.Entity("Socialize.Api.Modules.Campaigns.Data.Campaign", b => + { + b.HasOne("Socialize.Api.Modules.Clients.Data.Client", null) + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Channels.Data.Channel", b => + { + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Clients.Data.Client", b => + { + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.Comments.Data.Comment", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Comments.Data.Comment", null) + .WithMany() + .HasForeignKey("ParentCommentId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.ContentItems.Data.ContentItem", b => + { + b.HasOne("Socialize.Api.Modules.Campaigns.Data.Campaign", null) + .WithMany() + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Clients.Data.Client", null) + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.ContentItems.Data.ContentItemActivityEntry", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Socialize.Api.Modules.ContentItems.Data.ContentItemRevision", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackActivityEntry", b => { b.HasOne("Socialize.Api.Modules.Feedback.Data.FeedbackReport", "FeedbackReport") @@ -1887,6 +2068,29 @@ namespace Socialize.Api.Migrations b.Navigation("FeedbackReport"); }); + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackReport", b => + { + b.HasOne("Socialize.Api.Modules.Campaigns.Data.Campaign", null) + .WithMany() + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Socialize.Api.Modules.Clients.Data.Client", null) + .WithMany() + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.SetNull); + }); + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackScreenshot", b => { b.HasOne("Socialize.Api.Modules.Feedback.Data.FeedbackReport", "FeedbackReport") @@ -1909,6 +2113,20 @@ namespace Socialize.Api.Migrations b.Navigation("FeedbackReport"); }); + modelBuilder.Entity("Socialize.Api.Modules.Notifications.Data.NotificationEvent", b => + { + b.HasOne("Socialize.Api.Modules.ContentItems.Data.ContentItem", null) + .WithMany() + .HasForeignKey("ContentItemId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + modelBuilder.Entity("Socialize.Api.Modules.Organizations.Data.OrganizationMembership", b => { b.HasOne("Socialize.Api.Modules.Organizations.Data.Organization", null) @@ -1927,6 +2145,15 @@ namespace Socialize.Api.Migrations .IsRequired(); }); + modelBuilder.Entity("Socialize.Api.Modules.Workspaces.Data.WorkspaceInvite", b => + { + b.HasOne("Socialize.Api.Modules.Workspaces.Data.Workspace", null) + .WithMany() + .HasForeignKey("WorkspaceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + modelBuilder.Entity("Socialize.Api.Modules.Feedback.Data.FeedbackReport", b => { b.Navigation("ActivityEntries"); diff --git a/backend/src/Socialize.Api/Modules/Approvals/Data/ApprovalModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Approvals/Data/ApprovalModelConfiguration.cs index 2f6ba87d..043b8958 100644 --- a/backend/src/Socialize.Api/Modules/Approvals/Data/ApprovalModelConfiguration.cs +++ b/backend/src/Socialize.Api/Modules/Approvals/Data/ApprovalModelConfiguration.cs @@ -1,4 +1,6 @@ using Microsoft.EntityFrameworkCore; +using Socialize.Api.Modules.ContentItems.Data; +using Socialize.Api.Modules.Workspaces.Data; namespace Socialize.Api.Modules.Approvals.Data; @@ -20,6 +22,14 @@ internal static class ApprovalModelConfiguration workflowInstance.HasIndex(x => new { x.ContentItemId, x.State }) .IsUnique() .HasFilter("\"State\" = 'Pending'"); + workflowInstance.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); + workflowInstance.HasOne() + .WithMany() + .HasForeignKey(x => x.ContentItemId) + .OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(approvalRequest => @@ -40,6 +50,18 @@ internal static class ApprovalModelConfiguration approvalRequest.HasIndex(x => x.ContentItemId); approvalRequest.HasIndex(x => x.WorkflowInstanceId); approvalRequest.HasIndex(x => x.ReviewerEmail); + approvalRequest.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); + approvalRequest.HasOne() + .WithMany() + .HasForeignKey(x => x.ContentItemId) + .OnDelete(DeleteBehavior.Restrict); + approvalRequest.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkflowInstanceId) + .OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(approvalDecision => @@ -54,6 +76,10 @@ internal static class ApprovalModelConfiguration .ValueGeneratedOnAdd() .HasDefaultValueSql("CURRENT_TIMESTAMP"); approvalDecision.HasIndex(x => x.ApprovalRequestId); + approvalDecision.HasOne() + .WithMany() + .HasForeignKey(x => x.ApprovalRequestId) + .OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(approvalStep => @@ -69,6 +95,10 @@ internal static class ApprovalModelConfiguration .HasDefaultValueSql("CURRENT_TIMESTAMP"); approvalStep.HasIndex(x => x.WorkspaceId); approvalStep.HasIndex(x => new { x.WorkspaceId, x.SortOrder }).IsUnique(); + approvalStep.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); }); return modelBuilder; diff --git a/backend/src/Socialize.Api/Modules/Assets/Data/AssetModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Assets/Data/AssetModelConfiguration.cs index 77bb8458..61467a07 100644 --- a/backend/src/Socialize.Api/Modules/Assets/Data/AssetModelConfiguration.cs +++ b/backend/src/Socialize.Api/Modules/Assets/Data/AssetModelConfiguration.cs @@ -1,4 +1,6 @@ using Microsoft.EntityFrameworkCore; +using Socialize.Api.Modules.ContentItems.Data; +using Socialize.Api.Modules.Workspaces.Data; namespace Socialize.Api.Modules.Assets.Data; @@ -21,6 +23,14 @@ internal static class AssetModelConfiguration .HasDefaultValueSql("CURRENT_TIMESTAMP"); asset.HasIndex(x => x.WorkspaceId); asset.HasIndex(x => x.ContentItemId); + asset.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); + asset.HasOne() + .WithMany() + .HasForeignKey(x => x.ContentItemId) + .OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(revision => @@ -35,6 +45,10 @@ internal static class AssetModelConfiguration .HasDefaultValueSql("CURRENT_TIMESTAMP"); revision.HasIndex(x => x.AssetId); revision.HasIndex(x => new { x.AssetId, x.RevisionNumber }).IsUnique(); + revision.HasOne() + .WithMany() + .HasForeignKey(x => x.AssetId) + .OnDelete(DeleteBehavior.Cascade); }); return modelBuilder; diff --git a/backend/src/Socialize.Api/Modules/Campaigns/Data/CampaignModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Campaigns/Data/CampaignModelConfiguration.cs index aa09ea73..d7b04cfe 100644 --- a/backend/src/Socialize.Api/Modules/Campaigns/Data/CampaignModelConfiguration.cs +++ b/backend/src/Socialize.Api/Modules/Campaigns/Data/CampaignModelConfiguration.cs @@ -1,4 +1,6 @@ using Microsoft.EntityFrameworkCore; +using Socialize.Api.Modules.Clients.Data; +using Socialize.Api.Modules.Workspaces.Data; namespace Socialize.Api.Modules.Campaigns.Data; @@ -20,6 +22,14 @@ internal static class CampaignModelConfiguration campaign.HasIndex(x => new { x.ClientId, x.Name }).IsUnique(); campaign.HasIndex(x => x.WorkspaceId); campaign.HasIndex(x => x.ClientId); + campaign.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); + campaign.HasOne() + .WithMany() + .HasForeignKey(x => x.ClientId) + .OnDelete(DeleteBehavior.Restrict); }); return modelBuilder; diff --git a/backend/src/Socialize.Api/Modules/Channels/Data/ChannelModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Channels/Data/ChannelModelConfiguration.cs index 8c3f6dfb..05f55275 100644 --- a/backend/src/Socialize.Api/Modules/Channels/Data/ChannelModelConfiguration.cs +++ b/backend/src/Socialize.Api/Modules/Channels/Data/ChannelModelConfiguration.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Socialize.Api.Modules.Workspaces.Data; namespace Socialize.Api.Modules.Channels.Data; @@ -19,6 +20,10 @@ internal static class ChannelModelConfiguration .HasDefaultValueSql("CURRENT_TIMESTAMP"); channel.HasIndex(x => x.WorkspaceId); channel.HasIndex(x => new { x.WorkspaceId, x.Network, x.Name }).IsUnique(); + channel.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); }); return modelBuilder; diff --git a/backend/src/Socialize.Api/Modules/Clients/Data/ClientModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Clients/Data/ClientModelConfiguration.cs index 82179978..885cf818 100644 --- a/backend/src/Socialize.Api/Modules/Clients/Data/ClientModelConfiguration.cs +++ b/backend/src/Socialize.Api/Modules/Clients/Data/ClientModelConfiguration.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Socialize.Api.Modules.Workspaces.Data; namespace Socialize.Api.Modules.Clients.Data; @@ -21,6 +22,10 @@ internal static class ClientModelConfiguration .HasDefaultValueSql("CURRENT_TIMESTAMP"); client.HasIndex(x => new { x.WorkspaceId, x.Name }).IsUnique(); client.HasIndex(x => x.WorkspaceId); + client.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); }); return modelBuilder; diff --git a/backend/src/Socialize.Api/Modules/Comments/Data/CommentModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Comments/Data/CommentModelConfiguration.cs index ef27da12..f7bce594 100644 --- a/backend/src/Socialize.Api/Modules/Comments/Data/CommentModelConfiguration.cs +++ b/backend/src/Socialize.Api/Modules/Comments/Data/CommentModelConfiguration.cs @@ -1,4 +1,6 @@ using Microsoft.EntityFrameworkCore; +using Socialize.Api.Modules.ContentItems.Data; +using Socialize.Api.Modules.Workspaces.Data; namespace Socialize.Api.Modules.Comments.Data; @@ -24,6 +26,18 @@ internal static class CommentModelConfiguration comment.HasIndex(x => x.WorkspaceId); comment.HasIndex(x => x.ContentItemId); comment.HasIndex(x => x.ParentCommentId); + comment.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); + comment.HasOne() + .WithMany() + .HasForeignKey(x => x.ContentItemId) + .OnDelete(DeleteBehavior.Restrict); + comment.HasOne() + .WithMany() + .HasForeignKey(x => x.ParentCommentId) + .OnDelete(DeleteBehavior.Restrict); }); return modelBuilder; diff --git a/backend/src/Socialize.Api/Modules/ContentItems/Data/ContentItemModelConfiguration.cs b/backend/src/Socialize.Api/Modules/ContentItems/Data/ContentItemModelConfiguration.cs index 68be29d1..575896ff 100644 --- a/backend/src/Socialize.Api/Modules/ContentItems/Data/ContentItemModelConfiguration.cs +++ b/backend/src/Socialize.Api/Modules/ContentItems/Data/ContentItemModelConfiguration.cs @@ -1,4 +1,7 @@ using Microsoft.EntityFrameworkCore; +using Socialize.Api.Modules.Campaigns.Data; +using Socialize.Api.Modules.Clients.Data; +using Socialize.Api.Modules.Workspaces.Data; namespace Socialize.Api.Modules.ContentItems.Data; @@ -22,6 +25,18 @@ internal static class ContentItemModelConfiguration contentItem.HasIndex(x => x.WorkspaceId); contentItem.HasIndex(x => x.ClientId); contentItem.HasIndex(x => x.CampaignId); + contentItem.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); + contentItem.HasOne() + .WithMany() + .HasForeignKey(x => x.ClientId) + .OnDelete(DeleteBehavior.Restrict); + contentItem.HasOne() + .WithMany() + .HasForeignKey(x => x.CampaignId) + .OnDelete(DeleteBehavior.Restrict); }); modelBuilder.Entity(revision => @@ -39,6 +54,10 @@ internal static class ContentItemModelConfiguration .HasDefaultValueSql("CURRENT_TIMESTAMP"); revision.HasIndex(x => x.ContentItemId); revision.HasIndex(x => new { x.ContentItemId, x.RevisionNumber }).IsUnique(); + revision.HasOne() + .WithMany() + .HasForeignKey(x => x.ContentItemId) + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entry => @@ -56,6 +75,14 @@ internal static class ContentItemModelConfiguration entry.HasIndex(x => x.WorkspaceId); entry.HasIndex(x => x.ContentItemId); entry.HasIndex(x => new { x.ContentItemId, x.CreatedAt }); + entry.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); + entry.HasOne() + .WithMany() + .HasForeignKey(x => x.ContentItemId) + .OnDelete(DeleteBehavior.Restrict); }); return modelBuilder; diff --git a/backend/src/Socialize.Api/Modules/Feedback/Data/FeedbackModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Feedback/Data/FeedbackModelConfiguration.cs index d6a5707e..142d216c 100644 --- a/backend/src/Socialize.Api/Modules/Feedback/Data/FeedbackModelConfiguration.cs +++ b/backend/src/Socialize.Api/Modules/Feedback/Data/FeedbackModelConfiguration.cs @@ -1,4 +1,8 @@ using Microsoft.EntityFrameworkCore; +using Socialize.Api.Modules.Campaigns.Data; +using Socialize.Api.Modules.Clients.Data; +using Socialize.Api.Modules.ContentItems.Data; +using Socialize.Api.Modules.Workspaces.Data; namespace Socialize.Api.Modules.Feedback.Data; @@ -29,6 +33,22 @@ internal static class FeedbackModelConfiguration feedback.HasIndex(x => x.Type); feedback.HasIndex(x => x.WorkspaceId); feedback.HasIndex(x => x.LastActivityAt); + feedback.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.SetNull); + feedback.HasOne() + .WithMany() + .HasForeignKey(x => x.ClientId) + .OnDelete(DeleteBehavior.SetNull); + feedback.HasOne() + .WithMany() + .HasForeignKey(x => x.CampaignId) + .OnDelete(DeleteBehavior.SetNull); + feedback.HasOne() + .WithMany() + .HasForeignKey(x => x.ContentItemId) + .OnDelete(DeleteBehavior.SetNull); }); modelBuilder.Entity(tag => diff --git a/backend/src/Socialize.Api/Modules/Notifications/Data/NotificationModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Notifications/Data/NotificationModelConfiguration.cs index 0051aab1..147a822d 100644 --- a/backend/src/Socialize.Api/Modules/Notifications/Data/NotificationModelConfiguration.cs +++ b/backend/src/Socialize.Api/Modules/Notifications/Data/NotificationModelConfiguration.cs @@ -1,4 +1,6 @@ using Microsoft.EntityFrameworkCore; +using Socialize.Api.Modules.ContentItems.Data; +using Socialize.Api.Modules.Workspaces.Data; namespace Socialize.Api.Modules.Notifications.Data; @@ -22,6 +24,14 @@ internal static class NotificationModelConfiguration notificationEvent.HasIndex(x => x.ContentItemId); notificationEvent.HasIndex(x => x.RecipientUserId); notificationEvent.HasIndex(x => x.CreatedAt); + notificationEvent.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); + notificationEvent.HasOne() + .WithMany() + .HasForeignKey(x => x.ContentItemId) + .OnDelete(DeleteBehavior.SetNull); }); return modelBuilder; diff --git a/backend/src/Socialize.Api/Modules/Workspaces/Data/WorkspaceModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Workspaces/Data/WorkspaceModelConfiguration.cs index 574f658f..be43f3e9 100644 --- a/backend/src/Socialize.Api/Modules/Workspaces/Data/WorkspaceModelConfiguration.cs +++ b/backend/src/Socialize.Api/Modules/Workspaces/Data/WorkspaceModelConfiguration.cs @@ -41,6 +41,10 @@ internal static class WorkspaceModelConfiguration .HasDefaultValueSql("CURRENT_TIMESTAMP"); workspaceInvite.HasIndex(x => x.WorkspaceId); workspaceInvite.HasIndex(x => new { x.WorkspaceId, x.Email, x.Status }); + workspaceInvite.HasOne() + .WithMany() + .HasForeignKey(x => x.WorkspaceId) + .OnDelete(DeleteBehavior.Restrict); }); return modelBuilder; diff --git a/scripts/generate-db-diagram.sh b/scripts/generate-db-diagram.sh index f8d9fe67..5aca63e5 100755 --- a/scripts/generate-db-diagram.sh +++ b/scripts/generate-db-diagram.sh @@ -14,11 +14,6 @@ SVG_FILE="${OUT_DIR}/database-diagram.svg" PNG_FILE="${OUT_DIR}/database-diagram.png" HTML_FILE="${OUT_DIR}/database-diagram.html" -if ! command -v psql >/dev/null 2>&1; then - echo "psql is required to generate the database diagram." >&2 - exit 1 -fi - export PGCONNECT_TIMEOUT="${PGCONNECT_TIMEOUT:-5}" export PGHOST="${PGHOST:-localhost}" export PGPORT="${PGPORT:-5433}" @@ -38,6 +33,11 @@ else PSQL=(psql "${PSQL_ARGS[@]}") fi +if [[ "${PSQL[0]}" == "psql" ]] && ! command -v psql >/dev/null 2>&1; then + echo "psql is required to generate the database diagram when not using the local Docker container." >&2 + exit 1 +fi + echo "Reading schema from ${CONNECTION_LABEL}..." if ! "${PSQL[@]}" -c "select 1" >/dev/null 2>&1; then @@ -50,9 +50,31 @@ fi SELECT c.table_schema || '.' || c.table_name AS table_id, c.column_name, - c.udt_name, + CASE + WHEN c.data_type = 'character varying' + THEN 'varchar(' || c.character_maximum_length || ')' + WHEN c.data_type = 'character' + THEN 'char(' || c.character_maximum_length || ')' + WHEN c.data_type = 'text' + THEN 'text' + WHEN c.data_type = 'numeric' AND c.numeric_precision IS NOT NULL AND c.numeric_scale IS NOT NULL + THEN 'numeric(' || c.numeric_precision || ',' || c.numeric_scale || ')' + WHEN c.data_type = 'numeric' AND c.numeric_precision IS NOT NULL + THEN 'numeric(' || c.numeric_precision || ')' + WHEN c.data_type = 'timestamp with time zone' + THEN 'timestamptz' + WHEN c.data_type = 'timestamp without time zone' + THEN 'timestamp' + WHEN c.data_type = 'USER-DEFINED' + THEN c.udt_name + ELSE c.data_type + END AS formatted_type, c.is_nullable, - CASE WHEN pk.column_name IS NULL THEN '' ELSE 'PK' END AS key_type + concat_ws(' ', + CASE WHEN pk.column_name IS NULL THEN NULL ELSE 'PK' END, + CASE WHEN fk.column_name IS NULL THEN NULL ELSE 'FK' END, + CASE WHEN ix.column_name IS NULL THEN NULL ELSE 'IX' END + ) AS column_markers FROM information_schema.columns c LEFT JOIN ( SELECT @@ -69,6 +91,39 @@ LEFT JOIN ( ON pk.table_schema = c.table_schema AND pk.table_name = c.table_name AND pk.column_name = c.column_name +LEFT JOIN ( + SELECT DISTINCT + kcu.table_schema, + kcu.table_name, + kcu.column_name + FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu + ON kcu.constraint_catalog = tc.constraint_catalog + AND kcu.constraint_schema = tc.constraint_schema + AND kcu.constraint_name = tc.constraint_name + WHERE tc.constraint_type = 'FOREIGN KEY' +) fk + ON fk.table_schema = c.table_schema + AND fk.table_name = c.table_name + AND fk.column_name = c.column_name +LEFT JOIN ( + SELECT DISTINCT + indexed_schema.nspname AS table_schema, + indexed_table.relname AS table_name, + indexed_attribute.attname AS column_name + FROM pg_catalog.pg_index index_definition + JOIN pg_catalog.pg_class indexed_table + ON indexed_table.oid = index_definition.indrelid + JOIN pg_catalog.pg_namespace indexed_schema + ON indexed_schema.oid = indexed_table.relnamespace + JOIN pg_catalog.pg_attribute indexed_attribute + ON indexed_attribute.attrelid = indexed_table.oid + AND indexed_attribute.attnum = ANY(index_definition.indkey) + WHERE indexed_schema.nspname = 'public' +) ix + ON ix.table_schema = c.table_schema + AND ix.table_name = c.table_name + AND ix.column_name = c.column_name WHERE c.table_schema = 'public' AND c.table_name <> '__EFMigrationsHistory' ORDER BY c.table_name, c.ordinal_position; @@ -81,7 +136,27 @@ SELECT kcu.column_name AS child_column, ccu.table_schema || '.' || ccu.table_name AS parent_table, ccu.column_name AS parent_column, - col.is_nullable + col.is_nullable, + CASE + WHEN EXISTS ( + SELECT 1 + FROM pg_catalog.pg_index unique_index + JOIN pg_catalog.pg_class unique_table + ON unique_table.oid = unique_index.indrelid + JOIN pg_catalog.pg_namespace unique_schema + ON unique_schema.oid = unique_table.relnamespace + JOIN pg_catalog.pg_attribute unique_attribute + ON unique_attribute.attrelid = unique_table.oid + AND unique_attribute.attnum = ANY(unique_index.indkey) + WHERE unique_schema.nspname = kcu.table_schema + AND unique_table.relname = kcu.table_name + AND unique_attribute.attname = kcu.column_name + AND unique_index.indisunique + AND unique_index.indnatts = 1 + ) + THEN 'YES' + ELSE 'NO' + END AS child_column_is_unique FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON kcu.constraint_catalog = tc.constraint_catalog @@ -119,11 +194,18 @@ node_id() { printf '%s' "$value" } +dot_label_escape() { + local value="$1" + value="${value//\\/\\\\}" + value="${value//\"/\\\"}" + printf '%s' "$value" +} + declare -A TABLE_COLUMNS declare -A TABLE_SEEN declare -a TABLE_ORDER -while IFS=$'\t' read -r table_name column_name column_type nullable key_type; do +while IFS=$'\t' read -r table_name column_name column_type nullable column_markers; do [[ -n "$table_name" ]] || continue if [[ -z "${TABLE_SEEN[$table_name]:-}" ]]; then @@ -133,11 +215,12 @@ while IFS=$'\t' read -r table_name column_name column_type nullable key_type; do label="$(html_escape "$column_name")" type_label="$(html_escape "$column_type")" - suffix="" - [[ "$key_type" == "PK" ]] && suffix=" PK" - [[ "$nullable" == "YES" ]] && suffix="${suffix} nullable" + marker_label="$(html_escape "$column_markers")" + [[ -n "$marker_label" ]] || marker_label=" " + nullable_label="" + [[ "$nullable" == "YES" ]] && nullable_label=' nullable' - TABLE_COLUMNS["$table_name"]+=$' '"${label}"$''"${type_label}${suffix}"$'\n' + TABLE_COLUMNS["$table_name"]+=$' '"${marker_label}"$''"${label}"$''"${type_label}${nullable_label}"$'\n' done < "$TABLES_TSV" { @@ -149,7 +232,7 @@ digraph database { pad=0.4, nodesep=0.6, ranksep=1.0, - splines=ortho, + splines=spline, overlap=false ]; @@ -162,9 +245,26 @@ digraph database { color="#64748b", arrowsize=0.7, fontname="Arial", - fontsize=10 + fontsize=10, + labeldistance=1.8, + labelangle=22 ]; + legend [label=< + + + + + + + + + + + +
Legend
PKprimary key column
FKforeign key column
IXindexed column
solid linerequired FK
dashed linenullable FK
1exactly one referenced row
0..1zero or one referencing row
0..*zero or many referencing rows
Edges point from referenced table to referencing table.
+ >]; + DOT for table_name in "${TABLE_ORDER[@]}"; do @@ -172,20 +272,22 @@ DOT title="$(html_escape "${table_name#public.}")" printf ' %s [label=<\n' "$id" printf ' \n' - printf ' \n' "$title" + printf ' \n' "$title" printf '%s' "${TABLE_COLUMNS[$table_name]}" printf '
%s
%s
\n' printf ' >];\n\n' done - while IFS=$'\t' read -r constraint_name child_table child_column parent_table parent_column nullable; do + while IFS=$'\t' read -r constraint_name child_table child_column parent_table parent_column nullable child_column_is_unique; do [[ -n "$constraint_name" ]] || continue child_id="$(node_id "$child_table")" parent_id="$(node_id "$parent_table")" - label="$(html_escape "${child_column} -> ${parent_column}")" + child_cardinality="0..*" + [[ "$child_column_is_unique" == "YES" ]] && child_cardinality="0..1" + label="$(dot_label_escape "${parent_column} -> ${child_column}")" style="solid" [[ "$nullable" == "YES" ]] && style="dashed" - printf ' %s -> %s [label="%s", style="%s"];\n' "$child_id" "$parent_id" "$label" "$style" + printf ' %s -> %s [label="%s", taillabel="1", headlabel="%s", style="%s"];\n' "$parent_id" "$child_id" "$label" "$child_cardinality" "$style" done < "$RELATIONS_TSV" printf '}\n' @@ -219,14 +321,18 @@ cat > "$HTML_FILE" < "$HTML_FILE" < "$HTML_FILE" <
-

Socialize Database Diagram

- SVG - PNG - DOT +
+

Socialize Database Diagram

+ SVG + PNG + DOT +
+
    +
  • required FK
  • +
  • nullable FK
  • +
  • 1: exactly one referenced row
  • +
  • 0..1: zero or one referencing row
  • +
  • 0..*: zero or many referencing rows
  • +
  • Edges point from referenced table to referencing table.
  • +