diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-filter-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-filter-metadata.command.ts new file mode 100644 index 0000000000..b5c51acf7d --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-filter-metadata.command.ts @@ -0,0 +1,221 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { isDefined } from 'twenty-shared/utils'; +import { IsNull, Repository } from 'typeorm'; +import { v4 } from 'uuid'; + +import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; +import { ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; +import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; +import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; +import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util'; +import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; +import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type'; +import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; +import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity'; +import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; +import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; + +@Command({ + name: 'upgrade:1-16:identify-view-filter-metadata', + description: 'Identify standard view filter metadata', +}) +export class IdentifyViewFilterMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + constructor( + @InjectRepository(WorkspaceEntity) + protected readonly workspaceRepository: Repository, + @InjectRepository(ViewFilterEntity) + private readonly viewFilterRepository: Repository, + protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, + protected readonly dataSourceService: DataSourceService, + protected readonly applicationService: ApplicationService, + protected readonly workspaceCacheService: WorkspaceCacheService, + private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService, + ) { + super(workspaceRepository, twentyORMGlobalManager, dataSourceService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + this.logger.log( + `Running identify standard view filter metadata for workspace ${workspaceId}`, + ); + + const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { workspaceId }, + ); + + const { flatFieldMetadataMaps, flatViewMaps, flatViewFilterMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: [ + 'flatFieldMetadataMaps', + 'flatViewMaps', + 'flatViewFilterMaps', + ], + }, + ); + + await this.identifyStandardViewFilter({ + flatFieldMetadataMaps, + flatViewMaps, + flatViewFilterMaps, + twentyStandardApplicationId: twentyStandardFlatApplication.id, + dryRun: options.dryRun ?? false, + }); + + await this.identifyCustomViewFilters({ + workspaceId, + workspaceCustomApplicationId: workspaceCustomFlatApplication.id, + dryRun: options.dryRun ?? false, + }); + + const relatedMetadataNames = getMetadataRelatedMetadataNames('viewFilter'); + const relatedCacheKeysToInvalidate = relatedMetadataNames.map( + getMetadataFlatEntityMapsKey, + ); + + this.logger.log( + `Invalidating caches: ${relatedCacheKeysToInvalidate.join(' ')}`, + ); + if (!options.dryRun) { + await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ + 'flatViewFilterMaps', + ...relatedCacheKeysToInvalidate, + ]); + } + } + + private async identifyStandardViewFilter({ + flatFieldMetadataMaps, + flatViewMaps, + flatViewFilterMaps, + twentyStandardApplicationId, + dryRun, + }: { + flatFieldMetadataMaps: FlatEntityMaps; + flatViewMaps: FlatEntityMaps; + flatViewFilterMaps: FlatEntityMaps; + twentyStandardApplicationId: string; + dryRun: boolean; + }): Promise { + const flatView = findFlatEntityByUniversalIdentifier({ + flatEntityMaps: flatViewMaps, + universalIdentifier: + STANDARD_OBJECTS.task.views.assignedToMe.universalIdentifier, + }); + + if (!isDefined(flatView)) { + this.logger.warn( + `Standard view "assignedToMe" not found for task object, skipping standard view filter identification`, + ); + + return; + } + + const flatFieldMetadata = findFlatEntityByUniversalIdentifier({ + flatEntityMaps: flatFieldMetadataMaps, + universalIdentifier: + STANDARD_OBJECTS.task.fields.assignee.universalIdentifier, + }); + + if (!isDefined(flatFieldMetadata)) { + this.logger.warn( + `Field "assignee" not found for task object, skipping standard view filter identification`, + ); + + return; + } + + const relatedFlatViewFilters = + findManyFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityIds: flatView.viewFilterIds, + flatEntityMaps: flatViewFilterMaps, + }); + + const matchingFlatViewFilter = relatedFlatViewFilters.find( + (viewFilter) => viewFilter.fieldMetadataId === flatFieldMetadata.id, + ); + + if (!isDefined(matchingFlatViewFilter)) { + this.logger.warn( + `Standard view filter "assigneeIsMe" not found for view "assignedToMe" of task object, skipping`, + ); + + return; + } + + if (isDefined(matchingFlatViewFilter.applicationId)) { + this.logger.warn( + `Standard view filter "assigneeIsMe" already has applicationId set, skipping`, + ); + + return; + } + + this.logger.log( + ` - Standard view filter "assigneeIsMe" on view "assignedToMe" of object "task" (id=${matchingFlatViewFilter.id}) -> universalIdentifier=${STANDARD_OBJECTS.task.views.assignedToMe.viewFilters.assigneeIsMe.universalIdentifier}`, + ); + + if (!dryRun) { + await this.viewFilterRepository.save({ + id: matchingFlatViewFilter.id, + universalIdentifier: + STANDARD_OBJECTS.task.views.assignedToMe.viewFilters.assigneeIsMe + .universalIdentifier, + applicationId: twentyStandardApplicationId, + }); + } + } + + private async identifyCustomViewFilters({ + workspaceId, + workspaceCustomApplicationId, + dryRun, + }: { + workspaceId: string; + workspaceCustomApplicationId: string; + dryRun: boolean; + }): Promise { + const remainingCustomViewFilters = await this.viewFilterRepository.find({ + select: { + id: true, + universalIdentifier: true, + applicationId: true, + }, + where: { + workspaceId, + applicationId: IsNull(), + }, + withDeleted: true, + }); + + const customUpdates = remainingCustomViewFilters.map( + (viewFilterEntity) => ({ + id: viewFilterEntity.id, + universalIdentifier: viewFilterEntity.universalIdentifier ?? v4(), + applicationId: workspaceCustomApplicationId, + }), + ); + + this.logger.log( + `Found ${customUpdates.length} custom view filter(s) to update for workspace ${workspaceId}`, + ); + + if (!dryRun) { + await this.viewFilterRepository.save(customUpdates); + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-view-filter-universal-identifier-and-application-id-not-nullable-migration.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-view-filter-universal-identifier-and-application-id-not-nullable-migration.command.ts new file mode 100644 index 0000000000..0495fb09cb --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-view-filter-universal-identifier-and-application-id-not-nullable-migration.command.ts @@ -0,0 +1,71 @@ +import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { DataSource, Repository } from 'typeorm'; + +import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; +import { makeViewFilterUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768213174273-makeViewFilterUniversalIdentifierAndApplicationIdNotNullable.util'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; + +@Command({ + name: 'upgrade:1-16:make-view-filter-universal-identifier-and-application-id-not-nullable-migration', + description: + 'Make universalIdentifier and applicationId columns NOT NULL on viewFilter table', +}) +export class MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + private hasRunOnce = false; + + constructor( + @InjectRepository(WorkspaceEntity) + protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, + protected readonly dataSourceService: DataSourceService, + @InjectDataSource() + private readonly coreDataSource: DataSource, + ) { + super(workspaceRepository, twentyORMGlobalManager, dataSourceService); + } + + override async runOnWorkspace({ + options, + }: RunOnWorkspaceArgs): Promise { + if (this.hasRunOnce) { + this.logger.warn( + 'Skipping has already been run once MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', + ); + + return; + } + + if (options.dryRun) { + return; + } + + const queryRunner = this.coreDataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + await makeViewFilterUniversalIdentifierAndApplicationIdNotNullableQueries( + queryRunner, + ); + + await queryRunner.commitTransaction(); + this.logger.log( + 'Successfully run MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', + ); + this.hasRunOnce = true; + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error( + `Rolling back MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`, + ); + } finally { + await queryRunner.release(); + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-upgrade-version-command.module.ts index 0c4794afee..28d18c0d7c 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-upgrade-version-command.module.ts @@ -6,10 +6,12 @@ import { BackfillStandardPageLayoutsCommand } from 'src/database/commands/upgrad import { IdentifyFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-field-metadata.command'; import { IdentifyObjectMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-object-metadata.command'; import { IdentifyViewFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-field-metadata.command'; +import { IdentifyViewFilterMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-filter-metadata.command'; import { IdentifyViewMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-metadata.command'; import { MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-field-metadata-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-object-metadata-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-field-universal-identifier-and-application-id-not-nullable-migration.command'; +import { MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-filter-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-universal-identifier-and-application-id-not-nullable-migration.command'; import { UpdateTaskOnDeleteActionCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-update-task-on-delete-action.command'; import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; @@ -20,6 +22,7 @@ import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/ import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity'; +import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity'; import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; import { GlobalWorkspaceDataSourceModule } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource.module'; import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; @@ -34,6 +37,7 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace ObjectMetadataEntity, ViewEntity, ViewFieldEntity, + ViewFilterEntity, ]), DataSourceModule, WorkspaceCacheModule, @@ -52,10 +56,12 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace IdentifyObjectMetadataCommand, IdentifyViewMetadataCommand, IdentifyViewFieldMetadataCommand, + IdentifyViewFilterMetadataCommand, MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, + MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, ], exports: [ UpdateTaskOnDeleteActionCommand, @@ -65,10 +71,12 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace IdentifyObjectMetadataCommand, IdentifyViewMetadataCommand, IdentifyViewFieldMetadataCommand, + IdentifyViewFilterMetadataCommand, MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, + MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, ], }) export class V1_16_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts index c1975956bd..2f7567877b 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts @@ -27,10 +27,12 @@ import { BackfillStandardPageLayoutsCommand } from 'src/database/commands/upgrad import { IdentifyFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-field-metadata.command'; import { IdentifyObjectMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-object-metadata.command'; import { IdentifyViewFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-field-metadata.command'; +import { IdentifyViewFilterMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-filter-metadata.command'; import { IdentifyViewMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-metadata.command'; import { MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-field-metadata-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-object-metadata-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-field-universal-identifier-and-application-id-not-nullable-migration.command'; +import { MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-filter-universal-identifier-and-application-id-not-nullable-migration.command'; import { MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-universal-identifier-and-application-id-not-nullable-migration.command'; import { UpdateTaskOnDeleteActionCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-update-task-on-delete-action.command'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; @@ -79,10 +81,12 @@ export class UpgradeCommand extends UpgradeCommandRunner { protected readonly identifyObjectMetadataCommand: IdentifyObjectMetadataCommand, protected readonly identifyViewMetadataCommand: IdentifyViewMetadataCommand, protected readonly identifyViewFieldMetadataCommand: IdentifyViewFieldMetadataCommand, + protected readonly identifyViewFilterMetadataCommand: IdentifyViewFilterMetadataCommand, protected readonly makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, protected readonly makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, protected readonly makeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, protected readonly makeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, + protected readonly makeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, ) { super( workspaceRepository, @@ -124,6 +128,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { this.identifyObjectMetadataCommand, this.identifyViewMetadataCommand, this.identifyViewFieldMetadataCommand, + this.identifyViewFilterMetadataCommand, this .makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, this @@ -132,6 +137,8 @@ export class UpgradeCommand extends UpgradeCommandRunner { .makeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, this .makeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, + this + .makeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, ]; this.allCommands = { diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1768213174273-makeViewFilterUniversalIdentifierAndApplicationIdNotNullable.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1768213174273-makeViewFilterUniversalIdentifierAndApplicationIdNotNullable.ts new file mode 100644 index 0000000000..ff4b6d9cc7 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1768213174273-makeViewFilterUniversalIdentifierAndApplicationIdNotNullable.ts @@ -0,0 +1,64 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm'; + +import { makeViewFilterUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768213174273-makeViewFilterUniversalIdentifierAndApplicationIdNotNullable.util'; + +export class MakeViewFilterUniversalIdentifierAndApplicationIdNotNullable1768213174273 + implements MigrationInterface +{ + name = + 'MakeViewFilterUniversalIdentifierAndApplicationIdNotNullable1768213174273'; + + public async up(queryRunner: QueryRunner): Promise { + const savepointName = + 'sp_make_view_filter_universal_identifier_and_application_id_not_nullable'; + + try { + await queryRunner.query(`SAVEPOINT ${savepointName}`); + + await makeViewFilterUniversalIdentifierAndApplicationIdNotNullableQueries( + queryRunner, + ); + + await queryRunner.query(`RELEASE SAVEPOINT ${savepointName}`); + } catch (e) { + try { + await queryRunner.query(`ROLLBACK TO SAVEPOINT ${savepointName}`); + await queryRunner.query(`RELEASE SAVEPOINT ${savepointName}`); + } catch (rollbackError) { + // eslint-disable-next-line no-console + console.error( + 'Failed to rollback to savepoint in MakeViewFilterUniversalIdentifierAndApplicationIdNotNullable1768213174273', + rollbackError, + ); + throw rollbackError; + } + + // eslint-disable-next-line no-console + console.error( + 'Swallowing MakeViewFilterUniversalIdentifierAndApplicationIdNotNullable1768213174273 error', + e, + ); + } + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."viewFilter" DROP CONSTRAINT "FK_d5651cf33fa56a47cd262a3fb2c"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_cd4588bfc9ad73345b3953a039"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."viewFilter" ALTER COLUMN "applicationId" DROP NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "core"."viewFilter" ALTER COLUMN "universalIdentifier" DROP NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_cd4588bfc9ad73345b3953a039" ON "core"."viewFilter" ("workspaceId", "universalIdentifier") `, + ); + await queryRunner.query( + `ALTER TABLE "core"."viewFilter" ADD CONSTRAINT "FK_d5651cf33fa56a47cd262a3fb2c" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } +} diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768213174273-makeViewFilterUniversalIdentifierAndApplicationIdNotNullable.util.ts b/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768213174273-makeViewFilterUniversalIdentifierAndApplicationIdNotNullable.util.ts new file mode 100644 index 0000000000..2e84c43f5a --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768213174273-makeViewFilterUniversalIdentifierAndApplicationIdNotNullable.util.ts @@ -0,0 +1,23 @@ +import { type QueryRunner } from 'typeorm'; + +export const makeViewFilterUniversalIdentifierAndApplicationIdNotNullableQueries = + async (queryRunner: QueryRunner): Promise => { + await queryRunner.query( + `ALTER TABLE "core"."viewFilter" DROP CONSTRAINT "FK_d5651cf33fa56a47cd262a3fb2c"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_cd4588bfc9ad73345b3953a039"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."viewFilter" ALTER COLUMN "universalIdentifier" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "core"."viewFilter" ALTER COLUMN "applicationId" SET NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_cd4588bfc9ad73345b3953a039" ON "core"."viewFilter" ("workspaceId", "universalIdentifier") `, + ); + await queryRunner.query( + `ALTER TABLE "core"."viewFilter" ADD CONSTRAINT "FK_d5651cf33fa56a47cd262a3fb2c" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter/entities/view-filter.entity.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter/entities/view-filter.entity.ts index 0059dce363..05412d1703 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-filter/entities/view-filter.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter/entities/view-filter.entity.ts @@ -16,7 +16,7 @@ import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/ import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; import { type ViewFilterValue } from 'src/engine/metadata-modules/view-filter/types/view-filter-value.type'; import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; -import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface'; +import { SyncableEntityRequired } from 'src/engine/workspace-manager/types/syncable-entity-required.interface'; @Entity({ name: 'viewFilter', schema: 'core' }) @Index('IDX_VIEW_FILTER_WORKSPACE_ID_VIEW_ID', ['workspaceId', 'viewId']) @@ -25,7 +25,7 @@ import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-enti }) @Index('IDX_VIEW_FILTER_FIELD_METADATA_ID', ['fieldMetadataId']) export class ViewFilterEntity - extends SyncableEntity + extends SyncableEntityRequired implements Required { @PrimaryGeneratedColumn('uuid')