diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1754371805617-fixSoftDeleteUniqueConstraints.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1754371805617-fixSoftDeleteUniqueConstraints.ts new file mode 100644 index 0000000000..3068168010 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1754371805617-fixSoftDeleteUniqueConstraints.ts @@ -0,0 +1,61 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class FixSoftDeleteUniqueConstraints1754371805617 + implements MigrationInterface +{ + name = 'FixSoftDeleteUniqueConstraints1754371805617'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."userWorkspace" DROP CONSTRAINT "IDX_USER_WORKSPACE_USER_ID_WORKSPACE_ID_UNIQUE"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."agent" DROP CONSTRAINT "IDX_AGENT_NAME_WORKSPACE_ID_UNIQUE"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."viewField" DROP CONSTRAINT "IDX_VIEW_FIELD_FIELD_METADATA_ID_VIEW_ID_UNIQUE"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."viewSort" DROP CONSTRAINT "IDX_VIEW_SORT_FIELD_METADATA_ID_VIEW_ID_UNIQUE"`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_USER_WORKSPACE_USER_ID_WORKSPACE_ID_UNIQUE" ON "core"."userWorkspace" ("userId", "workspaceId") WHERE "deletedAt" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_AGENT_NAME_WORKSPACE_ID_UNIQUE" ON "core"."agent" ("name", "workspaceId") WHERE "deletedAt" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_VIEW_FIELD_FIELD_METADATA_ID_VIEW_ID_UNIQUE" ON "core"."viewField" ("fieldMetadataId", "viewId") WHERE "deletedAt" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_VIEW_SORT_FIELD_METADATA_ID_VIEW_ID_UNIQUE" ON "core"."viewSort" ("fieldMetadataId", "viewId") WHERE "deletedAt" IS NULL`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DROP INDEX "core"."IDX_VIEW_SORT_FIELD_METADATA_ID_VIEW_ID_UNIQUE"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_VIEW_FIELD_FIELD_METADATA_ID_VIEW_ID_UNIQUE"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_AGENT_NAME_WORKSPACE_ID_UNIQUE"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_USER_WORKSPACE_USER_ID_WORKSPACE_ID_UNIQUE"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."viewSort" ADD CONSTRAINT "IDX_VIEW_SORT_FIELD_METADATA_ID_VIEW_ID_UNIQUE" UNIQUE ("fieldMetadataId", "viewId")`, + ); + await queryRunner.query( + `ALTER TABLE "core"."viewField" ADD CONSTRAINT "IDX_VIEW_FIELD_FIELD_METADATA_ID_VIEW_ID_UNIQUE" UNIQUE ("fieldMetadataId", "viewId")`, + ); + await queryRunner.query( + `ALTER TABLE "core"."agent" ADD CONSTRAINT "IDX_AGENT_NAME_WORKSPACE_ID_UNIQUE" UNIQUE ("name", "workspaceId")`, + ); + await queryRunner.query( + `ALTER TABLE "core"."userWorkspace" ADD CONSTRAINT "IDX_USER_WORKSPACE_USER_ID_WORKSPACE_ID_UNIQUE" UNIQUE ("userId", "workspaceId")`, + ); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.entity.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.entity.ts index 9fb04e6e90..20b61238a7 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.entity.ts @@ -14,7 +14,6 @@ import { OneToMany, PrimaryGeneratedColumn, Relation, - Unique, UpdateDateColumn, } from 'typeorm'; @@ -36,10 +35,14 @@ registerEnumType(PermissionsOnAllObjectRecords, { @Entity({ name: 'userWorkspace', schema: 'core' }) @ObjectType() -@Unique('IDX_USER_WORKSPACE_USER_ID_WORKSPACE_ID_UNIQUE', [ - 'userId', - 'workspaceId', -]) +@Index( + 'IDX_USER_WORKSPACE_USER_ID_WORKSPACE_ID_UNIQUE', + ['userId', 'workspaceId'], + { + unique: true, + where: '"deletedAt" IS NULL', + }, +) @Index('IDX_USER_WORKSPACE_USER_ID', ['userId']) @Index('IDX_USER_WORKSPACE_WORKSPACE_ID', ['workspaceId']) export class UserWorkspace { diff --git a/packages/twenty-server/src/engine/metadata-modules/agent/agent.entity.ts b/packages/twenty-server/src/engine/metadata-modules/agent/agent.entity.ts index 6ed86e6a79..3d8799d00d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/agent/agent.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/agent/agent.entity.ts @@ -8,7 +8,6 @@ import { ManyToOne, OneToMany, PrimaryGeneratedColumn, - Unique, UpdateDateColumn, } from 'typeorm'; @@ -22,7 +21,10 @@ import { AgentHandoffEntity } from './agent-handoff.entity'; @Entity('agent') @Index('IDX_AGENT_ID_DELETED_AT', ['id', 'deletedAt']) -@Unique('IDX_AGENT_NAME_WORKSPACE_ID_UNIQUE', ['name', 'workspaceId']) +@Index('IDX_AGENT_NAME_WORKSPACE_ID_UNIQUE', ['name', 'workspaceId'], { + unique: true, + where: '"deletedAt" IS NULL', +}) export class AgentEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/packages/twenty-server/src/engine/metadata-modules/view/view-field.entity.ts b/packages/twenty-server/src/engine/metadata-modules/view/view-field.entity.ts index f89198656d..e00e807251 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view/view-field.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view/view-field.entity.ts @@ -9,7 +9,6 @@ import { ManyToOne, PrimaryGeneratedColumn, Relation, - Unique, UpdateDateColumn, } from 'typeorm'; @@ -20,10 +19,14 @@ import { View } from 'src/engine/metadata-modules/view/view.entity'; @Entity({ name: 'viewField', schema: 'core' }) @Index('IDX_VIEW_FIELD_WORKSPACE_ID_VIEW_ID', ['workspaceId', 'viewId']) -@Unique('IDX_VIEW_FIELD_FIELD_METADATA_ID_VIEW_ID_UNIQUE', [ - 'fieldMetadataId', - 'viewId', -]) +@Index( + 'IDX_VIEW_FIELD_FIELD_METADATA_ID_VIEW_ID_UNIQUE', + ['fieldMetadataId', 'viewId'], + { + unique: true, + where: '"deletedAt" IS NULL', + }, +) export class ViewField { @IDField(() => UUIDScalarType) @PrimaryGeneratedColumn('uuid') diff --git a/packages/twenty-server/src/engine/metadata-modules/view/view-sort.entity.ts b/packages/twenty-server/src/engine/metadata-modules/view/view-sort.entity.ts index 1b2a5aba48..8037174940 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view/view-sort.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view/view-sort.entity.ts @@ -11,7 +11,6 @@ import { ManyToOne, PrimaryGeneratedColumn, Relation, - Unique, UpdateDateColumn, } from 'typeorm'; @@ -24,10 +23,14 @@ registerEnumType(ViewSortDirection, { name: 'ViewSortDirection' }); @Entity({ name: 'viewSort', schema: 'core' }) @Index('IDX_VIEW_SORT_WORKSPACE_ID_VIEW_ID', ['workspaceId', 'viewId']) -@Unique('IDX_VIEW_SORT_FIELD_METADATA_ID_VIEW_ID_UNIQUE', [ - 'fieldMetadataId', - 'viewId', -]) +@Index( + 'IDX_VIEW_SORT_FIELD_METADATA_ID_VIEW_ID_UNIQUE', + ['fieldMetadataId', 'viewId'], + { + unique: true, + where: '"deletedAt" IS NULL', + }, +) export class ViewSort { @IDField(() => UUIDScalarType) @PrimaryGeneratedColumn('uuid')