diff --git a/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Infrastructure/Persistence/Migrations/20251103150353_FixUserTenantRolesIgnoreNavigation.cs b/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Infrastructure/Persistence/Migrations/20251103150353_FixUserTenantRolesIgnoreNavigation.cs index 3bfe16a..c042a09 100644 --- a/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Infrastructure/Persistence/Migrations/20251103150353_FixUserTenantRolesIgnoreNavigation.cs +++ b/colaflow-api/src/Modules/Identity/ColaFlow.Modules.Identity.Infrastructure/Persistence/Migrations/20251103150353_FixUserTenantRolesIgnoreNavigation.cs @@ -10,21 +10,68 @@ namespace ColaFlow.Modules.Identity.Infrastructure.Persistence.Migrations /// protected override void Up(MigrationBuilder migrationBuilder) { - // Drop and recreate foreign keys to ensure they reference the correct columns - // This fixes BUG-002: Foreign keys were incorrectly referencing user_id1/tenant_id1 + // IDEMPOTENT FIX: Check if table exists before modifying it + // If the table doesn't exist, create it first + migrationBuilder.Sql(@" + -- Create user_tenant_roles table if it doesn't exist + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'identity' + AND table_name = 'user_tenant_roles' + ) THEN + -- Create the table + CREATE TABLE identity.user_tenant_roles ( + id uuid NOT NULL PRIMARY KEY, + user_id uuid NOT NULL, + tenant_id uuid NOT NULL, + role character varying(50) NOT NULL, + assigned_at timestamp with time zone NOT NULL, + assigned_by_user_id uuid, + CONSTRAINT uq_user_tenant_roles_user_tenant UNIQUE (user_id, tenant_id) + ); - migrationBuilder.DropForeignKey( - name: "FK_user_tenant_roles_tenants_tenant_id", - schema: "identity", - table: "user_tenant_roles"); + -- Create basic indexes + -- Note: ix_user_tenant_roles_tenant_role will be created by a later migration + CREATE INDEX ix_user_tenant_roles_user_id ON identity.user_tenant_roles(user_id); + CREATE INDEX ix_user_tenant_roles_tenant_id ON identity.user_tenant_roles(tenant_id); + CREATE INDEX ix_user_tenant_roles_role ON identity.user_tenant_roles(role); + END IF; + END $$; + "); - migrationBuilder.DropForeignKey( - name: "FK_user_tenant_roles_users_user_id", - schema: "identity", - table: "user_tenant_roles"); + // Drop existing foreign keys if they exist + migrationBuilder.Sql(@" + DO $$ + BEGIN + -- Drop FK to tenants if it exists + IF EXISTS ( + SELECT FROM information_schema.table_constraints + WHERE constraint_schema = 'identity' + AND table_name = 'user_tenant_roles' + AND constraint_name = 'FK_user_tenant_roles_tenants_tenant_id' + ) THEN + ALTER TABLE identity.user_tenant_roles + DROP CONSTRAINT ""FK_user_tenant_roles_tenants_tenant_id""; + END IF; + + -- Drop FK to users if it exists + IF EXISTS ( + SELECT FROM information_schema.table_constraints + WHERE constraint_schema = 'identity' + AND table_name = 'user_tenant_roles' + AND constraint_name = 'FK_user_tenant_roles_users_user_id' + ) THEN + ALTER TABLE identity.user_tenant_roles + DROP CONSTRAINT ""FK_user_tenant_roles_users_user_id""; + END IF; + END $$; + "); // Recreate foreign keys with correct column references - // Note: users and tenants tables are in the default schema (no explicit schema) + // Note: At this point in time, users and tenants are still in the default schema + // (They will be moved to identity schema in a later migration) migrationBuilder.AddForeignKey( name: "FK_user_tenant_roles_users_user_id", schema: "identity", @@ -47,23 +94,35 @@ namespace ColaFlow.Modules.Identity.Infrastructure.Persistence.Migrations /// protected override void Down(MigrationBuilder migrationBuilder) { - migrationBuilder.AddForeignKey( - name: "FK_user_tenant_roles_tenants_tenant_id", - schema: "identity", - table: "user_tenant_roles", - column: "tenant_id", - principalTable: "tenants", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); + // Drop foreign keys if they exist + migrationBuilder.Sql(@" + DO $$ + BEGIN + IF EXISTS ( + SELECT FROM information_schema.table_constraints + WHERE constraint_schema = 'identity' + AND table_name = 'user_tenant_roles' + AND constraint_name = 'FK_user_tenant_roles_tenants_tenant_id' + ) THEN + ALTER TABLE identity.user_tenant_roles + DROP CONSTRAINT ""FK_user_tenant_roles_tenants_tenant_id""; + END IF; - migrationBuilder.AddForeignKey( - name: "FK_user_tenant_roles_users_user_id", - schema: "identity", - table: "user_tenant_roles", - column: "user_id", - principalTable: "users", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); + IF EXISTS ( + SELECT FROM information_schema.table_constraints + WHERE constraint_schema = 'identity' + AND table_name = 'user_tenant_roles' + AND constraint_name = 'FK_user_tenant_roles_users_user_id' + ) THEN + ALTER TABLE identity.user_tenant_roles + DROP CONSTRAINT ""FK_user_tenant_roles_users_user_id""; + END IF; + END $$; + "); + + // Note: We don't drop the table in Down() because it should have been created + // by a previous migration. If it was created by this migration (first run), + // then it will be cleaned up when the database is reset. } } }