diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-object-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-object-metadata.command.ts new file mode 100644 index 00000000000..1bcf8503fbf --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-object-metadata.command.ts @@ -0,0 +1,232 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { isDefined } from 'twenty-shared/utils'; +import { WorkspaceActivationStatus } from 'twenty-shared/workspace'; +import { IsNull, Repository } from 'typeorm'; +import { v4 } from 'uuid'; + +import { + RunOnWorkspaceArgs, + WorkspacesMigrationCommandRunner, +} 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 { 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 { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util'; +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'; + +type CustomObjectMetadata = { + objectMetadataEntity: ObjectMetadataEntity; + fromStandard: boolean; +}; + +type StandardObjectMetadata = { + objectMetadataEntity: ObjectMetadataEntity; + universalIdentifier: string; +}; + +type AllWarnings = 'unknown_standard_id'; + +type ObjectMetadataWarning = { + objectMetadataEntity: ObjectMetadataEntity; + warning: AllWarnings; +}; + +type AllExceptions = 'existing_universal_id_mismatch'; + +type ObjectMetadataException = { + objectMetadataEntity: ObjectMetadataEntity; + exception: AllExceptions; +}; + +@Command({ + name: 'upgrade:1-16:identify-object-metadata', + description: 'Identify standard object metadata', +}) +export class IdentifyObjectMetadataCommand extends WorkspacesMigrationCommandRunner { + constructor( + @InjectRepository(WorkspaceEntity) + protected readonly workspaceRepository: Repository, + @InjectRepository(ObjectMetadataEntity) + private readonly objectMetadataRepository: Repository, + protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, + protected readonly dataSourceService: DataSourceService, + protected readonly applicationService: ApplicationService, + protected readonly workspaceCacheService: WorkspaceCacheService, + ) { + super(workspaceRepository, twentyORMGlobalManager, dataSourceService, [ + WorkspaceActivationStatus.ACTIVE, + WorkspaceActivationStatus.SUSPENDED, + WorkspaceActivationStatus.ONGOING_CREATION, + ]); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + this.logger.log( + `Running identify standard object metadata for workspace ${workspaceId}`, + ); + + const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { workspaceId }, + ); + + const allObjectMetadataEntities = await this.objectMetadataRepository.find({ + select: { + id: true, + universalIdentifier: true, + applicationId: true, + nameSingular: true, + standardId: true, + isCustom: true, + }, + where: { + workspaceId, + applicationId: IsNull(), + }, + }); + + const customObjectMetadataEntities: CustomObjectMetadata[] = []; + const standardObjectMetadataEntities: StandardObjectMetadata[] = []; + const warnings: ObjectMetadataWarning[] = []; + const exceptions: ObjectMetadataException[] = []; + + for (const objectMetadataEntity of allObjectMetadataEntities) { + const isStandardMetadataResult = isStandardMetadata(objectMetadataEntity); + + if (!isStandardMetadataResult) { + customObjectMetadataEntities.push({ + objectMetadataEntity, + fromStandard: false, + }); + + continue; + } + + const objectConfig = + STANDARD_OBJECTS[ + objectMetadataEntity.nameSingular as keyof typeof STANDARD_OBJECTS + ]; + const universalIdentifier = objectConfig?.universalIdentifier; + + if (!isDefined(universalIdentifier)) { + warnings.push({ + objectMetadataEntity, + warning: 'unknown_standard_id', + }); + customObjectMetadataEntities.push({ + objectMetadataEntity, + fromStandard: true, + }); + continue; + } + + if ( + isDefined(objectMetadataEntity.universalIdentifier) && + objectMetadataEntity.universalIdentifier !== universalIdentifier + ) { + exceptions.push({ + objectMetadataEntity, + exception: 'existing_universal_id_mismatch', + }); + continue; + } + + standardObjectMetadataEntities.push({ + objectMetadataEntity, + universalIdentifier: + objectMetadataEntity.universalIdentifier ?? universalIdentifier, + }); + } + + const totalUpdates = + customObjectMetadataEntities.length + + standardObjectMetadataEntities.length; + + if (warnings.length > 0) { + this.logger.warn( + `Found ${warnings.length} warning(s) while processing object metadata for workspace ${workspaceId}. These objects will become custom.`, + ); + + for (const { objectMetadataEntity, warning } of warnings) { + this.logger.warn( + `Warning for object "${objectMetadataEntity.nameSingular}" (id=${objectMetadataEntity.id} standardId=${objectMetadataEntity.standardId}): ${warning}`, + ); + } + } + + if (exceptions.length > 0) { + this.logger.error( + `Found ${exceptions.length} exception(s) while processing object metadata for workspace ${workspaceId}. No updates will be applied.`, + ); + + for (const { objectMetadataEntity, exception } of exceptions) { + this.logger.error( + `Exception for object "${objectMetadataEntity.nameSingular}" (id=${objectMetadataEntity.id} standardId=${objectMetadataEntity.standardId}): ${exception}`, + ); + } + + throw new Error( + `Aborting migration for workspace ${workspaceId} due to ${exceptions.length} exception(s). See logs above for details.`, + ); + } + + this.logger.log( + `Successfully validated ${totalUpdates}/${allObjectMetadataEntities.length} object metadata update(s) for workspace ${workspaceId} (${customObjectMetadataEntities.length} custom, ${standardObjectMetadataEntities.length} standard)`, + ); + + if (!options.dryRun) { + const customUpdates = customObjectMetadataEntities.map( + ({ objectMetadataEntity }) => ({ + id: objectMetadataEntity.id, + universalIdentifier: objectMetadataEntity.universalIdentifier ?? v4(), + applicationId: workspaceCustomFlatApplication.id, + }), + ); + + const standardUpdates = standardObjectMetadataEntities.map( + ({ objectMetadataEntity, universalIdentifier }) => ({ + id: objectMetadataEntity.id, + universalIdentifier, + applicationId: twentyStandardFlatApplication.id, + }), + ); + + await this.objectMetadataRepository.save([ + ...customUpdates, + ...standardUpdates, + ]); + + const relatedMetadataNames = + getMetadataRelatedMetadataNames('objectMetadata'); + const cacheKeysToInvalidate = relatedMetadataNames.map( + getMetadataFlatEntityMapsKey, + ); + + this.logger.log( + `Invalidating caches: ${cacheKeysToInvalidate.join(' ')}`, + ); + await this.workspaceCacheService.invalidateAndRecompute( + workspaceId, + cacheKeysToInvalidate, + ); + + this.logger.log( + `Applied ${totalUpdates} object metadata update(s) for workspace ${workspaceId}`, + ); + } else { + this.logger.log( + `Dry run: would apply ${totalUpdates} object metadata update(s) for workspace ${workspaceId}`, + ); + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-object-metadata-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-object-metadata-universal-identifier-and-application-id-not-nullable-migration.command.ts new file mode 100644 index 00000000000..1eb4c44717f --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-object-metadata-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 { makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768212224801-makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable.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-object-metadata-universal-identifier-and-application-id-not-nullable-migration', + description: + 'Make universalIdentifier and applicationId columns NOT NULL on objectMetadata table', +}) +export class MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand 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 MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', + ); + + return; + } + + if (options.dryRun) { + return; + } + + const queryRunner = this.coreDataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + await makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableQueries( + queryRunner, + ); + + await queryRunner.commitTransaction(); + this.logger.log( + 'Successfully run MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', + ); + this.hasRunOnce = true; + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.log( + `Roll backing MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${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 f173f08a2a8..79dd43ddab8 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 @@ -4,13 +4,16 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { BackfillOpportunityOwnerFieldCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-backfill-opportunity-owner-field.command'; import { BackfillStandardPageLayoutsCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-backfill-standard-page-layouts.command'; 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 { 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 { 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'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.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'; import { TwentyStandardApplicationModule } from 'src/engine/workspace-manager/twenty-standard-application/twenty-standard-application.module'; @@ -18,7 +21,11 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace @Module({ imports: [ - TypeOrmModule.forFeature([WorkspaceEntity, FieldMetadataEntity]), + TypeOrmModule.forFeature([ + WorkspaceEntity, + FieldMetadataEntity, + ObjectMetadataEntity, + ]), DataSourceModule, WorkspaceCacheModule, FieldMetadataModule, @@ -32,14 +39,18 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace BackfillOpportunityOwnerFieldCommand, BackfillStandardPageLayoutsCommand, IdentifyFieldMetadataCommand, + IdentifyObjectMetadataCommand, MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, + MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, ], exports: [ UpdateTaskOnDeleteActionCommand, BackfillOpportunityOwnerFieldCommand, BackfillStandardPageLayoutsCommand, IdentifyFieldMetadataCommand, + IdentifyObjectMetadataCommand, MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, + MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, ], }) 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 c27908bc2cb..125aa811b55 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 @@ -25,7 +25,9 @@ import { MigratePageLayoutWidgetConfigurationCommand } from 'src/database/comman import { BackfillOpportunityOwnerFieldCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-backfill-opportunity-owner-field.command'; import { BackfillStandardPageLayoutsCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-backfill-standard-page-layouts.command'; 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 { 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 { 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'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -70,7 +72,9 @@ export class UpgradeCommand extends UpgradeCommandRunner { protected readonly backfillOpportunityOwnerFieldCommand: BackfillOpportunityOwnerFieldCommand, protected readonly backfillStandardPageLayoutsCommand: BackfillStandardPageLayoutsCommand, protected readonly identifyFieldMetadataCommand: IdentifyFieldMetadataCommand, + protected readonly identifyObjectMetadataCommand: IdentifyObjectMetadataCommand, protected readonly makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, + protected readonly makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, ) { super( workspaceRepository, @@ -109,8 +113,11 @@ export class UpgradeCommand extends UpgradeCommandRunner { this.backfillOpportunityOwnerFieldCommand, this.backfillStandardPageLayoutsCommand, this.identifyFieldMetadataCommand, + this.identifyObjectMetadataCommand, this .makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, + this + .makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, ]; this.allCommands = { diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1768212224801-makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1768212224801-makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable.ts new file mode 100644 index 00000000000..13bf00e012f --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1768212224801-makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable.ts @@ -0,0 +1,64 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm'; + +import { makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768212224801-makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable.util'; + +export class MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable1768212224801 + implements MigrationInterface +{ + name = + 'MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable1768212224801'; + + public async up(queryRunner: QueryRunner): Promise { + const savepointName = + 'sp_make_object_metadata_universal_identifier_and_application_id_not_nullable'; + + try { + await queryRunner.query(`SAVEPOINT ${savepointName}`); + + await makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableQueries( + 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 MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable1768212224801', + rollbackError, + ); + throw rollbackError; + } + + // eslint-disable-next-line no-console + console.error( + 'Swallowing MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable1768212224801 error', + e, + ); + } + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."objectMetadata" DROP CONSTRAINT "FK_71a7af5a5c916f0b96f358f25f7"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_3a00d35710f4227ded320fd96d"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."objectMetadata" ALTER COLUMN "applicationId" DROP NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "core"."objectMetadata" ALTER COLUMN "universalIdentifier" DROP NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_3a00d35710f4227ded320fd96d" ON "core"."objectMetadata" ("workspaceId", "universalIdentifier") `, + ); + await queryRunner.query( + `ALTER TABLE "core"."objectMetadata" ADD CONSTRAINT "FK_71a7af5a5c916f0b96f358f25f7" 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/1768212224801-makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable.util.ts b/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768212224801-makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable.util.ts new file mode 100644 index 00000000000..53529d159f8 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/utils/1768212224801-makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullable.util.ts @@ -0,0 +1,23 @@ +import { type QueryRunner } from 'typeorm'; + +export const makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableQueries = + async (queryRunner: QueryRunner): Promise => { + await queryRunner.query( + `ALTER TABLE "core"."objectMetadata" DROP CONSTRAINT "FK_71a7af5a5c916f0b96f358f25f7"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_3a00d35710f4227ded320fd96d"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."objectMetadata" ALTER COLUMN "universalIdentifier" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "core"."objectMetadata" ALTER COLUMN "applicationId" SET NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_3a00d35710f4227ded320fd96d" ON "core"."objectMetadata" ("workspaceId", "universalIdentifier") `, + ); + await queryRunner.query( + `ALTER TABLE "core"."objectMetadata" ADD CONSTRAINT "FK_71a7af5a5c916f0b96f358f25f7" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + }; diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/__mocks__/mockPersonObjectMetadata.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/__mocks__/mockPersonObjectMetadata.ts index ac15fa640f4..5d14a17ec31 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/__mocks__/mockPersonObjectMetadata.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/__mocks__/mockPersonObjectMetadata.ts @@ -156,7 +156,7 @@ export const mockPersonFlatObjectMetadata = ( indexMetadataIds: [], fieldMetadataIds: mockFieldMetadatas.map((field) => field.id), viewIds: [], - applicationId: null, + applicationId: 'test-application-id', isLabelSyncedWithName: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), diff --git a/packages/twenty-server/src/engine/core-modules/event-emitter/utils/__tests__/object-record-changed-values.spec.ts b/packages/twenty-server/src/engine/core-modules/event-emitter/utils/__tests__/object-record-changed-values.spec.ts index c23294eb51b..3b6d1553f98 100644 --- a/packages/twenty-server/src/engine/core-modules/event-emitter/utils/__tests__/object-record-changed-values.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/event-emitter/utils/__tests__/object-record-changed-values.spec.ts @@ -22,7 +22,7 @@ const mockObjectMetadata = { indexMetadataIds: [], fieldMetadataIds: [], viewIds: [], - applicationId: null, + applicationId: 'test-application-id', isLabelSyncedWithName: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util.ts index f89b648e04a..47d6fdf812b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util.ts @@ -18,6 +18,11 @@ export const fromFieldMetadataEntityToFlatFieldMetadata = < return { ...fieldMetadataWithoutRelations, + // TODO remove once MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand has been run once + universalIdentifier: + fieldMetadataWithoutRelations.universalIdentifier ?? + fieldMetadataWithoutRelations.standardId ?? + fieldMetadataWithoutRelations.id, createdAt: fieldMetadataWithoutRelations.createdAt.toISOString(), updatedAt: fieldMetadataWithoutRelations.updatedAt.toISOString(), kanbanAggregateOperationViewIds: diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock.ts index 5dad29c51c6..09009340c66 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock.ts @@ -34,7 +34,7 @@ export const getFlatObjectMetadataMock = ( nameSingular: 'defaultflatObjectMetadataNameSingular', shortcut: 'shortcut', standardId: null, - applicationId: null, + applicationId: faker.string.uuid(), standardOverrides: null, targetTableName: '', workspaceId: faker.string.uuid(), diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util.ts index b655e233949..db5e24195e1 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util.ts @@ -14,6 +14,11 @@ export const fromObjectMetadataEntityToFlatObjectMetadata = ( return { ...objectMetadataEntityWithoutRelations, + // TODO remove once MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand has been run once + universalIdentifier: + objectMetadataEntityWithoutRelations.universalIdentifier ?? + objectMetadataEntityWithoutRelations.standardId ?? + objectMetadataEntityWithoutRelations.id, createdAt: objectMetadataEntity.createdAt.toISOString(), updatedAt: objectMetadataEntity.updatedAt.toISOString(), viewIds: objectMetadataEntity.views.map((viewEntity) => viewEntity.id), @@ -23,8 +28,5 @@ export const fromObjectMetadataEntityToFlatObjectMetadata = ( fieldMetadataIds: objectMetadataEntity.fields.map( (fieldEntity) => fieldEntity.id, ), - universalIdentifier: - objectMetadataEntityWithoutRelations.standardId ?? - objectMetadataEntityWithoutRelations.id, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.entity.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.entity.ts index b9b5b696024..a65b1229cfe 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.entity.ts @@ -18,7 +18,7 @@ import { type ObjectStandardOverridesDTO } from 'src/engine/metadata-modules/obj import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity'; import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity'; 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('objectMetadata') @Unique('IDX_OBJECT_METADATA_NAME_SINGULAR_WORKSPACE_ID_UNIQUE', [ @@ -30,7 +30,7 @@ import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-enti 'workspaceId', ]) export class ObjectMetadataEntity - extends SyncableEntity + extends SyncableEntityRequired implements Required { @PrimaryGeneratedColumn('uuid') diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts index 262fc9859e3..194ad8f61a4 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts @@ -117,7 +117,7 @@ describe('WorkspaceEntityManager', () => { shortcut: null, standardId: null, standardOverrides: null, - applicationId: null, + applicationId: 'test-application-id', isLabelSyncedWithName: false, isUIReadOnly: false, duplicateCriteria: null, diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/get-column-name-to-field-metadata-id.util.spec.ts b/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/get-column-name-to-field-metadata-id.util.spec.ts index c32efd73e5e..590754a4c9a 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/get-column-name-to-field-metadata-id.util.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/get-column-name-to-field-metadata-id.util.spec.ts @@ -28,7 +28,7 @@ describe('getColumnNameToFieldMetadataIdMap', () => { indexMetadataIds: [], fieldMetadataIds, viewIds: [], - applicationId: null, + applicationId: 'test-application-id', isLabelSyncedWithName: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/get-field-metadata-id-to-column-names-map.util.spec.ts b/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/get-field-metadata-id-to-column-names-map.util.spec.ts index d40ea7500a1..20dfcf1a018 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/get-field-metadata-id-to-column-names-map.util.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/get-field-metadata-id-to-column-names-map.util.spec.ts @@ -28,7 +28,7 @@ describe('getFieldMetadataIdToColumnNamesMap', () => { indexMetadataIds: [], fieldMetadataIds, viewIds: [], - applicationId: null, + applicationId: 'test-application-id', isLabelSyncedWithName: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(),