diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-backfill-page-layout-universal-identifiers.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-backfill-page-layout-universal-identifiers.command.ts deleted file mode 100644 index 68085dab7c..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-backfill-page-layout-universal-identifiers.command.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -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/services/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 { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity'; -import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity'; -import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.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'; - -@Command({ - name: 'upgrade:1-13:backfill-page-layout-universal-identifiers', - description: - 'Backfill universalIdentifier and applicationId for pageLayout, pageLayoutTab and pageLayoutWidget', -}) -export class BackfillPageLayoutUniversalIdentifiersCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - @InjectRepository(PageLayoutWidgetEntity) - private readonly pageLayoutWidgetRepository: Repository, - @InjectRepository(PageLayoutTabEntity) - private readonly pageLayoutTabRepository: Repository, - @InjectRepository(PageLayoutEntity) - private readonly pageLayoutRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - private readonly applicationService: ApplicationService, - private readonly workspaceCacheService: WorkspaceCacheService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Starting backfill of universalIdentifier and applicationId for workspace ${workspaceId}`, - ); - - const { workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { - workspaceId, - }, - ); - - const applicationId = workspaceCustomFlatApplication.id; - - const pageLayoutsToUpdate = await this.pageLayoutRepository.find({ - where: [ - { workspaceId, universalIdentifier: IsNull() }, - { workspaceId, applicationId: IsNull() }, - ], - select: ['id', 'workspaceId'], - }); - - this.logger.log( - `Found ${pageLayoutsToUpdate.length} pageLayout records to backfill`, - ); - - for (const pageLayout of pageLayoutsToUpdate) { - const universalIdentifier = v4(); - - if (options.dryRun) { - this.logger.log( - `[DRY RUN] Would update pageLayout ${pageLayout.id} with universalIdentifier ${universalIdentifier} and applicationId ${applicationId}`, - ); - } else { - await this.pageLayoutRepository.update( - { id: pageLayout.id }, - { - universalIdentifier, - applicationId, - }, - ); - } - - this.logger.log( - `Updated pageLayout ${pageLayout.id} with universalIdentifier ${universalIdentifier}`, - ); - } - - const tabsToUpdate = await this.pageLayoutTabRepository.find({ - where: [ - { workspaceId, universalIdentifier: IsNull() }, - { workspaceId, applicationId: IsNull() }, - ], - select: ['id', 'workspaceId'], - }); - - this.logger.log( - `Found ${tabsToUpdate.length} pageLayoutTab records to backfill`, - ); - - for (const tab of tabsToUpdate) { - const universalIdentifier = v4(); - - if (options.dryRun) { - this.logger.log( - `[DRY RUN] Would update pageLayoutTab ${tab.id} with universalIdentifier ${universalIdentifier} and applicationId ${applicationId}`, - ); - } else { - await this.pageLayoutTabRepository.update( - { id: tab.id }, - { - universalIdentifier, - applicationId, - }, - ); - - this.logger.log( - `Updated pageLayoutTab ${tab.id} with universalIdentifier ${universalIdentifier}`, - ); - } - } - - const widgetsToUpdate = await this.pageLayoutWidgetRepository.find({ - where: [ - { workspaceId, universalIdentifier: IsNull() }, - { workspaceId, applicationId: IsNull() }, - ], - select: ['id', 'workspaceId'], - }); - - this.logger.log( - `Found ${widgetsToUpdate.length} pageLayoutWidget records to backfill`, - ); - - for (const widget of widgetsToUpdate) { - const universalIdentifier = v4(); - - if (options.dryRun) { - this.logger.log( - `[DRY RUN] Would update pageLayoutWidget ${widget.id} with universalIdentifier ${universalIdentifier} and applicationId ${applicationId}`, - ); - } else { - await this.pageLayoutWidgetRepository.update( - { id: widget.id }, - { - universalIdentifier, - applicationId, - }, - ); - - this.logger.log( - `Updated pageLayoutWidget ${widget.id} with universalIdentifier ${universalIdentifier}`, - ); - } - } - - this.logger.log( - `${options.dryRun ? '[DRY RUN] Would have ' : ''}Successfully backfilled ${pageLayoutsToUpdate.length} pageLayouts, ${tabsToUpdate.length} pageLayoutTabs and ${widgetsToUpdate.length} pageLayoutWidgets`, - ); - - if ( - !options.dryRun && - (pageLayoutsToUpdate.length > 0 || - tabsToUpdate.length > 0 || - widgetsToUpdate.length > 0) - ) { - this.logger.log( - `Invalidating and recomputing cache for workspace ${workspaceId}`, - ); - - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatPageLayoutMaps', - 'flatPageLayoutTabMaps', - 'flatPageLayoutWidgetMaps', - ]); - - this.logger.log(`Cache invalidated and recomputed successfully`); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-clean-empty-string-null-in-text-fields.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-clean-empty-string-null-in-text-fields.command.ts deleted file mode 100644 index 9d5d50df33..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-clean-empty-string-null-in-text-fields.command.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { FieldMetadataType } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; -import { 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 { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource'; -import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; -import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; -import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util'; - -@Command({ - name: 'upgrade:1-13:clean-empty-string-null-in-text-fields', - description: - 'Clean up empty string defaults in TEXT fields and convert them to NULL', -}) -export class CleanEmptyStringNullInTextFieldsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - protected readonly logger = new Logger( - CleanEmptyStringNullInTextFieldsCommand.name, - ); - - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, - @InjectRepository(ObjectMetadataEntity) - protected readonly objectMetadataRepository: Repository, - @InjectRepository(FieldMetadataEntity) - protected readonly fieldMetadataRepository: Repository, - protected readonly dataSourceService: DataSourceService, - ) { - super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - dataSource, - }: RunOnWorkspaceArgs): Promise { - const isDryRun = options.dryRun || false; - - if (!isDefined(dataSource)) { - throw new Error( - `Could not find data source for workspace ${workspaceId}, should never occur`, - ); - } - - const workspace = await this.workspaceRepository.findOne({ - where: { id: workspaceId }, - }); - - if (!isDefined(workspace)) { - throw new Error( - `Could not find workspace ${workspaceId}, should never occur`, - ); - } - - const schemaName = getWorkspaceSchemaName(workspaceId); - - if (isDryRun) { - this.logger.log('Dry run mode: No changes will be applied'); - } - - const objectMetadataItems = await this.objectMetadataRepository.find({ - where: { workspaceId }, - relations: ['fields'], - }); - - for (const objectMetadataItem of objectMetadataItems) { - try { - const tableName = computeObjectTargetTable(objectMetadataItem); - - if (!objectMetadataItem.isCustom) { - await this.cleanUpEmptyStringDefaultsInTextFieldsInStandardObjects( - objectMetadataItem, - tableName, - schemaName, - dataSource, - isDryRun, - ); - } - - if (objectMetadataItem.isCustom) { - await this.cleanUpEmptyStringDefaultsAndSetNullableInNameFieldInCustomObjects( - objectMetadataItem, - tableName, - schemaName, - dataSource, - isDryRun, - ); - } - } catch (error) { - this.logger.error( - `Could not cleanup ${objectMetadataItem.isCustom ? 'custom' : 'standard'} object ${objectMetadataItem.nameSingular} records for workspace ${workspaceId}`, - ); - this.logger.error(error); - } - } - } - - private async cleanUpEmptyStringDefaultsInTextFieldsInStandardObjects( - objectMetadataItem: ObjectMetadataEntity, - tableName: string, - schemaName: string, - dataSource: GlobalWorkspaceDataSource, - isDryRun: boolean, - ): Promise { - const textFields = objectMetadataItem.fields.filter( - (field) => - field.type === FieldMetadataType.TEXT && - field.isNullable === true && - field.defaultValue === null, - ); - - for (const field of textFields) { - this.logger.log( - `Checking field ${field.name} on standard object ${objectMetadataItem.nameSingular} (Table: ${tableName})`, - ); - - this.logger.log( - `Cleaning up empty string default for field ${field.name} on ${tableName}`, - ); - - if (!isDryRun) { - await dataSource.query( - `ALTER TABLE "${schemaName}"."${tableName}" ALTER COLUMN "${field.name}" DROP DEFAULT`, - [], - undefined, - { shouldBypassPermissionChecks: true }, - ); - - await dataSource.query( - `UPDATE "${schemaName}"."${tableName}" SET "${field.name}" = NULL WHERE "${field.name}" = ''`, - [], - undefined, - { shouldBypassPermissionChecks: true }, - ); - } - } - } - - private async cleanUpEmptyStringDefaultsAndSetNullableInNameFieldInCustomObjects( - objectMetadataItem: ObjectMetadataEntity, - tableName: string, - schemaName: string, - dataSource: GlobalWorkspaceDataSource, - isDryRun: boolean, - ): Promise { - const nameField = objectMetadataItem.fields.find( - (field) => - field.name === 'name' && - field.type === FieldMetadataType.TEXT && - field.defaultValue === "''", - ); - - if (!isDefined(nameField)) { - return; - } - - this.logger.log( - `Found "name" field with empty string default on ${objectMetadataItem.nameSingular} (Table: ${tableName})`, - ); - - if (!isDryRun) { - await dataSource.query( - `ALTER TABLE "${schemaName}"."${tableName}" ALTER COLUMN "name" DROP DEFAULT, ALTER COLUMN "name" DROP NOT NULL`, - [], - undefined, - { shouldBypassPermissionChecks: true }, - ); - - await dataSource.query( - `UPDATE "${schemaName}"."${tableName}" SET "name" = NULL WHERE "name" = ''`, - [], - undefined, - { shouldBypassPermissionChecks: true }, - ); - - await this.fieldMetadataRepository.update(nameField.id, { - defaultValue: null, - isNullable: true, - }); - - this.logger.log( - `Updated "name" field metadata and cleaned empty strings for ${objectMetadataItem.nameSingular}`, - ); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-deduplicate-role-targets.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-deduplicate-role-targets.command.ts deleted file mode 100644 index cbea2855d3..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-deduplicate-role-targets.command.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { In, type 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 { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity'; -import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; - -type DuplicateKey = 'apiKeyId' | 'agentId' | 'userWorkspaceId'; - -type DuplicateGroup = { - foreignKeyId: string; - duplicateKey: DuplicateKey; -}; - -@Command({ - name: 'upgrade:1-13:deduplicate-role-targets', - description: - 'Remove duplicate roleTargets keeping only the most recently updated one for each unique constraint', -}) -export class DeduplicateRoleTargetsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(RoleTargetEntity) - private readonly roleTargetRepository: Repository, - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - options, - workspaceId, - }: RunOnWorkspaceArgs): Promise { - const isDryRun = options.dryRun || false; - - const duplicateGroups = - await this.findDuplicateGroupsForWorkspace(workspaceId); - - if (duplicateGroups.length === 0) { - return; - } - - const idsToDelete: string[] = []; - - for (const duplicateGroup of duplicateGroups) { - const duplicateIds = await this.findDuplicateIdsToDelete( - workspaceId, - duplicateGroup, - ); - - idsToDelete.push(...duplicateIds); - } - - if (idsToDelete.length === 0) { - return; - } - - if (isDryRun) { - this.logger.log( - `DRY RUN: Would delete ${idsToDelete.length} duplicate roleTarget(s)`, - ); - - return; - } - - await this.roleTargetRepository.delete({ id: In(idsToDelete) }); - - this.logger.log(`Deleted ${idsToDelete.length} duplicate roleTarget(s)`); - } - - private async findDuplicateGroupsForWorkspace( - workspaceId: string, - ): Promise { - const duplicateGroups: DuplicateGroup[] = []; - - const duplicateKeys: DuplicateKey[] = [ - 'apiKeyId', - 'agentId', - 'userWorkspaceId', - ]; - - for (const duplicateKey of duplicateKeys) { - const duplicates = await this.roleTargetRepository - .createQueryBuilder('roleTarget') - .select([`roleTarget.${duplicateKey} AS "foreignKeyId"`]) - .where('roleTarget.workspaceId = :workspaceId', { workspaceId }) - .andWhere(`roleTarget.${duplicateKey} IS NOT NULL`) - .groupBy(`roleTarget.${duplicateKey}`) - .having('COUNT(*) > 1') - .getRawMany<{ foreignKeyId: string }>(); - - for (const duplicate of duplicates) { - duplicateGroups.push({ - foreignKeyId: duplicate.foreignKeyId, - duplicateKey, - }); - } - } - - return duplicateGroups; - } - - private async findDuplicateIdsToDelete( - workspaceId: string, - duplicateGroup: DuplicateGroup, - ): Promise { - const { foreignKeyId, duplicateKey } = duplicateGroup; - - const roleTargets = await this.roleTargetRepository.find({ - where: { - workspaceId, - [duplicateKey]: foreignKeyId, - }, - order: { - updatedAt: 'DESC', - }, - }); - - // Keep the first one (most recently updated), delete the rest - return roleTargets.slice(1).map((roleTarget) => roleTarget.id); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command.ts deleted file mode 100644 index dde6fd2a25..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command.ts +++ /dev/null @@ -1,380 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; -import { - type FieldMetadataComplexOption, - type FieldMetadataDefaultValue, - FieldMetadataType, - type FromTo, -} from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; -import { type Repository } from 'typeorm'; - -import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; -import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service'; -import { type MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type'; -import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; -import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util'; -import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; -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 { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-field-name.type'; -import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type'; -import { - CALENDAR_EVENT_STANDARD_FIELD_IDS, - MESSAGE_CHANNEL_STANDARD_FIELD_IDS, - MESSAGE_PARTICIPANT_STANDARD_FIELD_IDS, - MESSAGE_STANDARD_FIELD_IDS, - WORKSPACE_MEMBER_STANDARD_FIELD_IDS, -} from 'src/engine/workspace-manager/workspace-migration/constant/standard-field-ids'; - -const findStandardFlatObjectMetadataOrThrow = ({ - flatObjectMetadataMaps, - standardId, -}: { - flatObjectMetadataMaps: MetadataFlatEntityMaps<'objectMetadata'>; - standardId: string; -}): FlatObjectMetadata => { - const flatObjectMetadata = Object.values(flatObjectMetadataMaps.byId).find( - (flatObjectMetadata) => flatObjectMetadata?.standardId === standardId, - ); - - if (!isDefined(flatObjectMetadata)) { - throw new Error(`Could not find standard object ${standardId}`); - } - - return flatObjectMetadata; -}; - -const findStandardFlatFieldMetadatawOrThrow = ({ - flatFieldMetadataMaps, - flatObjectMetadata, - standardId, -}: { - standardId: string; - flatObjectMetadata: FlatObjectMetadata; - flatFieldMetadataMaps: MetadataFlatEntityMaps<'fieldMetadata'>; -}) => { - const objectFlatFieldMetadata = findManyFlatEntityByIdInFlatEntityMapsOrThrow( - { - flatEntityIds: flatObjectMetadata.fieldIds, - flatEntityMaps: flatFieldMetadataMaps, - }, - ); - - const flatFieldMetadata = objectFlatFieldMetadata.find( - (flatFieldMetadata) => flatFieldMetadata.standardId === standardId, - ); - - if (!isDefined(flatFieldMetadata)) { - throw new Error(`Could not find standard field ${standardId}`); - } - - return flatFieldMetadata; -}; - -const ENUM_FIELD_OPTIONS_TO_MUTATE = { - messageChannelMessageAssociation: { - fieldStandardId: MESSAGE_STANDARD_FIELD_IDS.direction, - field: 'direction', - defaultValue: { - fromDefaultValue: "'incoming'", - toDefaultValue: "'INCOMING'", - }, - options: [ - { - fromOption: { - value: 'incoming', - label: 'Incoming', - position: 0, - color: 'green', - }, - toOption: { - value: 'INCOMING', - label: 'Incoming', - position: 0, - color: 'green', - }, - }, - { - fromOption: { - value: 'outgoing', - label: 'Outgoing', - position: 1, - color: 'blue', - }, - toOption: { - value: 'OUTGOING', - label: 'Outgoing', - position: 1, - color: 'blue', - }, - }, - ], - }, - messageChannel: { - fieldStandardId: MESSAGE_CHANNEL_STANDARD_FIELD_IDS.type, - field: 'type', - defaultValue: { fromDefaultValue: "'email'", toDefaultValue: "'EMAIL'" }, - options: [ - { - fromOption: { - value: 'email', - label: 'Email', - position: 0, - color: 'green', - }, - toOption: { - value: 'EMAIL', - label: 'Email', - position: 0, - color: 'green', - }, - }, - { - fromOption: { value: 'sms', label: 'SMS', position: 1, color: 'blue' }, - toOption: { value: 'SMS', label: 'SMS', position: 1, color: 'blue' }, - }, - ], - }, - messageParticipant: { - fieldStandardId: MESSAGE_PARTICIPANT_STANDARD_FIELD_IDS.role, - field: 'role', - defaultValue: { fromDefaultValue: "'from'", toDefaultValue: "'FROM'" }, - options: [ - { - fromOption: { - value: 'from', - label: 'From', - position: 0, - color: 'green', - }, - toOption: { value: 'FROM', label: 'From', position: 0, color: 'green' }, - }, - { - fromOption: { value: 'to', label: 'To', position: 1, color: 'blue' }, - toOption: { value: 'TO', label: 'To', position: 1, color: 'blue' }, - }, - { - fromOption: { value: 'cc', label: 'Cc', position: 2, color: 'orange' }, - toOption: { value: 'CC', label: 'Cc', position: 2, color: 'orange' }, - }, - { - fromOption: { value: 'bcc', label: 'Bcc', position: 3, color: 'red' }, - toOption: { value: 'BCC', label: 'Bcc', position: 3, color: 'red' }, - }, - ], - }, - workspaceMember: { - fieldStandardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.numberFormat, - field: 'numberFormat', - defaultValue: { fromDefaultValue: "'system'", toDefaultValue: "'SYSTEM'" }, - options: [ - { - fromOption: { - value: 'SYSTEM', - label: 'System', - position: 0, - color: 'turquoise', - }, - toOption: { - value: 'SYSTEM', - label: 'System', - position: 0, - color: 'turquoise', - }, - }, - { - fromOption: { - value: 'COMMAS_AND_DOT', - label: 'Commas and dot (1,234.56)', - position: 1, - color: 'blue', - }, - toOption: { - value: 'COMMAS_AND_DOT', - label: 'Commas and dot', - position: 1, - color: 'blue', - }, - }, - { - fromOption: { - value: 'SPACES_AND_COMMA', - label: 'Spaces and comma (1 234,56)', - position: 2, - color: 'green', - }, - toOption: { - value: 'SPACES_AND_COMMA', - label: 'Spaces and comma', - position: 2, - color: 'green', - }, - }, - { - fromOption: { - value: 'DOTS_AND_COMMA', - label: 'Dots and comma (1.234,56)', - position: 3, - color: 'orange', - }, - toOption: { - value: 'DOTS_AND_COMMA', - label: 'Dots and comma', - position: 3, - color: 'orange', - }, - }, - { - fromOption: { - value: 'APOSTROPHE_AND_DOT', - label: "Apostrophe and dot (1'234.56)", - position: 4, - color: 'purple', - }, - toOption: { - value: 'APOSTROPHE_AND_DOT', - label: 'Apostrophe and dot', - position: 4, - color: 'purple', - }, - }, - ], - }, -} as const satisfies { - [P in AllStandardObjectName]?: { - fieldStandardId: string; - field: AllStandardObjectFieldName

; - options: FromTo[]; - defaultValue: FromTo; - }; -}; - -@Command({ - name: 'upgrade:1-13:migrate-standard-invalid-entities', - description: 'Migrate standard invalid entities', -}) -export class MigrateStandardInvalidEntitiesCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - private readonly workspaceCacheService: WorkspaceCacheService, - private readonly fieldMetadataService: FieldMetadataService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - options, - workspaceId, - }: RunOnWorkspaceArgs): Promise { - const isDryRun = options.dryRun || false; - - this.logger.log( - `MigrateStandardInvalidEntitiesCommand starting for workspace ${workspaceId}`, - ); - const { flatFieldMetadataMaps, flatObjectMetadataMaps } = - await this.workspaceCacheService.getOrRecompute(workspaceId, [ - 'flatFieldMetadataMaps', - 'flatObjectMetadataMaps', - ]); - - const flatCalendarEventObject = findStandardFlatObjectMetadataOrThrow({ - standardId: STANDARD_OBJECT_IDS.calendarEvent, - flatObjectMetadataMaps, - }); - - const iCalUidFieldId = findStandardFlatFieldMetadatawOrThrow({ - flatFieldMetadataMaps, - flatObjectMetadata: flatCalendarEventObject, - standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.iCalUid, - }).id; - - const allAffectedObjectNames = Object.keys( - ENUM_FIELD_OPTIONS_TO_MUTATE, - ).filter(isDefined) as (keyof typeof ENUM_FIELD_OPTIONS_TO_MUTATE)[]; - - const flatEnumFieldMetadatasToUpdate = allAffectedObjectNames.flatMap<{ - id: string; - options: FieldMetadataComplexOption[]; - defaultValue: FieldMetadataDefaultValue; - }>((objectName) => { - const { defaultValue, field, options, fieldStandardId } = - ENUM_FIELD_OPTIONS_TO_MUTATE[objectName]; - - const objectStandardId = STANDARD_OBJECT_IDS[objectName]; - - const flatObjectMetadata = findStandardFlatObjectMetadataOrThrow({ - flatObjectMetadataMaps, - standardId: objectStandardId, - }); - - const flatFieldMetadata = findStandardFlatFieldMetadatawOrThrow({ - flatFieldMetadataMaps, - flatObjectMetadata, - standardId: fieldStandardId, - }); - - if ( - flatFieldMetadata.name !== field || - !isFlatFieldMetadataOfType(flatFieldMetadata, FieldMetadataType.SELECT) - ) { - throw new Error( - `Retrieved flat field is not the one expected expected ${field} got ${flatFieldMetadata.name} in workspace ${workspaceId}`, - ); - } - - const updatedOptions = flatFieldMetadata.options.map((option) => { - const matchingNewOption = options.find( - ({ fromOption }) => fromOption.value === option.value, - ); - - if (!isDefined(matchingNewOption)) { - return option; - } - - return { - ...matchingNewOption.toOption, - id: option.id, - }; - }); - - const updatedDefaultValue: FieldMetadataDefaultValue = - flatFieldMetadata.defaultValue === defaultValue.fromDefaultValue - ? defaultValue.toDefaultValue - : flatFieldMetadata.defaultValue; - - return { - id: flatFieldMetadata.id, - options: updatedOptions, - defaultValue: updatedDefaultValue, - }; - }); - - const allUpdates = [ - { - id: iCalUidFieldId, - name: 'iCalUid', - }, - ...flatEnumFieldMetadatasToUpdate, - ]; - - if (!isDryRun) { - for (const updateFieldInput of allUpdates) { - await this.fieldMetadataService.updateOneField({ - updateFieldInput, - workspaceId, - isSystemBuild: true, - }); - } - } - this.logger.log('Migrated standard invalid entities'); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-timeline-activity-to-morph-relations.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-timeline-activity-to-morph-relations.command.ts deleted file mode 100644 index 2a35c411c5..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-timeline-activity-to-morph-relations.command.ts +++ /dev/null @@ -1,267 +0,0 @@ -import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { capitalize } from 'twenty-shared/utils'; -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 { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; -import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; -import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; -import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; -import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; -import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util'; -import { TIMELINE_ACTIVITY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-migration/constant/standard-field-ids'; -import { buildTimelineActivityRelatedMorphFieldMetadataName } from 'src/modules/timeline/utils/timeline-activity-related-morph-field-metadata-name-builder.util'; - -@Command({ - name: 'upgrade:1-13:migrate-timeline-activity-to-morph-relations', - description: - 'Migrate timeline activity relations to morph relation fields and set feature flag', -}) -export class MigrateTimelineActivityToMorphRelationsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - private readonly featureFlagService: FeatureFlagService, - @InjectRepository(ObjectMetadataEntity) - private readonly objectMetadataRepository: Repository, - @InjectDataSource() - private readonly coreDataSource: DataSource, - private readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - private readonly workspaceCacheStorageService: WorkspaceCacheStorageService, - private readonly workspaceCacheService: WorkspaceCacheService, - private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - const isMigrated = await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_TIMELINE_ACTIVITY_MIGRATED, - workspaceId, - ); - - this.logger.log(`Migrating timelineActivity for workspace ${workspaceId}`); - - if (isMigrated) { - this.logger.log( - `Timeline activity migration already completed. Skipping...`, - ); - - return; - } - - if (options.dryRun) { - this.logger.log( - `Would have migrated timeline activities for workspace ${workspaceId}. Skipping...`, - ); - - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - const schemaName = getWorkspaceSchemaName(workspaceId); - const tableName = 'timelineActivity'; - - const customObjectMetadata = await this.objectMetadataRepository.find({ - where: { - workspaceId, - isCustom: true, - }, - }); - const customObjectMetadataNames = customObjectMetadata.map( - (objectMetadata) => objectMetadata.nameSingular, - ); - - const fieldNameMigrations = [ - { old: 'company', new: 'targetCompany' }, - { old: 'person', new: 'targetPerson' }, - { old: 'opportunity', new: 'targetOpportunity' }, - { old: 'note', new: 'targetNote' }, - { old: 'task', new: 'targetTask' }, - { old: 'workflow', new: 'targetWorkflow' }, - { old: 'workflowVersion', new: 'targetWorkflowVersion' }, - { old: 'workflowRun', new: 'targetWorkflowRun' }, - { old: 'dashboard', new: 'targetDashboard' }, - ...customObjectMetadataNames.map((customObjectName) => ({ - old: `${customObjectName}`, - new: `target${capitalize(customObjectName)}`, - })), - ]; - - const fieldMigrations = fieldNameMigrations.map( - ({ old: oldFieldName, new: newFieldName }) => ({ - old: `${oldFieldName}Id`, - new: `${newFieldName}Id`, - }), - ); - - const timelineActivityObjectMetadata = - await this.objectMetadataRepository.findOne({ - where: { - workspaceId, - nameSingular: 'timelineActivity', - }, - relations: ['fields'], - }); - - if (!timelineActivityObjectMetadata) { - this.logger.error( - `🟥 Timeline activity object metadata not found for workspace ${workspaceId}`, - ); - - return; - } - - for (const { new: newField, old: oldField } of fieldMigrations) { - try { - await queryRunner.query( - `ALTER TABLE "${schemaName}"."${tableName}" - RENAME COLUMN "${oldField}" TO "${newField}"`, - ); - this.logger.log( - `Renamed column "${oldField}" to "${newField}" for "${tableName}"`, - ); - } catch (error) { - this.logger.error( - `Error renaming column "${oldField}" to "${newField}" for "${tableName}" in workspace ${workspaceId}`, - error, - ); - - throw error; - } - } - - this.logger.log(`✅ Successfully migrated timeline activity records`); - - const objectNamesToMigrate = fieldNameMigrations.map( - ({ old: oldFieldName }) => oldFieldName, - ); - - const relatedObjectMetadata = await this.objectMetadataRepository.find({ - where: { - workspaceId, - }, - }); - - const relatedObjectMetadataMap = new Map( - relatedObjectMetadata.map((obj) => [obj.nameSingular, obj]), - ); - - const morphId = TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId; - - for (const objectName of objectNamesToMigrate) { - const relatedObject = relatedObjectMetadataMap.get(objectName); - - if (!relatedObject) { - this.logger.warn( - `Related object metadata not found for ${objectName} in workspace ${workspaceId}`, - ); - continue; - } - - const fieldToMigrate = timelineActivityObjectMetadata.fields.find( - (field) => - field.type === 'RELATION' && - field.relationTargetObjectMetadataId === relatedObject.id, - ); - - if (!fieldToMigrate) { - this.logger.log( - `No RELATION field found for ${objectName} in timelineActivity`, - ); - continue; - } - - const newFieldName = buildTimelineActivityRelatedMorphFieldMetadataName( - fieldToMigrate.name, - ); - - const settings = { - ...fieldToMigrate.settings, - joinColumnName: computeMorphOrRelationFieldJoinColumnName({ - name: newFieldName, - }), - }; - - try { - const result = await queryRunner.query( - `UPDATE core."fieldMetadata" - SET name = $1, type = 'MORPH_RELATION', "morphId" = $3, settings = $4 - WHERE id = $2`, - [newFieldName, fieldToMigrate.id, morphId, settings], - ); - - const rowsUpdated = result[1] || 0; - - if (rowsUpdated > 0) { - this.logger.log( - `Updated fieldMetadata: ${fieldToMigrate.name} → ${newFieldName} (type: MORPH_RELATION)`, - ); - } - } catch (error) { - this.logger.error( - `Error updating fieldMetadata for ${objectName} in workspace ${workspaceId}`, - error, - ); - - throw error; - } - } - - this.logger.log( - `✅ Successfully migrated timeline activity fieldmetadata`, - ); - - await queryRunner.commitTransaction(); - - await this.featureFlagService.enableFeatureFlags( - [FeatureFlagKey.IS_TIMELINE_ACTIVITY_MIGRATED], - workspaceId, - ); - - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatFieldMetadataMaps', - 'flatObjectMetadataMaps', - 'featureFlagsMap', - ]); - - await this.workspaceMetadataVersionService.incrementMetadataVersion( - workspaceId, - ); - this.logger.log(`Cache flushed`); - - this.logger.log( - `Set IS_TIMELINE_ACTIVITY_MIGRATED feature flag for workspace ${workspaceId}`, - ); - - this.logger.log(`Flush cache for workspace ${workspaceId}`); - await this.workspaceCacheStorageService.flush(workspaceId); - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.error( - `Error migrating timeline activity to morph relations (rolled transaction back on ${workspaceId})`, - error, - ); - throw error; - } finally { - await queryRunner.release(); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-rename-index.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-rename-index.command.ts deleted file mode 100644 index 6ab8e6a6b0..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-rename-index.command.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { isDefined } from 'twenty-shared/utils'; -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 { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.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 { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; -import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.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 { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util'; - -@Command({ - name: 'upgrade:1-13:rename-index-name', - description: 'Rename indexes to use the new v2 index name format', -}) -export class RenameIndexNameCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - protected readonly logger = new Logger(RenameIndexNameCommand.name); - - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly workspaceCacheService: WorkspaceCacheService, - protected readonly dataSourceService: DataSourceService, - @InjectDataSource() - private readonly coreDataSource: DataSource, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - dataSource, - }: RunOnWorkspaceArgs): Promise { - const isDryRun = options.dryRun || false; - - if (!isDefined(dataSource)) { - throw new Error( - `Could not find data source for workspace ${workspaceId}, should never occur`, - ); - } - - const schemaName = getWorkspaceSchemaName(workspaceId); - - if (isDryRun) { - this.logger.log('Dry run mode: No changes will be applied'); - } - - const { flatIndexMaps, flatObjectMetadataMaps, flatFieldMetadataMaps } = - await this.workspaceCacheService.getOrRecompute(workspaceId, [ - 'flatIndexMaps', - 'flatObjectMetadataMaps', - 'flatFieldMetadataMaps', - ]); - - const indexMetadataByName = new Map( - Object.values(flatIndexMaps.byId) - .filter(isDefined) - .map((index) => [index.name, index]), - ); - - let hasIndexNameChanges = false; - let hasRemovedIndexMetadata = false; - - for (const index of Object.values(flatIndexMaps.byId).filter(isDefined)) { - const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityId: index.objectMetadataId, - flatEntityMaps: flatObjectMetadataMaps, - }); - - const flatFieldMetadatas = findManyFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityIds: flatObjectMetadata.fieldIds, - flatEntityMaps: flatFieldMetadataMaps, - }); - - const { name: indexNameV2 } = generateFlatIndexMetadataWithNameOrThrow({ - flatObjectMetadata, - objectFlatFieldMetadatas: flatFieldMetadatas, - flatIndex: index, - }); - - if (indexNameV2 === index.name) { - this.logger.log(`Index ${index.name} is V2`); - continue; - } - - // Check if another metadata entry already has the v2 name - const existingV2Metadata = indexMetadataByName.get(indexNameV2); - - if (isDefined(existingV2Metadata) && existingV2Metadata.id !== index.id) { - // V2 metadata already exists, this v1 metadata is stale - this.logger.log( - `Index metadata with v2 name ${indexNameV2} already exists, removing stale v1 metadata and index ${index.name}`, - ); - - if (!isDryRun) { - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await queryRunner.manager.delete(IndexMetadataEntity, index.id); - await queryRunner.query( - `DROP INDEX IF EXISTS "${schemaName}"."${index.name}"`, - ); - await queryRunner.commitTransaction(); - hasRemovedIndexMetadata = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - throw error; - } finally { - await queryRunner.release(); - } - } - continue; - } - - this.logger.log(`Renaming index ${index.name} to ${indexNameV2}`); - - if (isDryRun) { - continue; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await queryRunner.query( - `ALTER INDEX "${schemaName}"."${index.name}" RENAME TO "${indexNameV2}"`, - ); - - await queryRunner.manager.update(IndexMetadataEntity, index.id, { - name: indexNameV2, - }); - - await queryRunner.commitTransaction(); - hasIndexNameChanges = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - - // PostgreSQL error codes for non-existent index: - // - 42704: undefined_object - // - 42P01: undefined_table (index treated as relation) - if (error.code === '42704' || error.code === '42P01') { - this.logger.log( - `Index ${index.name} does not exist in schema ${schemaName}, removing metadata`, - ); - - await this.coreDataSource.manager.delete( - IndexMetadataEntity, - index.id, - ); - hasRemovedIndexMetadata = true; - } else if (error.code === '42P07') { - // PostgreSQL error code 42P07: duplicate_table (v2 index already exists) - // The v2 index already exists, remove stale v1 metadata and index - this.logger.log( - `Index ${indexNameV2} already exists at PG level, removing stale v1 metadata and index ${index.name}`, - ); - - await this.coreDataSource.manager.delete( - IndexMetadataEntity, - index.id, - ); - - await this.coreDataSource.query( - `DROP INDEX IF EXISTS "${schemaName}"."${index.name}"`, - ); - - hasRemovedIndexMetadata = true; - } else { - this.logger.error( - `Failed to rename index ${index.name}, error code: ${error.code}`, - ); - throw error; - } - } finally { - await queryRunner.release(); - } - } - - const shouldInvalidateCache = - hasIndexNameChanges || hasRemovedIndexMetadata; - - if (shouldInvalidateCache) { - this.logger.log('Invalidating workspace cache'); - - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatFieldMetadataMaps', - 'flatIndexMaps', - ]); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-update-role-targets-unique-constraint-migration.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-update-role-targets-unique-constraint-migration.command.ts deleted file mode 100644 index 3a118ef151..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-update-role-targets-unique-constraint-migration.command.ts +++ /dev/null @@ -1,77 +0,0 @@ -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 { 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-13:update-role-targets-unique-constraint-migration', - description: - 'Update roleTargets unique constraints from combined to separate constraints', -}) -export class UpdateRoleTargetsUniqueConstraintMigrationCommand 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.log( - 'Skipping has already been run once UpdateRoleTargetsUniqueConstraintMigrationCommand', - ); - - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - if (!options.dryRun) { - try { - await queryRunner.query( - `ALTER TABLE "core"."roleTarget" DROP CONSTRAINT "IDX_ROLE_TARGETS_UNIQUE"`, - ); - await queryRunner.query( - `ALTER TABLE "core"."roleTarget" ADD CONSTRAINT "IDX_ROLE_TARGET_UNIQUE_API_KEY" UNIQUE ("workspaceId", "apiKeyId")`, - ); - await queryRunner.query( - `ALTER TABLE "core"."roleTarget" ADD CONSTRAINT "IDX_ROLE_TARGET_UNIQUE_AGENT" UNIQUE ("workspaceId", "agentId")`, - ); - await queryRunner.query( - `ALTER TABLE "core"."roleTarget" ADD CONSTRAINT "IDX_ROLE_TARGET_UNIQUE_USER_WORKSPACE" UNIQUE ("workspaceId", "userWorkspaceId")`, - ); - - await queryRunner.commitTransaction(); - this.logger.log( - 'Successfully run UpdateRoleTargetsUniqueConstraintMigrationCommand', - ); - this.hasRunOnce = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.log( - `Rollbacking UpdateRoleTargetsUniqueConstraintMigrationCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-upgrade-version-command.module.ts deleted file mode 100644 index c43ca1b1d0..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-upgrade-version-command.module.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { BackfillPageLayoutUniversalIdentifiersCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-backfill-page-layout-universal-identifiers.command'; -import { CleanEmptyStringNullInTextFieldsCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-clean-empty-string-null-in-text-fields.command'; -import { DeduplicateRoleTargetsCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-deduplicate-role-targets.command'; -import { MigrateStandardInvalidEntitiesCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command'; -import { MigrateTimelineActivityToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-migrate-timeline-activity-to-morph-relations.command'; -import { RenameIndexNameCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-rename-index.command'; -import { UpdateRoleTargetsUniqueConstraintMigrationCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-update-role-targets-unique-constraint-migration.command'; -import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; -import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; -import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.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 { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module'; -import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity'; -import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity'; -import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity'; -import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity'; -import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity'; -import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; -import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module'; -import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module'; -import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; -import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; -import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([ - WorkspaceEntity, - ObjectMetadataEntity, - FieldMetadataEntity, - IndexMetadataEntity, - ViewEntity, - ViewGroupEntity, - FeatureFlagEntity, - RoleTargetEntity, - PageLayoutEntity, - PageLayoutWidgetEntity, - PageLayoutTabEntity, - TimelineActivityWorkspaceEntity, - ]), - DataSourceModule, - WorkspaceSchemaManagerModule, - WorkspaceCacheStorageModule, - WorkspaceMetadataVersionModule, - FeatureFlagModule, - WorkspaceCacheModule, - FieldMetadataModule, - ObjectMetadataModule, - ApplicationModule, - ], - providers: [ - CleanEmptyStringNullInTextFieldsCommand, - BackfillPageLayoutUniversalIdentifiersCommand, - DeduplicateRoleTargetsCommand, - RenameIndexNameCommand, - MigrateStandardInvalidEntitiesCommand, - UpdateRoleTargetsUniqueConstraintMigrationCommand, - MigrateTimelineActivityToMorphRelationsCommand, - ], - exports: [ - CleanEmptyStringNullInTextFieldsCommand, - BackfillPageLayoutUniversalIdentifiersCommand, - DeduplicateRoleTargetsCommand, - RenameIndexNameCommand, - MigrateStandardInvalidEntitiesCommand, - UpdateRoleTargetsUniqueConstraintMigrationCommand, - MigrateTimelineActivityToMorphRelationsCommand, - ], -}) -export class V1_13_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-delete-removed-agents.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-delete-removed-agents.command.ts deleted file mode 100644 index 24eddb723a..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-delete-removed-agents.command.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { isDefined } from 'twenty-shared/utils'; -import { type 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 { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { AgentService } from 'src/engine/metadata-modules/ai/ai-agent/agent.service'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { FlatAgentMaps } from 'src/engine/metadata-modules/flat-agent/types/flat-agent-maps.type'; -import { MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type'; -import { RoleService } from 'src/engine/metadata-modules/role/role.service'; -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'; - -const REMOVED_AGENT_STANDARD_IDS = [ - '20202020-0002-0001-0001-000000000006', // dashboard-builder - '20202020-0002-0001-0001-000000000003', // data-manipulator - '20202020-0002-0001-0001-000000000007', // metadata-builder - '20202020-0002-0001-0001-000000000005', // researcher - '20202020-0002-0001-0001-000000000001', // workflow-builder -]; - -const REMOVED_ROLE_STANDARD_IDS = [ - '20202020-0001-0001-0001-000000000002', // workflow-manager - '20202020-0001-0001-0001-000000000004', // data-manipulator - '20202020-0001-0001-0001-000000000005', // dashboard-manager - '20202020-0001-0001-0001-000000000006', // data-model-manager -]; - -@Command({ - name: 'upgrade:1-14:delete-removed-agents', - description: 'Delete agents and roles that were removed from the codebase', -}) -export class DeleteRemovedAgentsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - private readonly workspaceCacheService: WorkspaceCacheService, - private readonly agentService: AgentService, - private readonly roleService: RoleService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - options, - workspaceId, - }: RunOnWorkspaceArgs): Promise { - const isDryRun = options.dryRun || false; - - const { flatAgentMaps, flatRoleMaps } = - await this.workspaceCacheService.getOrRecompute(workspaceId, [ - 'flatAgentMaps', - 'flatRoleMaps', - ]); - - await this.deleteRemovedAgents({ flatAgentMaps, workspaceId, isDryRun }); - await this.deleteRemovedRoles({ flatRoleMaps, workspaceId, isDryRun }); - } - - private async deleteRemovedAgents({ - flatAgentMaps, - workspaceId, - isDryRun, - }: { - flatAgentMaps: FlatAgentMaps; - workspaceId: string; - isDryRun: boolean; - }): Promise { - const allAgents = Object.values(flatAgentMaps.byId).filter(isDefined); - - const agentsToDelete = allAgents.filter((agent) => - REMOVED_AGENT_STANDARD_IDS.includes(agent.standardId ?? ''), - ); - - const agentNames = agentsToDelete.map((agent) => agent.name).join(', '); - - this.logger.log( - `DRY RUN: Would delete ${agentsToDelete.length} removed agent(s): ${agentNames}`, - ); - - if (agentsToDelete.length === 0 || isDryRun) { - return; - } - - const agentIds = agentsToDelete.map((agent) => agent.id); - - try { - await this.agentService.deleteManyAgents({ - ids: agentIds, - workspaceId, - isSystemBuild: true, - }); - } catch (error) { - this.logger.error( - `Failed to delete workspace agents \n ${JSON.stringify(error, null, 2)}`, - ); - - throw error; - } - - this.logger.log( - `Deleted ${agentsToDelete.length} removed agent(s): ${agentNames}`, - ); - } - - private async deleteRemovedRoles({ - flatRoleMaps, - workspaceId, - isDryRun, - }: { - flatRoleMaps: MetadataFlatEntityMaps<'role'>; - workspaceId: string; - isDryRun: boolean; - }): Promise { - const allRoles = Object.values(flatRoleMaps.byId).filter(isDefined); - - const rolesToDelete = allRoles.filter((role) => - REMOVED_ROLE_STANDARD_IDS.includes(role.standardId ?? ''), - ); - - const roleLabels = rolesToDelete.map((role) => role.label).join(', '); - - this.logger.log( - `DRY RUN: Would delete ${rolesToDelete.length} removed role(s): ${roleLabels}`, - ); - - if (rolesToDelete.length === 0 || isDryRun) { - return; - } - - const roleIds = rolesToDelete.map((role) => role.id); - - try { - await this.roleService.deleteManyRoles({ - ids: roleIds, - workspaceId, - isSystemBuild: true, - }); - } catch (error) { - this.logger.error( - `Failed to delete workspace roles \n ${JSON.stringify(error, null, 2)}`, - ); - - throw error; - } - - this.logger.log( - `Deleted ${rolesToDelete.length} removed role(s): ${roleLabels}`, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-update-created-by-enum.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-update-created-by-enum.command.ts deleted file mode 100644 index 7d15346f31..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-update-created-by-enum.command.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { FieldMetadataType } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; -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 { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; -import { computeTableName } from 'src/engine/utils/compute-table-name.util'; -import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util'; -import { computePostgresEnumName } from 'src/engine/workspace-manager/workspace-migration/utils/compute-postgres-enum-name.util'; - -@Command({ - name: 'upgrade:1-14:udpate-created-by-enum', - description: 'Add new APPLICATION value to createdBy enum', -}) -export class UpdateCreatedByEnumCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - protected readonly logger = new Logger(UpdateCreatedByEnumCommand.name); - - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - @InjectRepository(FieldMetadataEntity) - protected readonly fieldMetadataRepository: Repository, - @InjectDataSource() - private readonly coreDataSource: DataSource, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - dataSource, - }: RunOnWorkspaceArgs): Promise { - const isDryRun = options.dryRun || false; - - if (!isDefined(dataSource)) { - throw new Error( - `Could not find data source for workspace ${workspaceId}, should never occur`, - ); - } - - const schemaName = getWorkspaceSchemaName(workspaceId); - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - const createdByFields = await this.fieldMetadataRepository.find({ - where: { workspaceId, name: 'createdBy', type: FieldMetadataType.ACTOR }, - relations: ['object'], - }); - - if (isDryRun) { - this.logger.log( - `Dry run mode: found ${createdByFields.length} createdBy enums to update`, - ); - } - - if (!options.dryRun) { - this.logger.log(`Updating ${createdByFields.length} createdBy enums`); - try { - for (const createdByField of createdByFields) { - const tableName = computeTableName( - createdByField.object.nameSingular, - createdByField.object.isCustom, - ); - const createdByEnumName = computePostgresEnumName({ - tableName, - columnName: 'createdBySource', - }); - - await queryRunner.query( - `ALTER TYPE "${schemaName}"."${createdByEnumName}" ADD VALUE IF NOT EXISTS 'APPLICATION'`, - ); - } - await queryRunner.commitTransaction(); - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.log( - `Rollbacking UpdateCreatedByEnumCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-upgrade-version-command.module.ts deleted file mode 100644 index 92b6b9a326..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-upgrade-version-command.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { DeleteRemovedAgentsCommand } from 'src/database/commands/upgrade-version-command/1-14/1-14-delete-removed-agents.command'; -import { UpdateCreatedByEnumCommand } from 'src/database/commands/upgrade-version-command/1-14/1-14-update-created-by-enum.command'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { AiAgentModule } from 'src/engine/metadata-modules/ai/ai-agent/ai-agent.module'; -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 { RoleModule } from 'src/engine/metadata-modules/role/role.module'; -import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module'; -import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([WorkspaceEntity, FieldMetadataEntity]), - DataSourceModule, - AiAgentModule, - RoleModule, - WorkspaceSchemaManagerModule, - WorkspaceCacheModule, - ], - providers: [UpdateCreatedByEnumCommand, DeleteRemovedAgentsCommand], - exports: [DeleteRemovedAgentsCommand, UpdateCreatedByEnumCommand], -}) -export class V1_14_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-add-workspace-foreign-keys-migration.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-add-workspace-foreign-keys-migration.command.ts deleted file mode 100644 index a8987fe230..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-add-workspace-foreign-keys-migration.command.ts +++ /dev/null @@ -1,68 +0,0 @@ -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 { addWorkspaceForeignKeysQueries } from 'src/database/typeorm/core/migrations/utils/1767002571103-addWorkspaceForeignKeys.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-15:add-workspace-foreign-keys-migration', - description: 'Add foreign key constraints for workspaceId on core tables', -}) -export class AddWorkspaceForeignKeysMigrationCommand 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.log( - 'Skipping has already been run once AddWorkspaceForeignKeysMigrationCommand', - ); - - return; - } - - if (options.dryRun) { - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await addWorkspaceForeignKeysQueries(queryRunner); - - await queryRunner.commitTransaction(); - this.logger.log( - 'Successfully run AddWorkspaceForeignKeysMigrationCommand', - ); - this.hasRunOnce = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.log( - `Rollbacking AddWorkspaceForeignKeysMigrationCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-backfill-updated-by-field.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-backfill-updated-by-field.command.ts deleted file mode 100644 index ffc74199ae..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-backfill-updated-by-field.command.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; -import { FieldMetadataType } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; -import { 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/services/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 { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; -import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service'; -import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; -import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.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 { - ATTACHMENT_STANDARD_FIELD_IDS, - COMPANY_STANDARD_FIELD_IDS, - CUSTOM_OBJECT_STANDARD_FIELD_IDS, - DASHBOARD_STANDARD_FIELD_IDS, - NOTE_STANDARD_FIELD_IDS, - OPPORTUNITY_STANDARD_FIELD_IDS, - PERSON_STANDARD_FIELD_IDS, - TASK_STANDARD_FIELD_IDS, - WORKFLOW_RUN_STANDARD_FIELD_IDS, - WORKFLOW_STANDARD_FIELD_IDS, -} from 'src/engine/workspace-manager/workspace-migration/constant/standard-field-ids'; - -@Command({ - name: 'upgrade:1-15:backfill-updated-by-field', - description: 'Backfill updatedBy field for all objects', -}) -export class BackfillUpdatedByFieldCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - protected readonly logger = new Logger(BackfillUpdatedByFieldCommand.name); - - private readonly objectToUpdatedByStandardIdMap: Record = { - [STANDARD_OBJECT_IDS.attachment]: ATTACHMENT_STANDARD_FIELD_IDS.updatedBy, - [STANDARD_OBJECT_IDS.company]: COMPANY_STANDARD_FIELD_IDS.updatedBy, - [STANDARD_OBJECT_IDS.person]: PERSON_STANDARD_FIELD_IDS.updatedBy, - [STANDARD_OBJECT_IDS.note]: NOTE_STANDARD_FIELD_IDS.updatedBy, - [STANDARD_OBJECT_IDS.task]: TASK_STANDARD_FIELD_IDS.updatedBy, - [STANDARD_OBJECT_IDS.opportunity]: OPPORTUNITY_STANDARD_FIELD_IDS.updatedBy, - [STANDARD_OBJECT_IDS.workflow]: WORKFLOW_STANDARD_FIELD_IDS.updatedBy, - [STANDARD_OBJECT_IDS.workflowRun]: - WORKFLOW_RUN_STANDARD_FIELD_IDS.updatedBy, - [STANDARD_OBJECT_IDS.dashboard]: DASHBOARD_STANDARD_FIELD_IDS.updatedBy, - }; - - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - private readonly fieldMetadataService: FieldMetadataService, - private readonly workspaceCacheService: WorkspaceCacheService, - private readonly applicationService: ApplicationService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Starting backfill of updatedBy field for workspace ${workspaceId}`, - ); - - const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { - workspaceId, - }, - ); - - const { flatFieldMetadataMaps, flatObjectMetadataMaps } = - await this.workspaceCacheService.getOrRecompute(workspaceId, [ - 'flatObjectMetadataMaps', - 'flatFieldMetadataMaps', - ]); - - const objectMetadataList = Object.values( - flatObjectMetadataMaps.byId, - ).filter(isDefined); - - if (objectMetadataList.length === 0) { - this.logger.log( - `No objects found that need updatedBy field for workspace ${workspaceId}`, - ); - - return; - } - - const createFieldInputs: CreateFieldInput[] = []; - - for (const flatObjectMetadata of objectMetadataList) { - const isStandardObject = belongsToTwentyStandardApp(flatObjectMetadata); - const updatedByStandardId = !isStandardObject - ? CUSTOM_OBJECT_STANDARD_FIELD_IDS.updatedBy - : isDefined(flatObjectMetadata.standardId) - ? this.objectToUpdatedByStandardIdMap[flatObjectMetadata.standardId] - : undefined; - - if (!isDefined(updatedByStandardId)) { - continue; - } - - const flatFieldMetadatas = findManyFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityIds: flatObjectMetadata.fieldIds, - flatEntityMaps: flatFieldMetadataMaps, - }); - - const updatedByField = flatFieldMetadatas.find( - (field) => field.standardId === updatedByStandardId, - ); - - if (isDefined(updatedByField)) { - this.logger.log( - `Object ${flatObjectMetadata.nameSingular} (${flatObjectMetadata.standardId || 'custom'}) already has updatedBy field, skipping`, - ); - continue; - } - - const applicationId = isStandardObject - ? twentyStandardFlatApplication.id - : workspaceCustomFlatApplication.id; - const universalIdentifier = isStandardObject ? updatedByStandardId : v4(); - - createFieldInputs.push({ - workspaceId, - objectMetadataId: flatObjectMetadata.id, - name: 'updatedBy', - type: FieldMetadataType.ACTOR, - label: 'Updated by', - description: 'The user who last updated the record', - icon: 'IconUserCircle', - isNullable: true, - isUIReadOnly: true, - isCustom: false, - isSystem: false, - isActive: true, - standardId: updatedByStandardId, - universalIdentifier, - applicationId, - }); - } - - if (createFieldInputs.length === 0) { - this.logger.log( - `All objects already have updatedBy field for workspace ${workspaceId}`, - ); - - return; - } - - this.logger.log( - `Found ${createFieldInputs.length} objects that need updatedBy field`, - ); - - if (options.dryRun) { - this.logger.log( - `[DRY RUN] Would create updatedBy field for ${createFieldInputs.length} objects`, - ); - - return; - } - - try { - await this.fieldMetadataService.createManyFields({ - createFieldInputs, - workspaceId, - isSystemBuild: true, - }); - } catch (error) { - this.logger.error( - `Failed to create many fields \n ${JSON.stringify(error, null, 2)}`, - ); - throw error; - } - - this.logger.log( - `Successfully backfilled updatedBy field for ${createFieldInputs.length} objects in workspace ${workspaceId}`, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-fix-nan-position-values-in-notes.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-fix-nan-position-values-in-notes.command.ts deleted file mode 100644 index 16f8cdc154..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-fix-nan-position-values-in-notes.command.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { isDefined } from 'twenty-shared/utils'; -import { 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 { 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-15:fix-nan-position-values-in-notes', - description: 'Fix NaN position values in notes by replacing them with 2', -}) -export class FixNanPositionValuesInNotesCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - protected readonly logger = new Logger( - FixNanPositionValuesInNotesCommand.name, - ); - - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - ) { - super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - dataSource, - }: RunOnWorkspaceArgs): Promise { - const isDryRun = options.dryRun || false; - - if (!isDefined(dataSource)) { - throw new Error( - `Could not find data source for workspace ${workspaceId}, should never occur`, - ); - } - - if (isDryRun) { - this.logger.log('Dry run mode: No changes will be applied'); - } - - try { - // Count NaN position values in notes - const noteRepository = await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'note', - { shouldBypassPermissionChecks: true }, - ); - - const nanCount = await noteRepository.count({ - where: { position: 'NaN' }, - }); - - this.logger.log( - `Found ${nanCount} note(s) with NaN position values in workspace ${workspaceId}`, - ); - - if (nanCount === 0) { - this.logger.log('No NaN position values to fix'); - - return; - } - - if (!isDryRun) { - // Update NaN position values to 2 because 1 is first position - await noteRepository - .createQueryBuilder() - .update() - .set({ position: 2 }) - .where('position = :nanString', { nanString: 'NaN' }) - .execute(); - - this.logger.log( - `Fixed ${nanCount} NaN position value(s) in notes for workspace ${workspaceId}`, - ); - } else { - this.logger.log( - `DRY RUN: Would fix ${nanCount} NaN position value(s) in notes for workspace ${workspaceId}`, - ); - } - } catch (error) { - this.logger.error( - `Could not fix NaN position values in notes for workspace ${workspaceId}`, - ); - this.logger.error(error); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.command.ts deleted file mode 100644 index c92b09591f..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.command.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { assertUnreachable, isDefined } from 'twenty-shared/utils'; -import { 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 { 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 { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity'; -import { BarChartLayout } from 'src/engine/metadata-modules/page-layout-widget/enums/bar-chart-layout.enum'; -import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; -import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; -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'; - -enum DeprecatedGraphType { - VERTICAL_BAR = 'VERTICAL_BAR', - HORIZONTAL_BAR = 'HORIZONTAL_BAR', - PIE = 'PIE', - LINE = 'LINE', - AGGREGATE = 'AGGREGATE', - GAUGE = 'GAUGE', -} - -const migrateConfiguration = ( - widgetType: WidgetType, - configuration: Record, -): Record => { - const existingConfigurationType = configuration.configurationType as - | string - | undefined; - - if (isDefined(existingConfigurationType)) { - return configuration; - } - - switch (widgetType) { - case WidgetType.GRAPH: - return migrateGraphConfiguration(configuration); - case WidgetType.IFRAME: - return { - ...configuration, - configurationType: WidgetConfigurationType.IFRAME, - }; - case WidgetType.STANDALONE_RICH_TEXT: - return { - ...configuration, - configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT, - }; - case WidgetType.VIEW: - return { - ...configuration, - configurationType: WidgetConfigurationType.VIEW, - }; - case WidgetType.FIELDS: - return { - ...configuration, - configurationType: WidgetConfigurationType.FIELDS, - }; - case WidgetType.TIMELINE: - return { - ...configuration, - configurationType: WidgetConfigurationType.TIMELINE, - }; - case WidgetType.TASKS: - return { - ...configuration, - configurationType: WidgetConfigurationType.TASKS, - }; - case WidgetType.NOTES: - return { - ...configuration, - configurationType: WidgetConfigurationType.NOTES, - }; - case WidgetType.FILES: - return { - ...configuration, - configurationType: WidgetConfigurationType.FILES, - }; - case WidgetType.EMAILS: - return { - ...configuration, - configurationType: WidgetConfigurationType.EMAILS, - }; - case WidgetType.CALENDAR: - return { - ...configuration, - configurationType: WidgetConfigurationType.CALENDAR, - }; - case WidgetType.FIELD_RICH_TEXT: - return { - ...configuration, - configurationType: WidgetConfigurationType.FIELD_RICH_TEXT, - }; - case WidgetType.WORKFLOW: - return { - ...configuration, - configurationType: WidgetConfigurationType.WORKFLOW, - }; - case WidgetType.WORKFLOW_VERSION: - return { - ...configuration, - configurationType: WidgetConfigurationType.WORKFLOW_VERSION, - }; - case WidgetType.WORKFLOW_RUN: - return { - ...configuration, - configurationType: WidgetConfigurationType.WORKFLOW_RUN, - }; - case WidgetType.FIELD: - return { - ...configuration, - configurationType: WidgetConfigurationType.FIELD, - }; - case WidgetType.FRONT_COMPONENT: - return { - ...configuration, - configurationType: WidgetConfigurationType.FRONT_COMPONENT, - }; - default: - assertUnreachable(widgetType); - } -}; - -const migrateGraphConfiguration = ( - configuration: Record, -): Record => { - const { graphType: _, ...restConfig } = configuration; - const graphType = configuration.graphType as DeprecatedGraphType | undefined; - - if (!isDefined(graphType)) { - return configuration; - } - - switch (graphType) { - case DeprecatedGraphType.VERTICAL_BAR: - return { - ...restConfig, - configurationType: WidgetConfigurationType.BAR_CHART, - layout: BarChartLayout.VERTICAL, - }; - case DeprecatedGraphType.HORIZONTAL_BAR: - return { - ...restConfig, - configurationType: WidgetConfigurationType.BAR_CHART, - layout: BarChartLayout.HORIZONTAL, - }; - case DeprecatedGraphType.PIE: - return { - ...restConfig, - configurationType: WidgetConfigurationType.PIE_CHART, - }; - case DeprecatedGraphType.LINE: - return { - ...restConfig, - configurationType: WidgetConfigurationType.LINE_CHART, - }; - case DeprecatedGraphType.AGGREGATE: - return { - ...restConfig, - configurationType: WidgetConfigurationType.AGGREGATE_CHART, - }; - case DeprecatedGraphType.GAUGE: - return { - ...restConfig, - configurationType: WidgetConfigurationType.GAUGE_CHART, - }; - default: - assertUnreachable(graphType); - } -}; - -const needsMigration = ( - widgetType: WidgetType, - configuration: Record, -): boolean => { - const graphType = configuration.graphType as DeprecatedGraphType | undefined; - const configurationType = configuration.configurationType as - | WidgetConfigurationType - | undefined; - - if (isDefined(graphType)) { - return true; - } - - if ( - !isDefined(configurationType) && - (widgetType === WidgetType.IFRAME || - widgetType === WidgetType.STANDALONE_RICH_TEXT) - ) { - return true; - } - - return false; -}; - -@Command({ - name: 'upgrade:1-15:migrate-page-layout-widget-configuration', - description: - 'Migrate page layout widget configurations to use configurationType', -}) -export class MigratePageLayoutWidgetConfigurationCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - @InjectRepository(PageLayoutWidgetEntity) - private readonly pageLayoutWidgetRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - private readonly workspaceCacheService: WorkspaceCacheService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Starting migration of page layout widget configurations for workspace ${workspaceId}`, - ); - - const widgets = await this.pageLayoutWidgetRepository.find({ - where: { workspaceId }, - select: ['id', 'type', 'configuration'], - }); - - const widgetsWithNullConfiguration = widgets.filter( - (widget) => !isDefined(widget.configuration), - ); - - for (const widget of widgetsWithNullConfiguration) { - this.logger.log( - `Widget ${widget.id} has a corrupted configuration (null). Skipping migration.`, - ); - } - - const widgetsToMigrate = widgets.filter( - (widget) => - isDefined(widget.configuration) && - needsMigration( - widget.type, - widget.configuration as unknown as Record, - ), - ); - - this.logger.log( - `Found ${widgetsToMigrate.length} widget(s) needing migration out of ${widgets.length} total`, - ); - - if (widgetsToMigrate.length === 0) { - return; - } - - for (const widget of widgetsToMigrate) { - const migratedConfiguration = migrateConfiguration( - widget.type, - widget.configuration as unknown as Record, - ); - - if (options.dryRun) { - this.logger.log( - `[DRY RUN] Would update widget ${widget.id} configuration from ${JSON.stringify(widget.configuration)} to ${JSON.stringify(migratedConfiguration)}`, - ); - } else { - await this.pageLayoutWidgetRepository.update( - { id: widget.id }, - { configuration: migratedConfiguration }, - ); - - this.logger.log(`Updated widget ${widget.id} configuration`); - } - } - - this.logger.log( - `${options.dryRun ? '[DRY RUN] Would have migrated' : 'Successfully migrated'} ${widgetsToMigrate.length} widget(s)`, - ); - - if (!options.dryRun && widgetsToMigrate.length > 0) { - this.logger.log( - `Invalidating and recomputing cache for workspace ${workspaceId}`, - ); - - const flatCacheToInvalidate = - getMetadataRelatedMetadataNames('pageLayoutWidget'); - - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatPageLayoutWidgetMaps', - ...flatCacheToInvalidate.map(getMetadataFlatEntityMapsKey), - ]); - - this.logger.log(`Cache invalidated and recomputed successfully`); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-upgrade-version-command.module.ts deleted file mode 100644 index f3d3e24333..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-upgrade-version-command.module.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { AddWorkspaceForeignKeysMigrationCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-add-workspace-foreign-keys-migration.command'; -import { BackfillUpdatedByFieldCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-backfill-updated-by-field.command'; -import { FixNanPositionValuesInNotesCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-fix-nan-position-values-in-notes.command'; -import { MigratePageLayoutWidgetConfigurationCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.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 { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity'; -import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity'; -import { ViewFieldModule } from 'src/engine/metadata-modules/view-field/view-field.module'; -import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; -import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module'; -import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([ - WorkspaceEntity, - PageLayoutWidgetEntity, - ObjectMetadataEntity, - ViewEntity, - ViewFieldEntity, - ]), - DataSourceModule, - WorkspaceSchemaManagerModule, - WorkspaceCacheModule, - FieldMetadataModule, - ViewFieldModule, - ApplicationModule, - ], - providers: [ - MigratePageLayoutWidgetConfigurationCommand, - FixNanPositionValuesInNotesCommand, - BackfillUpdatedByFieldCommand, - AddWorkspaceForeignKeysMigrationCommand, - ], - exports: [ - MigratePageLayoutWidgetConfigurationCommand, - FixNanPositionValuesInNotesCommand, - BackfillUpdatedByFieldCommand, - AddWorkspaceForeignKeysMigrationCommand, - ], -}) -export class V1_15_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-backfill-opportunity-owner-field.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-backfill-opportunity-owner-field.command.ts deleted file mode 100644 index f3645d6861..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-backfill-opportunity-owner-field.command.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; -import { FieldMetadataType, RelationType } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; -import { 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 { ApplicationService } from 'src/engine/core-modules/application/services/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 { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service'; -import { MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type'; -import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.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 { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; -import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-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'; - -@Command({ - name: 'upgrade:1-16:backfill-opportunity-owner-field', - description: 'Backfill owner field for opportunity object', -}) -export class BackfillOpportunityOwnerFieldCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - protected readonly logger = new Logger( - BackfillOpportunityOwnerFieldCommand.name, - ); - - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - @InjectRepository(FieldMetadataEntity) - private readonly fieldMetadataRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - private readonly fieldMetadataService: FieldMetadataService, - private readonly workspaceCacheService: WorkspaceCacheService, - private readonly applicationService: ApplicationService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Starting backfill of owner field for opportunity in workspace ${workspaceId}`, - ); - - const { twentyStandardFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { - workspaceId, - }, - ); - - const { flatFieldMetadataMaps, flatObjectMetadataMaps } = - await this.workspaceCacheService.getOrRecompute(workspaceId, [ - 'flatObjectMetadataMaps', - 'flatFieldMetadataMaps', - ]); - - const opportunityObjectMetadata = Object.values( - flatObjectMetadataMaps.byId, - ).find( - (obj) => - isDefined(obj) && obj.standardId === STANDARD_OBJECT_IDS.opportunity, - ); - - if (!isDefined(opportunityObjectMetadata)) { - this.logger.log( - `Opportunity object not found for workspace ${workspaceId}, skipping`, - ); - - return; - } - - const workspaceMemberObjectMetadata = Object.values( - flatObjectMetadataMaps.byId, - ).find( - (obj) => - isDefined(obj) && - obj.standardId === STANDARD_OBJECT_IDS.workspaceMember, - ); - - if (!isDefined(workspaceMemberObjectMetadata)) { - this.logger.log( - `WorkspaceMember object not found for workspace ${workspaceId}, skipping`, - ); - - return; - } - - const flatFieldMetadatas = findManyFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityIds: opportunityObjectMetadata.fieldIds, - flatEntityMaps: flatFieldMetadataMaps, - }); - - const ownerUniversalIdentifier = - STANDARD_OBJECTS.opportunity.fields.owner.universalIdentifier; - - const ownerField = flatFieldMetadatas.find( - (field) => field.universalIdentifier === ownerUniversalIdentifier, - ); - - if (isDefined(ownerField)) { - this.logger.log( - `Opportunity already has owner field in workspace ${workspaceId}, skipping`, - ); - - return; - } - - const customOwnerField = - flatFieldMetadatas.find( - (field) => - field.name === 'owner' && - field.universalIdentifier !== ownerUniversalIdentifier, - // Some self hoster might have run the name renaming successfully but not the joinColumnName - // Rather than asking them to restore db also grabbing them here - ) ?? flatFieldMetadatas.find((field) => field.name === 'ownerOld'); - - if (isDefined(customOwnerField)) { - await this.renameCustomOwnerFieldToOwnerOld({ - customOwnerField, - flatFieldMetadataMaps, - workspaceId, - dryRun: options.dryRun, - }); - } - - if (options.dryRun) { - this.logger.log( - `[DRY RUN] Would create owner field for opportunity in workspace ${workspaceId}`, - ); - - return; - } - - const createFieldInput: CreateFieldInput = { - workspaceId, - objectMetadataId: opportunityObjectMetadata.id, - name: 'owner', - type: FieldMetadataType.RELATION, - label: 'Owner', - description: 'Opportunity owner', - icon: 'IconUserCircle', - isNullable: true, - isCustom: false, - isSystem: false, - isActive: true, - standardId: ownerUniversalIdentifier, - universalIdentifier: ownerUniversalIdentifier, - applicationId: twentyStandardFlatApplication.id, - relationCreationPayload: { - type: RelationType.MANY_TO_ONE, - targetObjectMetadataId: workspaceMemberObjectMetadata.id, - targetFieldLabel: 'Owned opportunities', - targetFieldIcon: 'IconTargetArrow', - }, - }; - - const ownedOpportunitiesUniversalIdentifier = - STANDARD_OBJECTS.workspaceMember.fields.ownedOpportunities - .universalIdentifier; - - try { - await this.fieldMetadataService.createManyFields({ - createFieldInputs: [createFieldInput], - workspaceId, - isSystemBuild: true, - }); - - this.logger.log( - `Created owner field for opportunity in workspace ${workspaceId}`, - ); - - const ownedOpportunitiesField = - await this.fieldMetadataRepository.findOne({ - where: { - workspaceId, - objectMetadataId: workspaceMemberObjectMetadata.id, - name: 'ownedOpportunities', - }, - }); - - if (isDefined(ownedOpportunitiesField)) { - await this.fieldMetadataRepository.update( - { id: ownedOpportunitiesField.id }, - { - universalIdentifier: ownedOpportunitiesUniversalIdentifier, - standardId: ownedOpportunitiesUniversalIdentifier, - applicationId: twentyStandardFlatApplication.id, - }, - ); - - this.logger.log( - `Updated ownedOpportunities field universalIdentifier in workspace ${workspaceId}`, - ); - - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatFieldMetadataMaps', - ]); - } else { - throw new Error( - `Should never occur could not find the created ownedOpportunities field`, - ); - } - - this.logger.log( - `Successfully backfilled owner field for opportunity in workspace ${workspaceId}`, - ); - } catch (error) { - this.logger.error( - `Failed to create owner field for opportunity in workspace ${workspaceId}`, - error, - ); - throw error; - } - } - - private async renameCustomOwnerFieldToOwnerOld({ - customOwnerField, - flatFieldMetadataMaps, - workspaceId, - dryRun, - }: { - customOwnerField: FlatFieldMetadata; - flatFieldMetadataMaps: MetadataFlatEntityMaps<'fieldMetadata'>; - workspaceId: string; - dryRun?: boolean; - }): Promise { - if (dryRun) { - this.logger.log( - `[DRY RUN] Would rename custom owner field to 'ownerOld' in workspace and search for colliding foreignKey ${workspaceId}`, - ); - - return; - } - - const isMorphOrRelationField = - isMorphOrRelationFlatFieldMetadata(customOwnerField); - const hasCollidingJoinColumnName = - isMorphOrRelationField && - isDefined(customOwnerField.settings.joinColumnName) && - customOwnerField.settings.joinColumnName === 'ownerId'; - - await this.fieldMetadataService.updateOneField({ - updateFieldInput: { - id: customOwnerField.id, - name: 'ownerOld', - label: 'Owner (Old)', - ...(hasCollidingJoinColumnName - ? { - settings: { - ...customOwnerField.settings, - joinColumnName: `ownerOldId`, - }, - } - : {}), - }, - workspaceId, - isSystemBuild: true, - }); - this.logger.log( - `Renamed custom owner field to 'ownerOld' in workspace ${workspaceId}`, - ); - - if (!isMorphOrRelationField || hasCollidingJoinColumnName) { - return; - } - - const relatedFlatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({ - flatEntityId: customOwnerField.relationTargetFieldMetadataId, - flatEntityMaps: flatFieldMetadataMaps, - }); - - if ( - !isDefined(relatedFlatFieldMetadata) || - !isMorphOrRelationFlatFieldMetadata(relatedFlatFieldMetadata) - ) { - this.logger.error( - `Could not find custom owner relation target field ${customOwnerField.relationTargetFieldMetadataId}`, - ); - - return; - } - - const targetHasCollidingJoinColumnName = - isDefined(relatedFlatFieldMetadata.settings.joinColumnName) && - relatedFlatFieldMetadata.settings.joinColumnName === 'ownerId'; - - if (!targetHasCollidingJoinColumnName) { - return; - } - - await this.fieldMetadataService.updateOneField({ - updateFieldInput: { - id: relatedFlatFieldMetadata.id, - settings: { - ...relatedFlatFieldMetadata.settings, - joinColumnName: `ownerOldId`, - }, - }, - workspaceId, - isSystemBuild: true, - }); - - this.logger.log( - `Renamed custom owner field target related field join column name to 'ownerOldId' in workspace ${workspaceId}`, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-backfill-standard-page-layouts.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-backfill-standard-page-layouts.command.ts deleted file mode 100644 index 62bf8c367d..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-backfill-standard-page-layouts.command.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { FieldActorSource } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; -import { 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 { updateStandardPageLayoutWidgetsWithWorkspaceFieldIds } from 'src/database/commands/upgrade-version-command/1-16/utils/update-standard-page-layout-widgets-with-workspace-field-ids.util'; -import { ApplicationService } from 'src/engine/core-modules/application/services/application.service'; -import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.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 { MY_FIRST_DASHBOARD_ID } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-dashboards'; -import { STANDARD_PAGE_LAYOUTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout.constant'; -import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant'; -import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; -import { DashboardWorkspaceEntity } from 'src/modules/dashboard/standard-objects/dashboard.workspace-entity'; - -@Command({ - name: 'upgrade:1-16:backfill-standard-page-layouts', - description: 'Backfill standard page layouts and dashboard records', -}) -export class BackfillStandardPageLayoutsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - protected readonly logger = new Logger( - BackfillStandardPageLayoutsCommand.name, - ); - - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - private readonly applicationService: ApplicationService, - private readonly twentyConfigService: TwentyConfigService, - private readonly workspaceCacheService: WorkspaceCacheService, - private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Starting backfill of standard page layouts for workspace ${workspaceId}`, - ); - - await this.backfillStandardPageLayoutMetadata({ workspaceId, options }); - await this.backfillDashboardRecord({ workspaceId, options }); - - this.logger.log( - `Successfully backfilled standard page layouts for workspace ${workspaceId}`, - ); - } - - private async backfillStandardPageLayoutMetadata({ - workspaceId, - options, - }: { - workspaceId: string; - options: RunOnWorkspaceArgs['options']; - }): Promise { - const { - flatPageLayoutMaps: fromFlatPageLayoutMaps, - flatPageLayoutTabMaps: fromFlatPageLayoutTabMaps, - flatPageLayoutWidgetMaps: fromFlatPageLayoutWidgetMaps, - featureFlagsMap, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - } = await this.workspaceCacheService.getOrRecompute(workspaceId, [ - 'flatPageLayoutMaps', - 'flatPageLayoutWidgetMaps', - 'flatPageLayoutTabMaps', - 'featureFlagsMap', - 'flatObjectMetadataMaps', - 'flatFieldMetadataMaps', - ]); - - const flatPageLayout = findFlatEntityByUniversalIdentifier({ - flatEntityMaps: fromFlatPageLayoutMaps, - universalIdentifier: - STANDARD_PAGE_LAYOUTS.myFirstDashboard.universalIdentifier, - }); - - if (isDefined(flatPageLayout)) { - this.logger.log( - `Standard page layout already exists in workspace ${workspaceId}, skipping metadata backfill`, - ); - - return; - } - - const { twentyStandardFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { - workspaceId, - }, - ); - - const shouldIncludeRecordPageLayouts = this.twentyConfigService.get( - 'SHOULD_SEED_STANDARD_RECORD_PAGE_LAYOUTS', - ); - - const { - flatPageLayoutMaps: toFlatPageLayoutMaps, - flatPageLayoutTabMaps: toFlatPageLayoutTabMaps, - flatPageLayoutWidgetMaps: rawToFlatPageLayoutWidgetMaps, - } = computeTwentyStandardApplicationAllFlatEntityMaps({ - now: new Date().toISOString(), - workspaceId, - twentyStandardApplicationId: twentyStandardFlatApplication.id, - shouldIncludeRecordPageLayouts, - }); - - const toFlatPageLayoutWidgetMaps = - updateStandardPageLayoutWidgetsWithWorkspaceFieldIds({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps: rawToFlatPageLayoutWidgetMaps, - }); - - if (options.dryRun) { - this.logger.log( - `[DRY RUN] Would create standard page layout entities ${workspaceId}`, - ); - - return; - } - - const validateAndbuildResult = - await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigrationFromTo( - { - additionalCacheDataMaps: { - featureFlagsMap, - }, - buildOptions: { - isSystemBuild: true, - }, - fromToAllFlatEntityMaps: { - flatPageLayoutMaps: { - from: fromFlatPageLayoutMaps, - to: toFlatPageLayoutMaps, - }, - flatPageLayoutTabMaps: { - from: fromFlatPageLayoutTabMaps, - to: toFlatPageLayoutTabMaps, - }, - flatPageLayoutWidgetMaps: { - from: fromFlatPageLayoutWidgetMaps, - to: toFlatPageLayoutWidgetMaps, - }, - }, - workspaceId, - dependencyAllFlatEntityMaps: { - flatObjectMetadataMaps, - }, - applicationUniversalIdentifier: - twentyStandardFlatApplication.universalIdentifier, - }, - ); - - if (isDefined(validateAndbuildResult)) { - this.logger.error( - `Failed to create many fields \n ${JSON.stringify(validateAndbuildResult, null, 2)}`, - ); - throw new Error( - `Failed to create standard page layout/tab/widgets on workspace ${workspaceId}`, - ); - } - - this.logger.log( - `Successfully backfilled standard page layout metadata for workspace ${workspaceId}`, - ); - } - - private async backfillDashboardRecord({ - workspaceId, - options, - }: { - workspaceId: string; - options: RunOnWorkspaceArgs['options']; - }): Promise { - const dashboardRepository = - await this.twentyORMGlobalManager.getRepository( - workspaceId, - 'dashboard', - { shouldBypassPermissionChecks: true }, - ); - - const existingDashboard = await dashboardRepository.findOne({ - where: { id: MY_FIRST_DASHBOARD_ID }, - }); - - if (isDefined(existingDashboard)) { - this.logger.log( - `Dashboard already exists for workspace ${workspaceId}, skipping dashboard backfill`, - ); - - return; - } - - if (options.dryRun) { - this.logger.log( - `[DRY RUN] Would create dashboard record for workspace ${workspaceId}`, - ); - - return; - } - - const { flatPageLayoutMaps } = - await this.workspaceCacheService.getOrRecompute(workspaceId, [ - 'flatPageLayoutMaps', - ]); - - const myFirstDashboardPageLayout = findFlatEntityByUniversalIdentifier({ - flatEntityMaps: flatPageLayoutMaps, - universalIdentifier: - STANDARD_PAGE_LAYOUTS.myFirstDashboard.universalIdentifier, - }); - - if (!isDefined(myFirstDashboardPageLayout)) { - throw new Error( - `Page layout with universalIdentifier '${STANDARD_PAGE_LAYOUTS.myFirstDashboard.universalIdentifier}' not found`, - ); - } - - await dashboardRepository.insert({ - id: MY_FIRST_DASHBOARD_ID, - title: 'My First Dashboard', - pageLayoutId: myFirstDashboardPageLayout.id, - position: 0, - createdBy: { - source: FieldActorSource.SYSTEM, - workspaceMemberId: null, - name: 'System', - context: {}, - }, - updatedBy: { - source: FieldActorSource.SYSTEM, - workspaceMemberId: null, - name: 'System', - context: {}, - }, - }); - - this.logger.log( - `Successfully backfilled dashboard record for workspace ${workspaceId}`, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-flush-v2-cache-and-increment-metadata-version.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-flush-v2-cache-and-increment-metadata-version.command.ts deleted file mode 100644 index f579badc4f..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-flush-v2-cache-and-increment-metadata-version.command.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { 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 { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { ALL_FLAT_ENTITY_MAPS_PROPERTIES } from 'src/engine/metadata-modules/flat-entity/constant/all-flat-entity-maps-properties.constant'; -import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; -import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service'; - -@Command({ - name: 'upgrade:1-16:flush-v2-cache-and-increment-metadata-version', - description: 'Flush the whole v2 cache and increment the metadata version', -}) -export class FlushV2CacheAndIncrementMetadataVersionCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, - ) { - super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Flushing v2 cache and incrementing metadata version for workspace ${workspaceId}`, - ); - - if (options.dryRun) { - this.logger.log( - `DRY RUN: Would flush v2 cache and increment metadata version for workspace ${workspaceId}`, - ); - - return; - } - - await this.workspaceMigrationRunnerService.invalidateCache({ - allFlatEntityMapsKeys: ALL_FLAT_ENTITY_MAPS_PROPERTIES, - workspaceId, - }); - - this.logger.log( - `Successfully flushed v2 cache and incremented metadata version for workspace ${workspaceId}`, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command.ts deleted file mode 100644 index 3e7ce67784..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command.ts +++ /dev/null @@ -1,168 +0,0 @@ -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/services/application.service'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.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 { 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_AGENT } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-agent.constant'; - -@Command({ - name: 'upgrade:1-16:identify-agent-metadata', - description: 'Identify standard agent metadata', -}) -export class IdentifyAgentMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - @InjectRepository(AgentEntity) - private readonly agentRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - protected readonly applicationService: ApplicationService, - protected readonly workspaceCacheService: WorkspaceCacheService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Running identify standard agent metadata for workspace ${workspaceId}`, - ); - - const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspaceId }, - ); - - await this.identifyStandardAgent({ - workspaceId, - twentyStandardApplicationId: twentyStandardFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - await this.identifyCustomAgents({ - workspaceId, - workspaceCustomApplicationId: workspaceCustomFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - const relatedMetadataNames = getMetadataRelatedMetadataNames('agent'); - const relatedCacheKeysToInvalidate = relatedMetadataNames.map( - getMetadataFlatEntityMapsKey, - ); - - this.logger.log( - `Invalidating caches: flatAgentMaps ${relatedCacheKeysToInvalidate.join(' ')}`, - ); - if (!options.dryRun) { - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatAgentMaps', - ...relatedCacheKeysToInvalidate, - ]); - } - } - - private async identifyStandardAgent({ - workspaceId, - twentyStandardApplicationId, - dryRun, - }: { - workspaceId: string; - twentyStandardApplicationId: string; - dryRun: boolean; - }): Promise { - const helperAgent = await this.agentRepository.findOne({ - select: { - id: true, - name: true, - universalIdentifier: true, - applicationId: true, - }, - where: { - workspaceId, - name: 'helper', - isCustom: false, - }, - withDeleted: true, - }); - - if (!isDefined(helperAgent)) { - this.logger.warn( - `Standard agent "helper" not found for workspace ${workspaceId}, skipping standard agent identification`, - ); - - return; - } - - if (isDefined(helperAgent.applicationId)) { - this.logger.warn( - `Standard agent "helper" already has applicationId set, skipping`, - ); - - return; - } - - this.logger.log( - ` - Standard agent "helper" (id=${helperAgent.id}) -> universalIdentifier=${STANDARD_AGENT.helper.universalIdentifier}`, - ); - - if (!dryRun) { - await this.agentRepository.save({ - id: helperAgent.id, - universalIdentifier: STANDARD_AGENT.helper.universalIdentifier, - applicationId: twentyStandardApplicationId, - }); - } - } - - private async identifyCustomAgents({ - workspaceId, - workspaceCustomApplicationId, - dryRun, - }: { - workspaceId: string; - workspaceCustomApplicationId: string; - dryRun: boolean; - }): Promise { - const remainingCustomAgents = await this.agentRepository.find({ - select: { - id: true, - universalIdentifier: true, - applicationId: true, - }, - where: { - workspaceId, - applicationId: IsNull(), - }, - withDeleted: true, - }); - - const customUpdates = remainingCustomAgents.map((agentEntity) => ({ - id: agentEntity.id, - universalIdentifier: agentEntity.universalIdentifier ?? v4(), - applicationId: workspaceCustomApplicationId, - })); - - this.logger.log( - `Found ${customUpdates.length} custom agent(s) to update for workspace ${workspaceId}`, - ); - - if (!dryRun) { - await this.agentRepository.save(customUpdates); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-field-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-field-metadata.command.ts deleted file mode 100644 index 3b87a6e909..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-field-metadata.command.ts +++ /dev/null @@ -1,241 +0,0 @@ -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/services/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 { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -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 { 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 CustomFieldMetadata = { - fieldMetadataEntity: FieldMetadataEntity; - fromStandard: boolean; -}; - -type StandardFieldMetadata = { - fieldMetadataEntity: FieldMetadataEntity; - universalIdentifier: string; -}; - -type AllWarnings = 'unknown_standard_id'; - -type FieldMetadataWarning = { - fieldMetadataEntity: FieldMetadataEntity; - warning: AllWarnings; -}; - -type AllExceptions = 'existing_universal_id_mismatch'; - -type FieldMetadataException = { - fieldMetadataEntity: FieldMetadataEntity; - exception: AllExceptions; -}; - -@Command({ - name: 'upgrade:1-16:identify-field-metadata', - description: 'Identify standard field metadata', -}) -export class IdentifyFieldMetadataCommand extends WorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - @InjectRepository(FieldMetadataEntity) - private readonly fieldMetadataRepository: 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, - WorkspaceActivationStatus.PENDING_CREATION, - ]); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Running identify standard field metadata for workspace ${workspaceId}`, - ); - - const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspaceId }, - ); - - const allFieldMetadataEntities = await this.fieldMetadataRepository.find({ - select: { - id: true, - universalIdentifier: true, - applicationId: true, - name: true, - standardId: true, - object: { - nameSingular: true, - }, - isCustom: true, - }, - relations: ['object'], - where: { - workspaceId, - applicationId: IsNull(), - }, - }); - - const customFieldMetadataEntities: CustomFieldMetadata[] = []; - const standardFieldMetadataEntities: StandardFieldMetadata[] = []; - const warnings: FieldMetadataWarning[] = []; - const exceptions: FieldMetadataException[] = []; - - for (const fieldMetadataEntity of allFieldMetadataEntities) { - const shouldBelongToTwentyStandardApp = - !fieldMetadataEntity.isCustom && - isDefined(fieldMetadataEntity.standardId); - - if (!shouldBelongToTwentyStandardApp) { - customFieldMetadataEntities.push({ - fieldMetadataEntity, - fromStandard: false, - }); - - continue; - } - - const objectConfig = - STANDARD_OBJECTS[ - fieldMetadataEntity.object - .nameSingular as keyof typeof STANDARD_OBJECTS - ]; - const universalIdentifier = - objectConfig?.fields[ - fieldMetadataEntity.name as keyof typeof objectConfig.fields - ]?.universalIdentifier; - - if (!isDefined(universalIdentifier)) { - warnings.push({ - fieldMetadataEntity, - warning: 'unknown_standard_id', - }); - customFieldMetadataEntities.push({ - fieldMetadataEntity, - fromStandard: true, - }); - continue; - } - - if ( - isDefined(fieldMetadataEntity.universalIdentifier) && - fieldMetadataEntity.universalIdentifier !== universalIdentifier - ) { - exceptions.push({ - fieldMetadataEntity, - exception: 'existing_universal_id_mismatch', - }); - continue; - } - - standardFieldMetadataEntities.push({ - fieldMetadataEntity, - universalIdentifier: - fieldMetadataEntity.universalIdentifier ?? universalIdentifier, - }); - } - - const totalUpdates = - customFieldMetadataEntities.length + standardFieldMetadataEntities.length; - - if (warnings.length > 0) { - this.logger.warn( - `Found ${warnings.length} warning(s) while processing field metadata for workspace ${workspaceId}. These fields will become custom.`, - ); - - for (const { fieldMetadataEntity, warning } of warnings) { - this.logger.warn( - `Warning for field "${fieldMetadataEntity.name}" on object "${fieldMetadataEntity.object.nameSingular}" (id=${fieldMetadataEntity.id} standardId=${fieldMetadataEntity.standardId}): ${warning}`, - ); - } - } - - if (exceptions.length > 0) { - this.logger.error( - `Found ${exceptions.length} exception(s) while processing field metadata for workspace ${workspaceId}. No updates will be applied.`, - ); - - for (const { fieldMetadataEntity, exception } of exceptions) { - this.logger.error( - `Exception for field "${fieldMetadataEntity.name}" on object "${fieldMetadataEntity.object.nameSingular}" (id=${fieldMetadataEntity.id} standardId=${fieldMetadataEntity.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}/${allFieldMetadataEntities.length} field metadata update(s) for workspace ${workspaceId} (${customFieldMetadataEntities.length} custom, ${standardFieldMetadataEntities.length} standard)`, - ); - - if (!options.dryRun) { - const customUpdates = customFieldMetadataEntities.map( - ({ fieldMetadataEntity }) => ({ - id: fieldMetadataEntity.id, - universalIdentifier: fieldMetadataEntity.universalIdentifier ?? v4(), - applicationId: workspaceCustomFlatApplication.id, - }), - ); - - const standardUpdates = standardFieldMetadataEntities.map( - ({ fieldMetadataEntity, universalIdentifier }) => ({ - id: fieldMetadataEntity.id, - universalIdentifier, - applicationId: twentyStandardFlatApplication.id, - }), - ); - - await this.fieldMetadataRepository.save([ - ...customUpdates, - ...standardUpdates, - ]); - - const relatedMetadataNames = - getMetadataRelatedMetadataNames('fieldMetadata'); - const relatedCacheKeysToInvalidate = relatedMetadataNames.map( - getMetadataFlatEntityMapsKey, - ); - - this.logger.log( - `Invalidating caches: flatFieldMetadataMaps ${relatedCacheKeysToInvalidate.join(' ')}`, - ); - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatFieldMetadataMaps', - ...relatedCacheKeysToInvalidate, - ]); - - this.logger.log( - `Applied ${totalUpdates} field metadata update(s) for workspace ${workspaceId}`, - ); - } else { - this.logger.log( - `Dry run: would apply ${totalUpdates} field metadata update(s) for workspace ${workspaceId}`, - ); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-index-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-index-metadata.command.ts deleted file mode 100644 index 14cbef1c41..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-index-metadata.command.ts +++ /dev/null @@ -1,341 +0,0 @@ -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/services/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 FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type'; -import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; -import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.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'; - -import { STANDARD_INDEX_FIELD_UNIVERSAL_IDENTIFIERS } from './constants/standard-index-field-names.constant'; - -type StandardIndexUpdate = { - flatIndexMetadata: FlatIndexMetadata; - universalIdentifier: string; - objectNameSingular: string; - indexName: string; -}; - -@Command({ - name: 'upgrade:1-16:identify-index-metadata', - description: 'Identify standard index metadata', -}) -export class IdentifyIndexMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - @InjectRepository(IndexMetadataEntity) - private readonly indexMetadataRepository: 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 index metadata for workspace ${workspaceId}`, - ); - - const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspaceId }, - ); - - const { flatObjectMetadataMaps, flatFieldMetadataMaps, flatIndexMaps } = - await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( - { - workspaceId, - flatMapsKeys: [ - 'flatObjectMetadataMaps', - 'flatFieldMetadataMaps', - 'flatIndexMaps', - ], - }, - ); - - await this.identifyStandardIndexesOrThrow({ - workspaceId, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - flatIndexMaps, - twentyStandardApplicationId: twentyStandardFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - await this.identifyCustomIndexes({ - workspaceId, - flatObjectMetadataMaps, - workspaceCustomApplicationId: workspaceCustomFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - const relatedMetadataNames = getMetadataRelatedMetadataNames('index'); - const relatedCacheKeysToInvalidate = relatedMetadataNames.map( - getMetadataFlatEntityMapsKey, - ); - - this.logger.log( - `Invalidating caches: ${relatedCacheKeysToInvalidate.join(' ')}`, - ); - if (!options.dryRun) { - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatIndexMaps', - ...relatedCacheKeysToInvalidate, - ]); - } - } - - private async identifyStandardIndexesOrThrow({ - workspaceId, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - flatIndexMaps, - twentyStandardApplicationId, - dryRun, - }: { - workspaceId: string; - flatObjectMetadataMaps: FlatEntityMaps; - flatFieldMetadataMaps: FlatEntityMaps; - flatIndexMaps: FlatEntityMaps; - twentyStandardApplicationId: string; - dryRun: boolean; - }): Promise { - const standardIndexUpdates: StandardIndexUpdate[] = []; - - for (const [objectNameSingular, objectConfig] of Object.entries( - STANDARD_OBJECTS, - )) { - const objectIndexes = - 'indexes' in objectConfig - ? (objectConfig.indexes as Record< - string, - { universalIdentifier: string } | undefined - >) - : null; - - if ( - !isDefined(objectIndexes) || - Object.keys(objectIndexes).length === 0 - ) { - continue; - } - - const flatObjectMetadata = findFlatEntityByUniversalIdentifier({ - flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: objectConfig.universalIdentifier, - }); - - if (!isDefined(flatObjectMetadata)) { - this.logger.error( - `Standard object "${objectNameSingular}" not found in workspace, this needs investigation, skipping`, - ); - continue; - } - - const objectFlatIndexMetadatas = - findManyFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityIds: flatObjectMetadata.indexMetadataIds, - flatEntityMaps: flatIndexMaps, - }); - - for (const [indexName, indexConfig] of Object.entries(objectIndexes)) { - if (!isDefined(indexConfig)) { - continue; - } - - const indexUniversalIdentifier = indexConfig.universalIdentifier; - - if (!isDefined(indexUniversalIdentifier)) { - this.logger.warn( - `Index "${indexName}" config not found for object "${objectNameSingular}", skipping`, - ); - continue; - } - - const relatedFieldUniversalIdentifiers = - STANDARD_INDEX_FIELD_UNIVERSAL_IDENTIFIERS[objectNameSingular]?.[ - indexName - ]; - - if ( - !isDefined(relatedFieldUniversalIdentifiers) || - relatedFieldUniversalIdentifiers.length === 0 - ) { - this.logger.warn( - `No field mapping found for index "${indexName}" on object "${objectNameSingular}", skipping`, - ); - continue; - } - - const expectedFlatFieldMetadatas = relatedFieldUniversalIdentifiers - .map((fieldUniversalIdentifier) => - findFlatEntityByUniversalIdentifier({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: fieldUniversalIdentifier, - }), - ) - .filter(isDefined); - - if ( - expectedFlatFieldMetadatas.length !== - relatedFieldUniversalIdentifiers.length - ) { - this.logger.warn( - `Could not resolve all field metadata for index "${indexName}" on object "${objectNameSingular}", skipping`, - ); - continue; - } - - const expectedFieldMetadataIds = new Set( - expectedFlatFieldMetadatas.map((flatField) => flatField.id), - ); - const matchingFlatIndexMetadata = objectFlatIndexMetadatas.find( - (flatIndex) => { - const indexFieldMetadataIds = new Set( - flatIndex.flatIndexFieldMetadatas.map( - (indexField) => indexField.fieldMetadataId, - ), - ); - - return this.setsEqual( - indexFieldMetadataIds, - expectedFieldMetadataIds, - ); - }, - ); - - if (!isDefined(matchingFlatIndexMetadata)) { - this.logger.warn( - `Could not find matching index for "${indexName}" on object "${objectNameSingular}", skipping`, - ); - continue; - } - - if (isDefined(matchingFlatIndexMetadata.applicationId)) { - continue; - } - - standardIndexUpdates.push({ - flatIndexMetadata: matchingFlatIndexMetadata, - universalIdentifier: indexUniversalIdentifier, - objectNameSingular: flatObjectMetadata.nameSingular, - indexName, - }); - } - } - - const standardUpdates = standardIndexUpdates.map( - ({ flatIndexMetadata, universalIdentifier }) => ({ - id: flatIndexMetadata.id, - universalIdentifier, - applicationId: twentyStandardApplicationId, - }), - ); - - this.logger.log( - `Found ${standardUpdates.length} standard index(es) to update for workspace ${workspaceId}`, - ); - - for (const { - flatIndexMetadata, - universalIdentifier, - objectNameSingular, - indexName, - } of standardIndexUpdates) { - this.logger.log( - ` - Standard index "${indexName}" on object "${objectNameSingular}" (id=${flatIndexMetadata.id}) -> universalIdentifier=${universalIdentifier}`, - ); - } - - if (!dryRun) { - await this.indexMetadataRepository.save(standardUpdates); - } - } - - private async identifyCustomIndexes({ - workspaceId, - flatObjectMetadataMaps, - workspaceCustomApplicationId, - dryRun, - }: { - workspaceId: string; - flatObjectMetadataMaps: FlatEntityMaps; - workspaceCustomApplicationId: string; - dryRun: boolean; - }): Promise { - const remainingCustomIndexes = await this.indexMetadataRepository.find({ - select: { - id: true, - universalIdentifier: true, - applicationId: true, - name: true, - objectMetadataId: true, - }, - where: { - workspaceId, - applicationId: IsNull(), - }, - }); - - const customUpdates = remainingCustomIndexes.map((indexEntity) => ({ - id: indexEntity.id, - universalIdentifier: indexEntity.universalIdentifier ?? v4(), - applicationId: workspaceCustomApplicationId, - })); - - this.logger.log( - `Found ${customUpdates.length} custom index(es) to update for workspace ${workspaceId}`, - ); - - for (const indexEntity of remainingCustomIndexes) { - const flatObjectMetadata = - flatObjectMetadataMaps.byId[indexEntity.objectMetadataId]; - - this.logger.log( - ` - Custom index "${indexEntity.name}" on object "${flatObjectMetadata?.nameSingular ?? 'unknown'}" (id=${indexEntity.id})`, - ); - } - - if (!dryRun) { - await this.indexMetadataRepository.save(customUpdates); - } - } - - private setsEqual(a: Set, b: Set): boolean { - if (a.size !== b.size) { - return false; - } - - for (const value of a) { - if (!b.has(value)) { - return false; - } - } - - return true; - } -} 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 deleted file mode 100644 index 2f2502e29b..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-object-metadata.command.ts +++ /dev/null @@ -1,233 +0,0 @@ -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/services/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 { 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 shouldBelongToTwentyStandardApp = - !objectMetadataEntity.isCustom && - isDefined(objectMetadataEntity.standardId); - - if (!shouldBelongToTwentyStandardApp) { - 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 relatedCacheKeysToInvalidate = relatedMetadataNames.map( - getMetadataFlatEntityMapsKey, - ); - - this.logger.log( - `Invalidating caches: flatObjectMetadataMaps ${relatedCacheKeysToInvalidate.join(' ')}`, - ); - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatObjectMetadataMaps', - ...relatedCacheKeysToInvalidate, - ]); - - 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-identify-remaining-entities-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-remaining-entities-metadata.command.ts deleted file mode 100644 index 14eda591b1..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-remaining-entities-metadata.command.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { type AllMetadataName } from 'twenty-shared/metadata'; -import { DataSource, 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/services/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 { ALL_METADATA_ENTITY_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-entity-by-metadata-name.constant'; -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 { ViewSortEntity } from 'src/engine/metadata-modules/view-sort/entities/view-sort.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 { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface'; - -const REMAINING_ENTITIES_METADATA_NAMES = [ - 'roleTarget', - 'rowLevelPermissionPredicate', - 'rowLevelPermissionPredicateGroup', - 'viewFilterGroup', - 'logicFunction', - 'skill', - 'pageLayoutWidget', - 'pageLayout', - 'pageLayoutTab', -] as const satisfies AllMetadataName[]; - -@Command({ - name: 'upgrade:1-16:identify-remaining-entities-metadata', - description: - 'Identify remaining entities metadata (roleTarget, rowLevelPermissionPredicate, rowLevelPermissionPredicateGroup, viewFilterGroup, viewSort, logicFunction, skill, pageLayoutWidget, pageLayout, pageLayoutTab)', -}) -export class IdentifyRemainingEntitiesMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - protected readonly applicationService: ApplicationService, - protected readonly workspaceCacheService: WorkspaceCacheService, - @InjectDataSource() - private readonly coreDataSource: DataSource, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Running identify remaining entities metadata for workspace ${workspaceId}`, - ); - - const { workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspaceId }, - ); - - for (const metadataName of REMAINING_ENTITIES_METADATA_NAMES) { - await this.identifyEntitiesForMetadataName({ - metadataName, - workspaceId, - workspaceCustomApplicationId: workspaceCustomFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - } - - await this.identifyViewSortEntities({ - workspaceId, - workspaceCustomApplicationId: workspaceCustomFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - } - - private async identifyEntitiesForMetadataName({ - metadataName, - workspaceId, - workspaceCustomApplicationId, - dryRun, - }: { - metadataName: AllMetadataName; - workspaceId: string; - workspaceCustomApplicationId: string; - dryRun: boolean; - }): Promise { - const entityClass = ALL_METADATA_ENTITY_BY_METADATA_NAME[metadataName]; - const repository = this.coreDataSource.getRepository(entityClass); - - const entitiesWithoutApplicationId = await repository.find({ - select: ['id', 'universalIdentifier', 'applicationId'], - where: { - workspaceId, - applicationId: IsNull(), - }, - withDeleted: true, - }); - - if (entitiesWithoutApplicationId.length === 0) { - this.logger.log( - `No ${metadataName} entities found without applicationId for workspace ${workspaceId}`, - ); - - return; - } - - const updates = entitiesWithoutApplicationId.map( - (entity: SyncableEntity & { id: string }) => ({ - id: entity.id, - universalIdentifier: entity.universalIdentifier ?? v4(), - applicationId: workspaceCustomApplicationId, - }), - ); - - this.logger.log( - `Found ${updates.length} ${metadataName} entities to update for workspace ${workspaceId}`, - ); - - if (!dryRun) { - await repository.save(updates); - } - - const relatedMetadataNames = getMetadataRelatedMetadataNames(metadataName); - const relatedCacheKeysToInvalidate = relatedMetadataNames.map( - getMetadataFlatEntityMapsKey, - ); - - const flatEntityMapsKey = getMetadataFlatEntityMapsKey(metadataName); - - this.logger.log( - `Invalidating caches: ${flatEntityMapsKey} ${relatedCacheKeysToInvalidate.join(' ')}`, - ); - - if (!dryRun) { - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - flatEntityMapsKey, - ...relatedCacheKeysToInvalidate, - ]); - } - } - - private async identifyViewSortEntities({ - workspaceId, - workspaceCustomApplicationId, - dryRun, - }: { - workspaceId: string; - workspaceCustomApplicationId: string; - dryRun: boolean; - }): Promise { - const viewSortRepository = - this.coreDataSource.getRepository(ViewSortEntity); - - const viewSortsWithoutApplicationId = await viewSortRepository.find({ - select: ['id', 'universalIdentifier', 'applicationId'], - where: { - workspaceId, - applicationId: IsNull(), - }, - withDeleted: true, - }); - - if (viewSortsWithoutApplicationId.length === 0) { - this.logger.log( - `No viewSort entities found without applicationId for workspace ${workspaceId}`, - ); - - return; - } - - const updates = viewSortsWithoutApplicationId.map((viewSort) => ({ - id: viewSort.id, - universalIdentifier: viewSort.universalIdentifier ?? v4(), - applicationId: workspaceCustomApplicationId, - })); - - this.logger.log( - `Found ${updates.length} viewSort entities to update for workspace ${workspaceId}`, - ); - - if (!dryRun) { - await viewSortRepository.save(updates); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-role-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-role-metadata.command.ts deleted file mode 100644 index a5a62c2ac2..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-role-metadata.command.ts +++ /dev/null @@ -1,166 +0,0 @@ -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/services/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 { RoleEntity } from 'src/engine/metadata-modules/role/role.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_ROLE } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-role.constant'; - -@Command({ - name: 'upgrade:1-16:identify-role-metadata', - description: 'Identify standard role metadata', -}) -export class IdentifyRoleMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - @InjectRepository(RoleEntity) - private readonly roleRepository: Repository, - protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - protected readonly applicationService: ApplicationService, - protected readonly workspaceCacheService: WorkspaceCacheService, - ) { - super(workspaceRepository, twentyORMGlobalManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Running identify standard role metadata for workspace ${workspaceId}`, - ); - - const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspaceId }, - ); - - await this.identifyStandardRole({ - workspaceId, - twentyStandardApplicationId: twentyStandardFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - await this.identifyCustomRoles({ - workspaceId, - workspaceCustomApplicationId: workspaceCustomFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - const relatedMetadataNames = getMetadataRelatedMetadataNames('role'); - const relatedCacheKeysToInvalidate = relatedMetadataNames.map( - getMetadataFlatEntityMapsKey, - ); - - this.logger.log( - `Invalidating caches: flatRoleMaps ${relatedCacheKeysToInvalidate.join(' ')}`, - ); - if (!options.dryRun) { - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatRoleMaps', - ...relatedCacheKeysToInvalidate, - ]); - } - } - - private async identifyStandardRole({ - workspaceId, - twentyStandardApplicationId, - dryRun, - }: { - workspaceId: string; - twentyStandardApplicationId: string; - dryRun: boolean; - }): Promise { - const adminRole = await this.roleRepository.findOne({ - select: { - id: true, - label: true, - universalIdentifier: true, - applicationId: true, - }, - where: { - workspaceId, - label: 'Admin', - isEditable: false, - }, - }); - - if (!isDefined(adminRole)) { - this.logger.warn( - `Standard role "Admin" not found for workspace ${workspaceId}, skipping standard role identification`, - ); - - return; - } - - if (isDefined(adminRole.applicationId)) { - this.logger.warn( - `Standard role "Admin" already has applicationId set, skipping`, - ); - - return; - } - - this.logger.log( - ` - Standard role "Admin" (id=${adminRole.id}) -> universalIdentifier=${STANDARD_ROLE.admin.universalIdentifier}`, - ); - - if (!dryRun) { - await this.roleRepository.save({ - id: adminRole.id, - universalIdentifier: STANDARD_ROLE.admin.universalIdentifier, - applicationId: twentyStandardApplicationId, - }); - } - } - - private async identifyCustomRoles({ - workspaceId, - workspaceCustomApplicationId, - dryRun, - }: { - workspaceId: string; - workspaceCustomApplicationId: string; - dryRun: boolean; - }): Promise { - const remainingCustomRoles = await this.roleRepository.find({ - select: { - id: true, - universalIdentifier: true, - applicationId: true, - }, - where: { - workspaceId, - applicationId: IsNull(), - }, - }); - - const customUpdates = remainingCustomRoles.map((roleEntity) => ({ - id: roleEntity.id, - universalIdentifier: roleEntity.universalIdentifier ?? v4(), - applicationId: workspaceCustomApplicationId, - })); - - this.logger.log( - `Found ${customUpdates.length} custom role(s) to update for workspace ${workspaceId}`, - ); - - if (!dryRun) { - await this.roleRepository.save(customUpdates); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-field-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-field-metadata.command.ts deleted file mode 100644 index e522afe7d2..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-field-metadata.command.ts +++ /dev/null @@ -1,383 +0,0 @@ -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/services/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 FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; -import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type'; -import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; -import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.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'; - -type StandardViewFieldUpdate = { - flatViewField: FlatViewField; - universalIdentifier: string; - objectNameSingular: string; - viewName: string; - fieldName: string; -}; - -type AllExceptions = 'unknown_standard_view_field'; - -type ViewFieldMetadataException = { - flatViewField: FlatViewField; - exception: AllExceptions; - objectNameSingular: string; - viewName: string; - fieldName: string; -}; - -@Command({ - name: 'upgrade:1-16:identify-view-field-metadata', - description: 'Identify standard view field metadata', -}) -export class IdentifyViewFieldMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - @InjectRepository(ViewFieldEntity) - private readonly viewFieldRepository: 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 field metadata for workspace ${workspaceId}`, - ); - - const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspaceId }, - ); - - const { - flatObjectMetadataMaps, - flatFieldMetadataMaps, - flatViewMaps, - flatViewFieldMaps, - } = - await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( - { - workspaceId, - flatMapsKeys: [ - 'flatObjectMetadataMaps', - 'flatFieldMetadataMaps', - 'flatViewMaps', - 'flatViewFieldMaps', - ], - }, - ); - - await this.identifyStandardViewFieldsOrThrow({ - workspaceId, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - flatViewMaps, - flatViewFieldMaps, - twentyStandardApplicationId: twentyStandardFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - await this.identifyCustomViewFields({ - workspaceId, - flatObjectMetadataMaps, - flatViewMaps, - flatFieldMetadataMaps, - workspaceCustomApplicationId: workspaceCustomFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - const relatedMetadataNames = getMetadataRelatedMetadataNames('viewField'); - const relatedCacheKeysToInvalidate = relatedMetadataNames.map( - getMetadataFlatEntityMapsKey, - ); - - this.logger.log( - `Invalidating caches: ${relatedCacheKeysToInvalidate.join(' ')}`, - ); - if (!options.dryRun) { - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatViewFieldMaps', - ...relatedCacheKeysToInvalidate, - ]); - } - } - - private async identifyStandardViewFieldsOrThrow({ - workspaceId, - flatObjectMetadataMaps, - flatFieldMetadataMaps, - flatViewMaps, - flatViewFieldMaps, - twentyStandardApplicationId, - dryRun, - }: { - workspaceId: string; - flatObjectMetadataMaps: FlatEntityMaps; - flatFieldMetadataMaps: FlatEntityMaps; - flatViewMaps: FlatEntityMaps; - flatViewFieldMaps: FlatEntityMaps; - twentyStandardApplicationId: string; - dryRun: boolean; - }): Promise { - const standardViewFieldUpdates: StandardViewFieldUpdate[] = []; - const exceptions: ViewFieldMetadataException[] = []; - - for (const [objectNameSingular, objectConfig] of Object.entries( - STANDARD_OBJECTS, - )) { - const objectViews = - 'views' in objectConfig - ? (objectConfig.views as Record< - string, - | { - universalIdentifier: string; - viewFields?: Record< - string, - { universalIdentifier: string } | undefined - >; - } - | undefined - >) - : null; - - if (!isDefined(objectViews)) { - continue; - } - - const flatObjectMetadata = findFlatEntityByUniversalIdentifier({ - flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: objectConfig.universalIdentifier, - }); - - if (!isDefined(flatObjectMetadata)) { - this.logger.error( - `Standard object "${objectNameSingular}" not found in workspace, this needs investigation, skipping`, - ); - continue; - } - - // Iterate over view configs and find views by their universalIdentifier - // (views have already been identified by the view identification command) - for (const [viewName, viewConfig] of Object.entries(objectViews)) { - if (!isDefined(viewConfig) || !isDefined(viewConfig.viewFields)) { - continue; - } - - const flatView = findFlatEntityByUniversalIdentifier({ - flatEntityMaps: flatViewMaps, - universalIdentifier: viewConfig.universalIdentifier, - }); - - if (!isDefined(flatView)) { - this.logger.warn( - `Standard view "${viewName}" not found for object "${flatObjectMetadata.nameSingular}", skipping view fields`, - ); - continue; - } - - const relatedFlatViewFields = - findManyFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityIds: flatView.viewFieldIds, - flatEntityMaps: flatViewFieldMaps, - }); - - // Iterate over expected view fields from config - for (const [fieldName, viewFieldConfig] of Object.entries( - viewConfig.viewFields, - )) { - if (!isDefined(viewFieldConfig)) { - continue; - } - - const fieldUniversalIdentifier = - objectConfig.fields[fieldName as keyof typeof objectConfig.fields] - ?.universalIdentifier; - - if (!isDefined(fieldUniversalIdentifier)) { - this.logger.warn( - `Field "${fieldName}" config not found for object "${flatObjectMetadata.nameSingular}", skipping view field`, - ); - continue; - } - - const viewFieldUniversalIdentifier = - viewConfig.viewFields[fieldName]?.universalIdentifier; - - if (!isDefined(viewFieldUniversalIdentifier)) { - this.logger.warn( - `View field for field "${fieldName}" config not found for object "${flatObjectMetadata.nameSingular}", skipping view field`, - ); - continue; - } - - // Find the field metadata by universal identifier - const flatFieldMetadata = findFlatEntityByUniversalIdentifier({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: fieldUniversalIdentifier, - }); - - if (!isDefined(flatFieldMetadata)) { - this.logger.warn( - `Field "${fieldName}" not found in workspace for object "${flatObjectMetadata.nameSingular}", skipping view field`, - ); - continue; - } - - // Find the existing view field that matches this field - const matchingFlatViewField = relatedFlatViewFields.find( - (viewField) => viewField.fieldMetadataId === flatFieldMetadata.id, - ); - - if ( - !isDefined(matchingFlatViewField) || - isDefined(matchingFlatViewField.applicationId) - ) { - continue; - } - - standardViewFieldUpdates.push({ - flatViewField: matchingFlatViewField, - universalIdentifier: viewFieldUniversalIdentifier, - objectNameSingular: flatObjectMetadata.nameSingular, - viewName: flatView.name, - fieldName, - }); - } - } - } - - if (exceptions.length > 0) { - this.logger.error( - `Found ${exceptions.length} exception(s) while processing view field metadata for workspace ${workspaceId}. No updates will be applied.`, - ); - - for (const { - flatViewField, - exception, - objectNameSingular, - viewName, - fieldName, - } of exceptions) { - this.logger.error( - `Exception for view field "${fieldName}" on view "${viewName}" of object "${objectNameSingular}" (id=${flatViewField.id}): ${exception}`, - ); - } - - throw new Error( - `Aborting migration for workspace ${workspaceId} due to ${exceptions.length} exception(s). See logs above for details.`, - ); - } - - const standardUpdates = standardViewFieldUpdates.map( - ({ flatViewField, universalIdentifier }) => ({ - id: flatViewField.id, - universalIdentifier, - applicationId: twentyStandardApplicationId, - }), - ); - - this.logger.log( - `Found ${standardUpdates.length} standard view field(s) to update for workspace ${workspaceId}`, - ); - - for (const { - flatViewField, - universalIdentifier, - objectNameSingular, - viewName, - fieldName, - } of standardViewFieldUpdates) { - this.logger.log( - ` - Standard view field "${fieldName}" on view "${viewName}" of object "${objectNameSingular}" (id=${flatViewField.id}) -> universalIdentifier=${universalIdentifier}`, - ); - } - - if (!dryRun) { - await this.viewFieldRepository.save(standardUpdates); - } - } - - private async identifyCustomViewFields({ - workspaceId, - flatObjectMetadataMaps, - flatViewMaps, - flatFieldMetadataMaps, - workspaceCustomApplicationId, - dryRun, - }: { - workspaceId: string; - flatObjectMetadataMaps: FlatEntityMaps; - flatViewMaps: FlatEntityMaps; - flatFieldMetadataMaps: FlatEntityMaps; - workspaceCustomApplicationId: string; - dryRun: boolean; - }): Promise { - const remainingCustomViewFields = await this.viewFieldRepository.find({ - select: { - id: true, - universalIdentifier: true, - applicationId: true, - viewId: true, - fieldMetadataId: true, - }, - where: { - workspaceId, - applicationId: IsNull(), - }, - withDeleted: true, - }); - - const customUpdates = remainingCustomViewFields.map((viewFieldEntity) => ({ - id: viewFieldEntity.id, - universalIdentifier: viewFieldEntity.universalIdentifier ?? v4(), - applicationId: workspaceCustomApplicationId, - })); - - this.logger.log( - `Found ${customUpdates.length} custom view field(s) to update for workspace ${workspaceId}`, - ); - - for (const viewFieldEntity of remainingCustomViewFields) { - const flatView = flatViewMaps.byId[viewFieldEntity.viewId]; - const flatObjectMetadata = isDefined(flatView) - ? flatObjectMetadataMaps.byId[flatView.objectMetadataId] - : undefined; - const flatFieldMetadata = - flatFieldMetadataMaps.byId[viewFieldEntity.fieldMetadataId]; - - this.logger.log( - ` - Custom view field for field "${flatFieldMetadata?.name ?? 'unknown'}" on view "${flatView?.name ?? 'unknown'}" of object "${flatObjectMetadata?.nameSingular ?? 'unknown'}" (id=${viewFieldEntity.id})`, - ); - } - - if (!dryRun) { - await this.viewFieldRepository.save(customUpdates); - } - } -} 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 deleted file mode 100644 index f4964bf143..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-filter-metadata.command.ts +++ /dev/null @@ -1,221 +0,0 @@ -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/services/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-identify-view-group-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-group-metadata.command.ts deleted file mode 100644 index 8995d2dacf..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-group-metadata.command.ts +++ /dev/null @@ -1,340 +0,0 @@ -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/services/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 FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; -import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type'; -import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; -import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.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'; - -const VIEW_GROUP_TO_FIELD_VALUE_MAPPING: Record< - string, - Record> -> = { - opportunity: { - byStage: { - new: 'NEW', - screening: 'SCREENING', - meeting: 'MEETING', - proposal: 'PROPOSAL', - customer: 'CUSTOMER', - }, - }, - task: { - assignedToMe: { - todo: 'TODO', - inProgress: 'IN_PROGRESS', - done: 'DONE', - empty: '', - }, - byStatus: { - todo: 'TODO', - inProgress: 'IN_PROGRESS', - done: 'DONE', - }, - }, -}; - -type StandardViewGroupUpdate = { - flatViewGroup: FlatViewGroup; - universalIdentifier: string; - objectNameSingular: string; - viewName: string; - viewGroupName: string; -}; - -@Command({ - name: 'upgrade:1-16:identify-view-group-metadata', - description: 'Identify standard view group metadata', -}) -export class IdentifyViewGroupMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - @InjectRepository(ViewGroupEntity) - private readonly viewGroupRepository: 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 group metadata for workspace ${workspaceId}`, - ); - - const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspaceId }, - ); - - const { flatObjectMetadataMaps, flatViewMaps, flatViewGroupMaps } = - await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( - { - workspaceId, - flatMapsKeys: [ - 'flatObjectMetadataMaps', - 'flatViewMaps', - 'flatViewGroupMaps', - ], - }, - ); - - await this.identifyStandardViewGroups({ - flatObjectMetadataMaps, - flatViewMaps, - flatViewGroupMaps, - twentyStandardApplicationId: twentyStandardFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - await this.identifyCustomViewGroups({ - workspaceId, - flatObjectMetadataMaps, - flatViewMaps, - workspaceCustomApplicationId: workspaceCustomFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - const relatedMetadataNames = getMetadataRelatedMetadataNames('viewGroup'); - const relatedCacheKeysToInvalidate = relatedMetadataNames.map( - getMetadataFlatEntityMapsKey, - ); - - this.logger.log( - `Invalidating caches: ${relatedCacheKeysToInvalidate.join(' ')}`, - ); - if (!options.dryRun) { - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatViewGroupMaps', - ...relatedCacheKeysToInvalidate, - ]); - } - } - - private async identifyStandardViewGroups({ - flatObjectMetadataMaps, - flatViewMaps, - flatViewGroupMaps, - twentyStandardApplicationId, - dryRun, - }: { - flatObjectMetadataMaps: FlatEntityMaps; - flatViewMaps: FlatEntityMaps; - flatViewGroupMaps: FlatEntityMaps; - twentyStandardApplicationId: string; - dryRun: boolean; - }): Promise { - const standardViewGroupUpdates: StandardViewGroupUpdate[] = []; - - for (const [objectNameSingular, objectConfig] of Object.entries( - STANDARD_OBJECTS, - )) { - const objectViews = - 'views' in objectConfig - ? (objectConfig.views as Record< - string, - | { - universalIdentifier: string; - viewGroups?: Record< - string, - { universalIdentifier: string } | undefined - >; - } - | undefined - >) - : null; - - if (!isDefined(objectViews)) { - continue; - } - - const flatObjectMetadata = findFlatEntityByUniversalIdentifier({ - flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: objectConfig.universalIdentifier, - }); - - if (!isDefined(flatObjectMetadata)) { - this.logger.error( - `Standard object "${objectNameSingular}" not found in workspace, this needs investigation, skipping`, - ); - continue; - } - - const objectViewGroupMapping = - VIEW_GROUP_TO_FIELD_VALUE_MAPPING[objectNameSingular]; - - for (const [viewName, viewConfig] of Object.entries(objectViews)) { - if (!isDefined(viewConfig) || !isDefined(viewConfig.viewGroups)) { - continue; - } - - const flatView = findFlatEntityByUniversalIdentifier({ - flatEntityMaps: flatViewMaps, - universalIdentifier: viewConfig.universalIdentifier, - }); - - if (!isDefined(flatView)) { - this.logger.warn( - `Standard view "${viewName}" not found for object "${flatObjectMetadata.nameSingular}", skipping view groups`, - ); - continue; - } - - const relatedFlatViewGroups = - findManyFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityIds: flatView.viewGroupIds, - flatEntityMaps: flatViewGroupMaps, - }); - - const viewGroupMapping = objectViewGroupMapping?.[viewName]; - - for (const [viewGroupName, viewGroupConfig] of Object.entries( - viewConfig.viewGroups, - )) { - if (!isDefined(viewGroupConfig)) { - continue; - } - - const fieldValue = viewGroupMapping?.[viewGroupName]; - - if (!isDefined(fieldValue) && fieldValue !== '') { - this.logger.warn( - `Field value mapping for view group "${viewGroupName}" not found for view "${viewName}" of object "${flatObjectMetadata.nameSingular}", skipping view group`, - ); - continue; - } - - const matchingFlatViewGroup = relatedFlatViewGroups.find( - (viewGroup) => viewGroup.fieldValue === fieldValue, - ); - - if (!isDefined(matchingFlatViewGroup)) { - this.logger.warn( - `Standard view group "${viewGroupName}" with fieldValue="${fieldValue}" not found for view "${viewName}" of object "${flatObjectMetadata.nameSingular}", skipping`, - ); - continue; - } - - if (isDefined(matchingFlatViewGroup.applicationId)) { - continue; - } - - standardViewGroupUpdates.push({ - flatViewGroup: matchingFlatViewGroup, - universalIdentifier: viewGroupConfig.universalIdentifier, - objectNameSingular: flatObjectMetadata.nameSingular, - viewName: flatView.name, - viewGroupName, - }); - } - } - } - - const standardUpdates = standardViewGroupUpdates.map( - ({ flatViewGroup, universalIdentifier }) => ({ - id: flatViewGroup.id, - universalIdentifier, - applicationId: twentyStandardApplicationId, - }), - ); - - this.logger.log( - `Found ${standardUpdates.length} standard view group(s) to update`, - ); - - for (const { - flatViewGroup, - universalIdentifier, - objectNameSingular, - viewName, - viewGroupName, - } of standardViewGroupUpdates) { - this.logger.log( - ` - Standard view group "${viewGroupName}" on view "${viewName}" of object "${objectNameSingular}" (id=${flatViewGroup.id}) -> universalIdentifier=${universalIdentifier}`, - ); - } - - if (!dryRun) { - await this.viewGroupRepository.save(standardUpdates); - } - } - - private async identifyCustomViewGroups({ - workspaceId, - flatObjectMetadataMaps, - flatViewMaps, - workspaceCustomApplicationId, - dryRun, - }: { - workspaceId: string; - flatObjectMetadataMaps: FlatEntityMaps; - flatViewMaps: FlatEntityMaps; - workspaceCustomApplicationId: string; - dryRun: boolean; - }): Promise { - const remainingCustomViewGroups = await this.viewGroupRepository.find({ - select: { - id: true, - universalIdentifier: true, - applicationId: true, - viewId: true, - fieldValue: true, - }, - where: { - workspaceId, - applicationId: IsNull(), - }, - withDeleted: true, - }); - - const customUpdates = remainingCustomViewGroups.map((viewGroupEntity) => ({ - id: viewGroupEntity.id, - universalIdentifier: viewGroupEntity.universalIdentifier ?? v4(), - applicationId: workspaceCustomApplicationId, - })); - - this.logger.log( - `Found ${customUpdates.length} custom view group(s) to update for workspace ${workspaceId}`, - ); - - for (const viewGroupEntity of remainingCustomViewGroups) { - const flatView = flatViewMaps.byId[viewGroupEntity.viewId]; - const flatObjectMetadata = isDefined(flatView) - ? flatObjectMetadataMaps.byId[flatView.objectMetadataId] - : undefined; - - this.logger.log( - ` - Custom view group with fieldValue="${viewGroupEntity.fieldValue}" on view "${flatView?.name ?? 'unknown'}" of object "${flatObjectMetadata?.nameSingular ?? 'unknown'}" (id=${viewGroupEntity.id})`, - ); - } - - if (!dryRun) { - await this.viewGroupRepository.save(customUpdates); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-metadata.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-metadata.command.ts deleted file mode 100644 index 6fd6c5ba37..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-identify-view-metadata.command.ts +++ /dev/null @@ -1,378 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { capitalize, isDefined, uncapitalize } 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 { ALL_ENTITY_VIEW_NAME } from 'src/database/commands/upgrade-version-command/1-16/utils/compute-formatted-view-name.util'; -import { ApplicationService } from 'src/engine/core-modules/application/services/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 FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; -import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; -import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; -import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum'; -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 StandardViewUpdate = { - flatView: FlatView; - universalIdentifier: string; - objectNameSingular: string; -}; - -type AllExceptions = 'unknown_standard_view'; - -type ViewMetadataException = { - flatView: FlatView; - exception: AllExceptions; - objectNameSingular?: string; -}; - -type DuplicateStandardView = { - skippedFlatView: FlatView; - keptFlatView: FlatView; - objectNameSingular: string; - universalIdentifier: string; -}; - -@Command({ - name: 'upgrade:1-16:identify-view-metadata', - description: 'Identify standard view metadata', -}) -export class IdentifyViewMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - @InjectRepository(ViewEntity) - private readonly viewRepository: 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 metadata for workspace ${workspaceId}`, - ); - - const { twentyStandardFlatApplication, workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspaceId }, - ); - - const { flatObjectMetadataMaps, flatViewMaps } = - await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( - { - workspaceId, - flatMapsKeys: ['flatObjectMetadataMaps', 'flatViewMaps'], - }, - ); - - await this.identifyStandardViewsOrThrow({ - workspaceId, - flatObjectMetadataMaps, - flatViewMaps, - twentyStandardApplicationId: twentyStandardFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - await this.identifyCustomViews({ - workspaceId, - flatObjectMetadataMaps, - workspaceCustomApplicationId: workspaceCustomFlatApplication.id, - dryRun: options.dryRun ?? false, - }); - - const relatedMetadataNames = getMetadataRelatedMetadataNames('view'); - const relatedCacheKeysToInvalidate = relatedMetadataNames.map( - getMetadataFlatEntityMapsKey, - ); - - this.logger.log( - `Invalidating caches: ${relatedCacheKeysToInvalidate.join(' ')}`, - ); - if (!options.dryRun) { - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'flatViewMaps', - ...relatedCacheKeysToInvalidate, - ]); - } - } - - private async identifyStandardViewsOrThrow({ - workspaceId, - flatObjectMetadataMaps, - flatViewMaps, - twentyStandardApplicationId, - dryRun, - }: { - workspaceId: string; - flatObjectMetadataMaps: FlatEntityMaps; - flatViewMaps: FlatEntityMaps; - twentyStandardApplicationId: string; - dryRun: boolean; - }): Promise { - const standardViewUpdates: StandardViewUpdate[] = []; - const exceptions: ViewMetadataException[] = []; - - for (const [objectNameSingular, objectConfig] of Object.entries( - STANDARD_OBJECTS, - )) { - const objectViews = - 'views' in objectConfig - ? (objectConfig.views as Record< - string, - { universalIdentifier: string } | undefined - >) - : null; - - if (!isDefined(objectViews)) { - continue; - } - const flatObjectMetadata = findFlatEntityByUniversalIdentifier({ - flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: objectConfig.universalIdentifier, - }); - - if (!isDefined(flatObjectMetadata)) { - this.logger.error( - `Standard object "${objectNameSingular}" not found in workspace, this needs investigation, skipping`, - ); - continue; - } - - const relatedFlatViews = findManyFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityIds: flatObjectMetadata.viewIds, - flatEntityMaps: flatViewMaps, - }); - - for (const flatView of relatedFlatViews) { - if (isDefined(flatView.applicationId)) { - continue; - } - - // INDEX views -> forward to standard (if object has views config) - if ( - flatView.key === ViewKey.INDEX && - flatView.name === ALL_ENTITY_VIEW_NAME - ) { - const formattedViewName = `all${capitalize(flatObjectMetadata.namePlural)}`; - - const viewConfig = objectViews[formattedViewName]; - const universalIdentifier = viewConfig?.universalIdentifier; - - if (!isDefined(universalIdentifier)) { - exceptions.push({ - exception: 'unknown_standard_view', - flatView, - objectNameSingular: flatObjectMetadata.nameSingular, - }); - continue; - } - - standardViewUpdates.push({ - flatView, - universalIdentifier, - objectNameSingular: flatObjectMetadata.nameSingular, - }); - continue; - } - - // Views with "All" name pattern but not INDEX -> forward to custom - if (flatView.name.startsWith('All ')) { - continue; - } - - // Remaining views (like assignedToMe, byStatus, etc.) - const formattedViewName = uncapitalize( - flatView.name.split(' ').map(capitalize).join(''), - ); - - const viewConfig = objectViews[formattedViewName]; - const universalIdentifier = viewConfig?.universalIdentifier; - - if (!isDefined(universalIdentifier)) { - continue; - } - - standardViewUpdates.push({ - flatView, - universalIdentifier, - objectNameSingular: flatObjectMetadata.nameSingular, - }); - } - } - - if (exceptions.length > 0) { - this.logger.error( - `Found ${exceptions.length} exception(s) while processing view metadata for workspace ${workspaceId}. No updates will be applied.`, - ); - - for (const { flatView, exception, objectNameSingular } of exceptions) { - this.logger.error( - ` - Exception for view "${flatView.name}" on object "${objectNameSingular ?? 'unknown'}" (id=${flatView.id}): ${exception}`, - ); - } - - throw new Error( - `Aborting migration for workspace ${workspaceId} due to ${exceptions.length} exception(s). See logs above for details.`, - ); - } - - // Detect duplicate universalIdentifiers and keep the oldest view (created during workspace setup) - const seenUniversalIdentifiers = new Map(); - const duplicates: DuplicateStandardView[] = []; - - for (const update of standardViewUpdates) { - const existingUpdate = seenUniversalIdentifiers.get( - update.universalIdentifier, - ); - - if (isDefined(existingUpdate)) { - const currentCreatedAt = new Date(update.flatView.createdAt).getTime(); - const existingCreatedAt = new Date( - existingUpdate.flatView.createdAt, - ).getTime(); - - // Keep the oldest view, skip the newer one - if (currentCreatedAt < existingCreatedAt) { - // Current is older, replace existing - duplicates.push({ - skippedFlatView: existingUpdate.flatView, - keptFlatView: update.flatView, - objectNameSingular: update.objectNameSingular, - universalIdentifier: update.universalIdentifier, - }); - seenUniversalIdentifiers.set(update.universalIdentifier, update); - } else { - // Existing is older, skip current - duplicates.push({ - skippedFlatView: update.flatView, - keptFlatView: existingUpdate.flatView, - objectNameSingular: update.objectNameSingular, - universalIdentifier: update.universalIdentifier, - }); - } - continue; - } - - seenUniversalIdentifiers.set(update.universalIdentifier, update); - } - - if (duplicates.length > 0) { - this.logger.warn( - `Found ${duplicates.length} duplicate standard view(s) for workspace ${workspaceId}. Keeping oldest, newer duplicates will be treated as custom views.`, - ); - - for (const { - skippedFlatView, - keptFlatView, - objectNameSingular, - universalIdentifier, - } of duplicates) { - this.logger.warn( - ` - Duplicate view "${skippedFlatView.name}" on object "${objectNameSingular}" (id=${skippedFlatView.id}, createdAt=${skippedFlatView.createdAt}) skipped in favor of older view (id=${keptFlatView.id}, createdAt=${keptFlatView.createdAt}) for universalIdentifier=${universalIdentifier}`, - ); - } - } - - const deduplicatedStandardViewUpdates = Array.from( - seenUniversalIdentifiers.values(), - ); - - const standardUpdates = deduplicatedStandardViewUpdates.map( - ({ flatView, universalIdentifier }) => ({ - id: flatView.id, - universalIdentifier, - applicationId: twentyStandardApplicationId, - }), - ); - - this.logger.log( - `Found ${standardUpdates.length} standard view(s) to update for workspace ${workspaceId}`, - ); - - for (const { - flatView, - universalIdentifier, - objectNameSingular, - } of deduplicatedStandardViewUpdates) { - this.logger.log( - ` - Standard view "${flatView.name}" on object "${objectNameSingular}" (id=${flatView.id}) -> universalIdentifier=${universalIdentifier}`, - ); - } - - if (!dryRun) { - await this.viewRepository.save(standardUpdates); - } - } - - private async identifyCustomViews({ - workspaceId, - flatObjectMetadataMaps, - workspaceCustomApplicationId, - dryRun, - }: { - workspaceId: string; - flatObjectMetadataMaps: FlatEntityMaps; - workspaceCustomApplicationId: string; - dryRun: boolean; - }): Promise { - const remainingCustomViews = await this.viewRepository.find({ - select: { - id: true, - universalIdentifier: true, - applicationId: true, - name: true, - objectMetadataId: true, - isCustom: true, - }, - where: { - workspaceId, - applicationId: IsNull(), - }, - withDeleted: true, - }); - - const customUpdates = remainingCustomViews.map((viewEntity) => ({ - id: viewEntity.id, - universalIdentifier: viewEntity.universalIdentifier ?? v4(), - applicationId: workspaceCustomApplicationId, - })); - - this.logger.log( - `Found ${customUpdates.length} custom view(s) to update for workspace ${workspaceId}`, - ); - - for (const viewEntity of remainingCustomViews) { - const flatObjectMetadata = - flatObjectMetadataMaps.byId[viewEntity.objectMetadataId]; - - this.logger.log( - ` - Custom view "${viewEntity.name}" on object "${flatObjectMetadata?.nameSingular ?? 'unknown'}" (id=${viewEntity.id})`, - ); - } - - if (!dryRun) { - await this.viewRepository.save(customUpdates); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-agent-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-agent-universal-identifier-and-application-id-not-nullable-migration.command.ts deleted file mode 100644 index 0c361307d6..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-agent-universal-identifier-and-application-id-not-nullable-migration.command.ts +++ /dev/null @@ -1,71 +0,0 @@ -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 { makeAgentUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768213174274-makeAgentUniversalIdentifierAndApplicationIdNotNullable.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-agent-universal-identifier-and-application-id-not-nullable-migration', - description: - 'Make universalIdentifier and applicationId columns NOT NULL on agent table', -}) -export class MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand 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 MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - - return; - } - - if (options.dryRun) { - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await makeAgentUniversalIdentifierAndApplicationIdNotNullableQueries( - queryRunner, - ); - - await queryRunner.commitTransaction(); - this.logger.log( - 'Successfully run MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - this.hasRunOnce = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.error( - `Rolling back MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-field-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-field-metadata-universal-identifier-and-application-id-not-nullable-migration.command.ts deleted file mode 100644 index 04e066b1a5..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-field-metadata-universal-identifier-and-application-id-not-nullable-migration.command.ts +++ /dev/null @@ -1,71 +0,0 @@ -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 { makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1767277454048-makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullable.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-field-metadata-universal-identifier-and-application-id-not-nullable-migration', - description: - 'Make universalIdentifier and applicationId columns NOT NULL on fieldMetadata table', -}) -export class MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand 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 MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - - return; - } - - if (options.dryRun) { - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableQueries( - queryRunner, - ); - - await queryRunner.commitTransaction(); - this.logger.log( - 'Successfully run MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - this.hasRunOnce = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.log( - `Rollbacking MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-index-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-index-metadata-universal-identifier-and-application-id-not-nullable-migration.command.ts deleted file mode 100644 index eff95f11ec..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-index-metadata-universal-identifier-and-application-id-not-nullable-migration.command.ts +++ /dev/null @@ -1,71 +0,0 @@ -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 { makeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768830235328-makeIndexMetadataUniversalIdentifierAndApplicationIdNotNullable.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-index-metadata-universal-identifier-and-application-id-not-nullable-migration', - description: - 'Make universalIdentifier and applicationId columns NOT NULL on indexMetadata table', -}) -export class MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand 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 MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - - return; - } - - if (options.dryRun) { - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await makeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableQueries( - queryRunner, - ); - - await queryRunner.commitTransaction(); - this.logger.log( - 'Successfully run MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - this.hasRunOnce = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.error( - `Rolling back MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } -} 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 deleted file mode 100644 index 1eb4c44717..0000000000 --- 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 +++ /dev/null @@ -1,71 +0,0 @@ -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-make-remaining-entities-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-remaining-entities-universal-identifier-and-application-id-not-nullable-migration.command.ts deleted file mode 100644 index 999a23c2e6..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-remaining-entities-universal-identifier-and-application-id-not-nullable-migration.command.ts +++ /dev/null @@ -1,71 +0,0 @@ -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 { makeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768916632478-makeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullable.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-remaining-entities-universal-identifier-and-application-id-not-nullable-migration', - description: - 'Make universalIdentifier and applicationId columns NOT NULL on remaining entities (roleTarget, rowLevelPermissionPredicate, rowLevelPermissionPredicateGroup, viewFilterGroup, viewSort, cronTrigger, databaseEventTrigger, routeTrigger, logicFunction, skill, pageLayoutWidget, pageLayout, pageLayoutTab)', -}) -export class MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand 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 MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - - return; - } - - if (options.dryRun) { - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await makeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableQueries( - queryRunner, - ); - - await queryRunner.commitTransaction(); - this.logger.log( - 'Successfully run MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - this.hasRunOnce = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.error( - `Rolling back MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-role-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-role-universal-identifier-and-application-id-not-nullable-migration.command.ts deleted file mode 100644 index 13ce4db25e..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-role-universal-identifier-and-application-id-not-nullable-migration.command.ts +++ /dev/null @@ -1,71 +0,0 @@ -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 { makeRoleUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768213174275-makeRoleUniversalIdentifierAndApplicationIdNotNullable.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-role-universal-identifier-and-application-id-not-nullable-migration', - description: - 'Make universalIdentifier and applicationId columns NOT NULL on role table', -}) -export class MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand 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 MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - - return; - } - - if (options.dryRun) { - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await makeRoleUniversalIdentifierAndApplicationIdNotNullableQueries( - queryRunner, - ); - - await queryRunner.commitTransaction(); - this.logger.log( - 'Successfully run MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - this.hasRunOnce = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.error( - `Rolling back MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-view-field-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-field-universal-identifier-and-application-id-not-nullable-migration.command.ts deleted file mode 100644 index 320c13f41b..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-view-field-universal-identifier-and-application-id-not-nullable-migration.command.ts +++ /dev/null @@ -1,71 +0,0 @@ -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 { makeViewFieldUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768213174272-makeViewFieldUniversalIdentifierAndApplicationIdNotNullable.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-field-universal-identifier-and-application-id-not-nullable-migration', - description: - 'Make universalIdentifier and applicationId columns NOT NULL on viewField table', -}) -export class MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand 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 MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - - return; - } - - if (options.dryRun) { - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await makeViewFieldUniversalIdentifierAndApplicationIdNotNullableQueries( - queryRunner, - ); - - await queryRunner.commitTransaction(); - this.logger.log( - 'Successfully run MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - this.hasRunOnce = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.error( - `Rolling back MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } -} 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 deleted file mode 100644 index 0495fb09cb..0000000000 --- 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 +++ /dev/null @@ -1,71 +0,0 @@ -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-make-view-group-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-group-universal-identifier-and-application-id-not-nullable-migration.command.ts deleted file mode 100644 index b171ecc71a..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-view-group-universal-identifier-and-application-id-not-nullable-migration.command.ts +++ /dev/null @@ -1,71 +0,0 @@ -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 { makeViewGroupUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768213174274-makeViewGroupUniversalIdentifierAndApplicationIdNotNullable.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-group-universal-identifier-and-application-id-not-nullable-migration', - description: - 'Make universalIdentifier and applicationId columns NOT NULL on viewGroup table', -}) -export class MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand 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 MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - - return; - } - - if (options.dryRun) { - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await makeViewGroupUniversalIdentifierAndApplicationIdNotNullableQueries( - queryRunner, - ); - - await queryRunner.commitTransaction(); - this.logger.log( - 'Successfully run MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - this.hasRunOnce = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.error( - `Rolling back MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-view-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-universal-identifier-and-application-id-not-nullable-migration.command.ts deleted file mode 100644 index 948f443a17..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-make-view-universal-identifier-and-application-id-not-nullable-migration.command.ts +++ /dev/null @@ -1,71 +0,0 @@ -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 { makeViewUniversalIdentifierAndApplicationIdNotNullableQueries } from 'src/database/typeorm/core/migrations/utils/1768213174271-makeViewUniversalIdentifierAndApplicationIdNotNullable.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-universal-identifier-and-application-id-not-nullable-migration', - description: - 'Make universalIdentifier and applicationId columns NOT NULL on view table', -}) -export class MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand 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 MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - - return; - } - - if (options.dryRun) { - return; - } - - const queryRunner = this.coreDataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await makeViewUniversalIdentifierAndApplicationIdNotNullableQueries( - queryRunner, - ); - - await queryRunner.commitTransaction(); - this.logger.log( - 'Successfully run MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand', - ); - this.hasRunOnce = true; - } catch (error) { - await queryRunner.rollbackTransaction(); - this.logger.error( - `Rolling back MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: ${error.message}`, - ); - } finally { - await queryRunner.release(); - } - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-update-task-on-delete-action.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-update-task-on-delete-action.command.ts deleted file mode 100644 index f372ef515f..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-update-task-on-delete-action.command.ts +++ /dev/null @@ -1,341 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; -import { - FieldMetadataSettingsMapping, - FieldMetadataType, - RelationOnDeleteAction, -} from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; -import { IsNull, 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 { ApplicationService } from 'src/engine/core-modules/application/services/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 { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.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 { TASK_TARGET_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-migration/constant/standard-field-ids'; -import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; - -@Command({ - name: 'upgrade:1-16:update-task-on-delete-action', - description: - 'Update task relation onDelete action to CASCADE and delete orphaned taskTarget records', -}) -export class UpdateTaskOnDeleteActionCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { - protected readonly logger = new Logger(UpdateTaskOnDeleteActionCommand.name); - - constructor( - @InjectRepository(WorkspaceEntity) - protected readonly workspaceRepository: Repository, - protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, - protected readonly dataSourceService: DataSourceService, - private readonly workspaceCacheService: WorkspaceCacheService, - private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, - private readonly applicationService: ApplicationService, - ) { - super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService); - } - - override async runOnWorkspace({ - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - const isDryRun = options.dryRun ?? false; - - this.logger.log( - `Running UpdateTaskOnDeleteActionCommand for workspace ${workspaceId} (dryRun: ${isDryRun})`, - ); - - this.logger.log( - `[Phase 1] Starting task relation onDelete action update for workspace ${workspaceId}`, - ); - - await this.updateTaskRelationOnDeleteAction(workspaceId, isDryRun); - - this.logger.log( - `[Phase 1] Completed task relation onDelete action update for workspace ${workspaceId}`, - ); - - this.logger.log( - `[Phase 2] Starting orphaned taskTarget cleanup for workspace ${workspaceId}`, - ); - - await this.deleteOrphanedTaskTargets(workspaceId, isDryRun); - - this.logger.log( - `[Phase 2] Completed orphaned taskTarget cleanup for workspace ${workspaceId}`, - ); - - this.logger.log( - `UpdateTaskOnDeleteActionCommand completed successfully for workspace ${workspaceId}`, - ); - } - - private async updateTaskRelationOnDeleteAction( - workspaceId: string, - isDryRun: boolean, - ): Promise { - this.logger.log( - `[Step 1/4] Fetching workspace cache for workspace ${workspaceId}`, - ); - - const { flatFieldMetadataMaps, flatObjectMetadataMaps } = - await this.workspaceCacheService.getOrRecompute(workspaceId, [ - 'flatFieldMetadataMaps', - 'flatObjectMetadataMaps', - ]); - - this.logger.log( - `[Step 2/4] Looking for taskTarget object metadata in workspace ${workspaceId}`, - ); - - const taskTargetObjectMetadata = Object.values( - flatObjectMetadataMaps.byId, - ).find( - (objectMetadata) => - objectMetadata?.standardId === STANDARD_OBJECT_IDS.taskTarget, - ); - - if (!isDefined(taskTargetObjectMetadata)) { - this.logger.warn( - `TaskTarget object metadata not found in workspace ${workspaceId}`, - ); - - return; - } - - this.logger.log( - `[Step 2/4] Found taskTarget object metadata with id ${taskTargetObjectMetadata.id} that has ${taskTargetObjectMetadata.fieldIds.length} field(s) in workspace ${workspaceId}`, - ); - - this.logger.log( - `[Step 3/4] Looking for task field on taskTarget object in workspace ${workspaceId}`, - ); - - const taskTargetFields = findManyFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityIds: taskTargetObjectMetadata.fieldIds, - flatEntityMaps: flatFieldMetadataMaps, - }); - - const taskField = taskTargetFields.find( - (field) => field.standardId === TASK_TARGET_STANDARD_FIELD_IDS.task, - ); - - if (!isDefined(taskField)) { - this.logger.warn( - `Task field not found on taskTarget object in workspace ${workspaceId}`, - ); - - return; - } - - this.logger.log( - `[Step 3/4] Found task field with id ${taskField.id} in workspace ${workspaceId}`, - ); - - if (taskField.type !== FieldMetadataType.RELATION) { - this.logger.warn( - `Task field is not a relation field (type: ${taskField.type}) in workspace ${workspaceId}`, - ); - - return; - } - - const taskFieldSettings = - taskField.settings as FieldMetadataSettingsMapping['RELATION']; - - if (taskFieldSettings?.onDelete === RelationOnDeleteAction.CASCADE) { - this.logger.log( - `[Step 4/4] Task relation already has CASCADE onDelete in workspace ${workspaceId}, skipping update`, - ); - - return; - } - - this.logger.log( - `[Step 4/4] Updating task relation onDelete from ${taskFieldSettings?.onDelete ?? 'undefined'} to CASCADE in workspace ${workspaceId}`, - ); - - if (!isDryRun) { - const updatedSettings: FieldMetadataSettingsMapping['RELATION'] = { - ...taskFieldSettings, - onDelete: RelationOnDeleteAction.CASCADE, - }; - - const updatedTaskField = { - ...taskField, - settings: updatedSettings, - updatedAt: new Date().toISOString(), - }; - - this.logger.log( - `[Step 4/4] Calling validateBuildAndRunWorkspaceMigration for field ${taskField.id} in workspace ${workspaceId}`, - ); - - try { - const { twentyStandardFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { workspaceId }, - ); - - const validateAndBuildResult = - await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( - { - allFlatEntityOperationByMetadataName: { - fieldMetadata: { - flatEntityToCreate: [], - flatEntityToDelete: [], - flatEntityToUpdate: [updatedTaskField], - }, - }, - workspaceId, - isSystemBuild: true, - applicationUniversalIdentifier: - twentyStandardFlatApplication.universalIdentifier, - }, - ); - - this.logger.error( - `[Step 4/4] Validation result: ${JSON.stringify(validateAndBuildResult, null, 2)}`, - ); - - if (isDefined(validateAndBuildResult)) { - this.logger.error( - `[Step 4/4] Failed to update task relation onDelete in workspace ${workspaceId}`, - ); - - throw new Error( - `Failed to update task relation onDelete in workspace ${workspaceId}`, - ); - } - - this.logger.log( - `[Step 4/4] Successfully updated task relation onDelete to CASCADE in workspace ${workspaceId}`, - ); - } catch (error) { - this.logger.error( - `[Step 4/4] Failed to update task relation onDelete in workspace ${workspaceId}`, - ); - this.logger.error( - `[Step 4/4] Field id: ${taskField.id}, current settings: ${JSON.stringify(taskFieldSettings)}`, - ); - this.logger.error( - `[Step 4/4] Target settings: ${JSON.stringify(updatedSettings)}`, - ); - this.logger.error( - `[Step 4/4] Error message: ${error instanceof Error ? error.message : String(error)}`, - ); - this.logger.error( - `[Step 4/4] Error stack: ${error instanceof Error ? error.stack : 'No stack available'}`, - ); - - this.logger.error(JSON.stringify(error, null, 2)); - - throw error; - } - } else { - this.logger.log( - `[Step 4/4] DRY RUN: Would update task relation onDelete to CASCADE in workspace ${workspaceId}`, - ); - } - } - - private async deleteOrphanedTaskTargets( - workspaceId: string, - isDryRun: boolean, - ): Promise { - this.logger.log( - `[Orphan cleanup 1/3] Getting taskTarget repository for workspace ${workspaceId}`, - ); - - const taskTargetRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'taskTarget', - { shouldBypassPermissionChecks: true }, - ); - - this.logger.log( - `[Orphan cleanup 2/3] Searching for orphaned taskTarget records (taskId is null) in workspace ${workspaceId}`, - ); - - const orphanedTaskTargets = await taskTargetRepository.find({ - withDeleted: true, - select: ['id'], - where: { - taskId: IsNull(), - }, - }); - - const orphanedCount = orphanedTaskTargets.length; - - this.logger.log( - `[Orphan cleanup 2/3] Found ${orphanedCount} orphaned taskTarget record(s) in workspace ${workspaceId}`, - ); - - if (orphanedCount === 0) { - this.logger.log( - `[Orphan cleanup 3/3] No orphaned records to delete in workspace ${workspaceId}`, - ); - - return; - } - - if (!isDryRun) { - const orphanedIds = orphanedTaskTargets.map((record) => record.id); - - const batchSize = 100; - const totalBatches = Math.ceil(orphanedIds.length / batchSize); - - this.logger.log( - `[Orphan cleanup 3/3] Deleting ${orphanedCount} orphaned taskTarget record(s) in ${totalBatches} batch(es) in workspace ${workspaceId}`, - ); - - for (let i = 0; i < orphanedIds.length; i += batchSize) { - const batch = orphanedIds.slice(i, i + batchSize); - const batchNumber = Math.floor(i / batchSize) + 1; - - this.logger.log( - `[Orphan cleanup 3/3] Deleting batch ${batchNumber}/${totalBatches} (${batch.length} records) in workspace ${workspaceId}`, - ); - - try { - await taskTargetRepository - .createQueryBuilder() - .delete() - .whereInIds(batch) - .execute(); - } catch (error) { - this.logger.error( - `[Orphan cleanup 3/3] Failed to delete batch ${batchNumber}/${totalBatches} in workspace ${workspaceId}`, - ); - this.logger.error( - `[Orphan cleanup 3/3] Batch record ids: ${JSON.stringify(batch)}`, - ); - this.logger.error( - `[Orphan cleanup 3/3] Error message: ${error instanceof Error ? error.message : String(error)}`, - ); - this.logger.error( - `[Orphan cleanup 3/3] Error stack: ${error instanceof Error ? error.stack : 'No stack available'}`, - ); - - throw error; - } - } - - this.logger.log( - `[Orphan cleanup 3/3] Successfully deleted ${orphanedCount} orphaned taskTarget record(s) in workspace ${workspaceId}`, - ); - } else { - this.logger.log( - `[Orphan cleanup 3/3] DRY RUN: Would delete ${orphanedCount} orphaned taskTarget record(s) in workspace ${workspaceId}`, - ); - } - } -} 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 deleted file mode 100644 index c49f283d4c..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-upgrade-version-command.module.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { Module } from '@nestjs/common'; -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 { DeleteFileRecordsCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-delete-all-files.command'; -import { FlushV2CacheAndIncrementMetadataVersionCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-flush-v2-cache-and-increment-metadata-version.command'; -import { IdentifyAgentMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command'; -import { IdentifyFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-field-metadata.command'; -import { IdentifyIndexMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-index-metadata.command'; -import { IdentifyObjectMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-object-metadata.command'; -import { IdentifyRemainingEntitiesMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-remaining-entities-metadata.command'; -import { IdentifyRoleMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-role-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 { IdentifyViewGroupMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-group-metadata.command'; -import { IdentifyViewMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-metadata.command'; -import { MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-agent-universal-identifier-and-application-id-not-nullable-migration.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 { MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-index-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 { MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-remaining-entities-universal-identifier-and-application-id-not-nullable-migration.command'; -import { MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-role-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 { MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-group-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'; -import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.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 { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module'; -import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { RoleEntity } from 'src/engine/metadata-modules/role/role.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 { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.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'; -import { TwentyStandardApplicationModule } from 'src/engine/workspace-manager/twenty-standard-application/twenty-standard-application.module'; -import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/workspace-migration-runner.module'; -import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([ - WorkspaceEntity, - AgentEntity, - FieldMetadataEntity, - IndexMetadataEntity, - ObjectMetadataEntity, - RoleEntity, - ViewEntity, - ViewFieldEntity, - ViewFilterEntity, - ViewGroupEntity, - FileEntity, - ]), - DataSourceModule, - WorkspaceCacheModule, - FieldMetadataModule, - ApplicationModule, - GlobalWorkspaceDataSourceModule, - TwentyStandardApplicationModule, - WorkspaceMigrationModule, - WorkspaceMigrationRunnerModule, - WorkspaceManyOrAllFlatEntityMapsCacheModule, - ], - providers: [ - UpdateTaskOnDeleteActionCommand, - BackfillOpportunityOwnerFieldCommand, - BackfillStandardPageLayoutsCommand, - DeleteFileRecordsCommand, - IdentifyAgentMetadataCommand, - IdentifyFieldMetadataCommand, - IdentifyIndexMetadataCommand, - IdentifyObjectMetadataCommand, - IdentifyRoleMetadataCommand, - IdentifyViewMetadataCommand, - IdentifyViewFieldMetadataCommand, - IdentifyViewFilterMetadataCommand, - MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - IdentifyViewGroupMetadataCommand, - MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - IdentifyRemainingEntitiesMetadataCommand, - FlushV2CacheAndIncrementMetadataVersionCommand, - ], - exports: [ - UpdateTaskOnDeleteActionCommand, - BackfillOpportunityOwnerFieldCommand, - BackfillStandardPageLayoutsCommand, - DeleteFileRecordsCommand, - IdentifyAgentMetadataCommand, - IdentifyFieldMetadataCommand, - IdentifyIndexMetadataCommand, - IdentifyObjectMetadataCommand, - IdentifyRoleMetadataCommand, - IdentifyViewMetadataCommand, - IdentifyViewFieldMetadataCommand, - IdentifyViewFilterMetadataCommand, - MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - IdentifyViewGroupMetadataCommand, - MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - IdentifyRemainingEntitiesMetadataCommand, - FlushV2CacheAndIncrementMetadataVersionCommand, - ], -}) -export class V1_16_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/constants/standard-index-field-names.constant.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/constants/standard-index-field-names.constant.ts deleted file mode 100644 index e7fa7fa7fc..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/constants/standard-index-field-names.constant.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; - -// Maps indexName to its related field universal identifiers for each standard object -export const STANDARD_INDEX_FIELD_UNIVERSAL_IDENTIFIERS: Record< - string, - Record -> = { - attachment: { - taskIdIndex: [ - STANDARD_OBJECTS.attachment.fields.targetTask.universalIdentifier, - ], - noteIdIndex: [ - STANDARD_OBJECTS.attachment.fields.targetNote.universalIdentifier, - ], - personIdIndex: [ - STANDARD_OBJECTS.attachment.fields.targetPerson.universalIdentifier, - ], - companyIdIndex: [ - STANDARD_OBJECTS.attachment.fields.targetCompany.universalIdentifier, - ], - opportunityIdIndex: [ - STANDARD_OBJECTS.attachment.fields.targetOpportunity.universalIdentifier, - ], - dashboardIdIndex: [ - STANDARD_OBJECTS.attachment.fields.targetDashboard.universalIdentifier, - ], - workflowIdIndex: [ - STANDARD_OBJECTS.attachment.fields.targetWorkflow.universalIdentifier, - ], - }, - blocklist: { - workspaceMemberIdIndex: [ - STANDARD_OBJECTS.blocklist.fields.workspaceMember.universalIdentifier, - ], - }, - calendarChannelEventAssociation: { - calendarChannelIdIndex: [ - STANDARD_OBJECTS.calendarChannelEventAssociation.fields.calendarChannel - .universalIdentifier, - ], - calendarEventIdIndex: [ - STANDARD_OBJECTS.calendarChannelEventAssociation.fields.calendarEvent - .universalIdentifier, - ], - }, - calendarChannel: { - connectedAccountIdIndex: [ - STANDARD_OBJECTS.calendarChannel.fields.connectedAccount - .universalIdentifier, - ], - }, - calendarEventParticipant: { - calendarEventIdIndex: [ - STANDARD_OBJECTS.calendarEventParticipant.fields.calendarEvent - .universalIdentifier, - ], - personIdIndex: [ - STANDARD_OBJECTS.calendarEventParticipant.fields.person - .universalIdentifier, - ], - workspaceMemberIdIndex: [ - STANDARD_OBJECTS.calendarEventParticipant.fields.workspaceMember - .universalIdentifier, - ], - }, - company: { - accountOwnerIdIndex: [ - STANDARD_OBJECTS.company.fields.accountOwner.universalIdentifier, - ], - domainNameUniqueIndex: [ - STANDARD_OBJECTS.company.fields.domainName.universalIdentifier, - ], - searchVectorGinIndex: [ - STANDARD_OBJECTS.company.fields.searchVector.universalIdentifier, - ], - }, - connectedAccount: { - accountOwnerIdIndex: [ - STANDARD_OBJECTS.connectedAccount.fields.accountOwner.universalIdentifier, - ], - }, - dashboard: { - searchVectorGinIndex: [ - STANDARD_OBJECTS.dashboard.fields.searchVector.universalIdentifier, - ], - }, - favorite: { - forWorkspaceMemberIdIndex: [ - STANDARD_OBJECTS.favorite.fields.forWorkspaceMember.universalIdentifier, - ], - personIdIndex: [ - STANDARD_OBJECTS.favorite.fields.person.universalIdentifier, - ], - companyIdIndex: [ - STANDARD_OBJECTS.favorite.fields.company.universalIdentifier, - ], - favoriteFolderIdIndex: [ - STANDARD_OBJECTS.favorite.fields.favoriteFolder.universalIdentifier, - ], - opportunityIdIndex: [ - STANDARD_OBJECTS.favorite.fields.opportunity.universalIdentifier, - ], - workflowIdIndex: [ - STANDARD_OBJECTS.favorite.fields.workflow.universalIdentifier, - ], - workflowVersionIdIndex: [ - STANDARD_OBJECTS.favorite.fields.workflowVersion.universalIdentifier, - ], - workflowRunIdIndex: [ - STANDARD_OBJECTS.favorite.fields.workflowRun.universalIdentifier, - ], - taskIdIndex: [STANDARD_OBJECTS.favorite.fields.task.universalIdentifier], - noteIdIndex: [STANDARD_OBJECTS.favorite.fields.note.universalIdentifier], - dashboardIdIndex: [ - STANDARD_OBJECTS.favorite.fields.dashboard.universalIdentifier, - ], - }, - messageChannelMessageAssociation: { - messageChannelIdIndex: [ - STANDARD_OBJECTS.messageChannelMessageAssociation.fields.messageChannel - .universalIdentifier, - ], - messageIdIndex: [ - STANDARD_OBJECTS.messageChannelMessageAssociation.fields.message - .universalIdentifier, - ], - messageChannelIdMessageIdUniqueIndex: [ - STANDARD_OBJECTS.messageChannelMessageAssociation.fields.messageChannel - .universalIdentifier, - STANDARD_OBJECTS.messageChannelMessageAssociation.fields.message - .universalIdentifier, - ], - }, - messageChannel: { - connectedAccountIdIndex: [ - STANDARD_OBJECTS.messageChannel.fields.connectedAccount - .universalIdentifier, - ], - }, - messageFolder: { - messageChannelIdIndex: [ - STANDARD_OBJECTS.messageFolder.fields.messageChannel.universalIdentifier, - ], - }, - messageParticipant: { - messageIdIndex: [ - STANDARD_OBJECTS.messageParticipant.fields.message.universalIdentifier, - ], - personIdIndex: [ - STANDARD_OBJECTS.messageParticipant.fields.person.universalIdentifier, - ], - workspaceMemberIdIndex: [ - STANDARD_OBJECTS.messageParticipant.fields.workspaceMember - .universalIdentifier, - ], - }, - message: { - messageThreadIdIndex: [ - STANDARD_OBJECTS.message.fields.messageThread.universalIdentifier, - ], - }, - note: { - searchVectorGinIndex: [ - STANDARD_OBJECTS.note.fields.searchVector.universalIdentifier, - ], - }, - noteTarget: { - noteIdIndex: [STANDARD_OBJECTS.noteTarget.fields.note.universalIdentifier], - personIdIndex: [ - STANDARD_OBJECTS.noteTarget.fields.person.universalIdentifier, - ], - companyIdIndex: [ - STANDARD_OBJECTS.noteTarget.fields.company.universalIdentifier, - ], - opportunityIdIndex: [ - STANDARD_OBJECTS.noteTarget.fields.opportunity.universalIdentifier, - ], - }, - opportunity: { - pointOfContactIdIndex: [ - STANDARD_OBJECTS.opportunity.fields.pointOfContact.universalIdentifier, - ], - companyIdIndex: [ - STANDARD_OBJECTS.opportunity.fields.company.universalIdentifier, - ], - stageIndex: [STANDARD_OBJECTS.opportunity.fields.stage.universalIdentifier], - searchVectorGinIndex: [ - STANDARD_OBJECTS.opportunity.fields.searchVector.universalIdentifier, - ], - }, - person: { - companyIdIndex: [ - STANDARD_OBJECTS.person.fields.company.universalIdentifier, - ], - emailsUniqueIndex: [ - STANDARD_OBJECTS.person.fields.emails.universalIdentifier, - ], - searchVectorGinIndex: [ - STANDARD_OBJECTS.person.fields.searchVector.universalIdentifier, - ], - }, - task: { - assigneeIdIndex: [ - STANDARD_OBJECTS.task.fields.assignee.universalIdentifier, - ], - searchVectorGinIndex: [ - STANDARD_OBJECTS.task.fields.searchVector.universalIdentifier, - ], - }, - taskTarget: { - taskIdIndex: [STANDARD_OBJECTS.taskTarget.fields.task.universalIdentifier], - personIdIndex: [ - STANDARD_OBJECTS.taskTarget.fields.person.universalIdentifier, - ], - companyIdIndex: [ - STANDARD_OBJECTS.taskTarget.fields.company.universalIdentifier, - ], - opportunityIdIndex: [ - STANDARD_OBJECTS.taskTarget.fields.opportunity.universalIdentifier, - ], - }, - timelineActivity: { - workspaceMemberIdIndex: [ - STANDARD_OBJECTS.timelineActivity.fields.workspaceMember - .universalIdentifier, - ], - personIdIndex: [ - STANDARD_OBJECTS.timelineActivity.fields.targetPerson.universalIdentifier, - ], - companyIdIndex: [ - STANDARD_OBJECTS.timelineActivity.fields.targetCompany - .universalIdentifier, - ], - opportunityIdIndex: [ - STANDARD_OBJECTS.timelineActivity.fields.targetOpportunity - .universalIdentifier, - ], - noteIdIndex: [ - STANDARD_OBJECTS.timelineActivity.fields.targetNote.universalIdentifier, - ], - taskIdIndex: [ - STANDARD_OBJECTS.timelineActivity.fields.targetTask.universalIdentifier, - ], - workflowIdIndex: [ - STANDARD_OBJECTS.timelineActivity.fields.targetWorkflow - .universalIdentifier, - ], - workflowVersionIdIndex: [ - STANDARD_OBJECTS.timelineActivity.fields.targetWorkflowVersion - .universalIdentifier, - ], - workflowRunIdIndex: [ - STANDARD_OBJECTS.timelineActivity.fields.targetWorkflowRun - .universalIdentifier, - ], - dashboardIdIndex: [ - STANDARD_OBJECTS.timelineActivity.fields.targetDashboard - .universalIdentifier, - ], - }, - workflow: { - searchVectorGinIndex: [ - STANDARD_OBJECTS.workflow.fields.searchVector.universalIdentifier, - ], - }, - workflowAutomatedTrigger: { - workflowIdIndex: [ - STANDARD_OBJECTS.workflowAutomatedTrigger.fields.workflow - .universalIdentifier, - ], - }, - workflowRun: { - workflowVersionIdIndex: [ - STANDARD_OBJECTS.workflowRun.fields.workflowVersion.universalIdentifier, - ], - workflowIdIndex: [ - STANDARD_OBJECTS.workflowRun.fields.workflow.universalIdentifier, - ], - searchVectorGinIndex: [ - STANDARD_OBJECTS.workflowRun.fields.searchVector.universalIdentifier, - ], - }, - workflowVersion: { - workflowIdIndex: [ - STANDARD_OBJECTS.workflowVersion.fields.workflow.universalIdentifier, - ], - searchVectorGinIndex: [ - STANDARD_OBJECTS.workflowVersion.fields.searchVector.universalIdentifier, - ], - }, - workspaceMember: { - userEmailUniqueIndex: [ - STANDARD_OBJECTS.workspaceMember.fields.userEmail.universalIdentifier, - ], - searchVectorGinIndex: [ - STANDARD_OBJECTS.workspaceMember.fields.searchVector.universalIdentifier, - ], - }, -}; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/utils/compute-formatted-view-name.util.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/utils/compute-formatted-view-name.util.ts deleted file mode 100644 index cd1e87208f..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/utils/compute-formatted-view-name.util.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { capitalize, uncapitalize } from 'twenty-shared/utils'; - -import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; - -export const ALL_ENTITY_VIEW_NAME = 'All {objectLabelPlural}'; - -export const computeFormattedViewName = ({ - viewName, - flatObjectMetadata, -}: { - viewName: string; - flatObjectMetadata: FlatObjectMetadata; -}) => - viewName === ALL_ENTITY_VIEW_NAME - ? `all${capitalize(flatObjectMetadata.namePlural)}` - : uncapitalize(viewName.split(' ').map(capitalize).join('')); diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/utils/update-standard-page-layout-widgets-with-workspace-field-ids.util.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/utils/update-standard-page-layout-widgets-with-workspace-field-ids.util.ts deleted file mode 100644 index 9038587430..0000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/utils/update-standard-page-layout-widgets-with-workspace-field-ids.util.ts +++ /dev/null @@ -1,422 +0,0 @@ -import { isDefined, type RecordFilter } from 'twenty-shared/utils'; - -import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; -import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util'; -import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util'; -import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; -import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; -import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type'; -import { isFlatPageLayoutWidgetConfigurationOfType } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/is-flat-page-layout-widget-configuration-of-type.util'; -import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; -import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; -import { STANDARD_PAGE_LAYOUTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout.constant'; - -type UpdateStandardPageLayoutWidgetsArgs = { - flatFieldMetadataMaps: FlatEntityMaps; - flatObjectMetadataMaps: FlatEntityMaps; - flatPageLayoutWidgetMaps: FlatEntityMaps; -}; - -const findWidgetByUniversalIdentifierOrThrow = < - T extends WidgetConfigurationType, ->({ - flatPageLayoutWidgetMaps, - universalIdentifier, - widgetDescription, - expectedConfigurationType, -}: { - flatPageLayoutWidgetMaps: FlatEntityMaps; - universalIdentifier: string; - widgetDescription: string; - expectedConfigurationType: T; -}): FlatPageLayoutWidget => { - const flatPageLayoutWidget = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatPageLayoutWidgetMaps, - universalIdentifier, - }); - - if ( - !isFlatPageLayoutWidgetConfigurationOfType( - flatPageLayoutWidget, - expectedConfigurationType, - ) - ) { - throw new Error( - `Widget ${widgetDescription} has unexpected configuration type ${flatPageLayoutWidget.configuration.configurationType}, expected ${expectedConfigurationType}`, - ); - } - - return flatPageLayoutWidget; -}; - -// Widget updater: dealsByCompany (PIE_CHART) -const updateDealsByCompanyWidget = ({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps, -}: UpdateStandardPageLayoutWidgetsArgs): FlatEntityMaps => { - const opportunityIdField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.id.universalIdentifier, - }); - - const opportunityCompanyField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.company.universalIdentifier, - }); - - const opportunityObject = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: STANDARD_OBJECTS.opportunity.universalIdentifier, - }); - - const widget = findWidgetByUniversalIdentifierOrThrow({ - flatPageLayoutWidgetMaps, - universalIdentifier: - STANDARD_PAGE_LAYOUTS.myFirstDashboard.tabs.tab1.widgets.dealsByCompany - .universalIdentifier, - widgetDescription: 'dealsByCompany', - expectedConfigurationType: WidgetConfigurationType.PIE_CHART, - }); - - const updatedWidget: FlatPageLayoutWidget = - { - ...widget, - objectMetadataId: opportunityObject.id, - configuration: { - ...widget.configuration, - aggregateFieldMetadataId: opportunityIdField.id, - groupByFieldMetadataId: opportunityCompanyField.id, - }, - }; - - return replaceFlatEntityInFlatEntityMapsOrThrow({ - flatEntity: updatedWidget, - flatEntityMaps: flatPageLayoutWidgetMaps, - }); -}; - -// Widget updater: pipelineValueByStage (BAR_CHART) -const updatePipelineValueByStageWidget = ({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps, -}: UpdateStandardPageLayoutWidgetsArgs): FlatEntityMaps => { - const opportunityAmountField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.amount.universalIdentifier, - }); - - const opportunityStageField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.stage.universalIdentifier, - }); - - const opportunityCompanyField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.company.universalIdentifier, - }); - - const opportunityObject = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: STANDARD_OBJECTS.opportunity.universalIdentifier, - }); - - const widget = findWidgetByUniversalIdentifierOrThrow({ - flatPageLayoutWidgetMaps, - universalIdentifier: - STANDARD_PAGE_LAYOUTS.myFirstDashboard.tabs.tab1.widgets - .pipelineValueByStage.universalIdentifier, - widgetDescription: 'pipelineValueByStage', - expectedConfigurationType: WidgetConfigurationType.BAR_CHART, - }); - - const updatedWidget: FlatPageLayoutWidget = - { - ...widget, - objectMetadataId: opportunityObject.id, - configuration: { - ...widget.configuration, - aggregateFieldMetadataId: opportunityAmountField.id, - primaryAxisGroupByFieldMetadataId: opportunityStageField.id, - secondaryAxisGroupByFieldMetadataId: opportunityCompanyField.id, - }, - }; - - return replaceFlatEntityInFlatEntityMapsOrThrow({ - flatEntity: updatedWidget, - flatEntityMaps: flatPageLayoutWidgetMaps, - }); -}; - -// Widget updater: revenueTimeline (LINE_CHART) -const updateRevenueTimelineWidget = ({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps, -}: UpdateStandardPageLayoutWidgetsArgs): FlatEntityMaps => { - const opportunityAmountField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.amount.universalIdentifier, - }); - - const opportunityCloseDateField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.closeDate.universalIdentifier, - }); - - const opportunityObject = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: STANDARD_OBJECTS.opportunity.universalIdentifier, - }); - - const widget = findWidgetByUniversalIdentifierOrThrow({ - flatPageLayoutWidgetMaps, - universalIdentifier: - STANDARD_PAGE_LAYOUTS.myFirstDashboard.tabs.tab1.widgets.revenueTimeline - .universalIdentifier, - widgetDescription: 'revenueTimeline', - expectedConfigurationType: WidgetConfigurationType.LINE_CHART, - }); - - const updatedWidget: FlatPageLayoutWidget = - { - ...widget, - objectMetadataId: opportunityObject.id, - configuration: { - ...widget.configuration, - aggregateFieldMetadataId: opportunityAmountField.id, - primaryAxisGroupByFieldMetadataId: opportunityCloseDateField.id, - }, - }; - - return replaceFlatEntityInFlatEntityMapsOrThrow({ - flatEntity: updatedWidget, - flatEntityMaps: flatPageLayoutWidgetMaps, - }); -}; - -// Widget updater: opportunitiesByOwner (BAR_CHART) -const updateOpportunitiesByOwnerWidget = ({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps, -}: UpdateStandardPageLayoutWidgetsArgs): FlatEntityMaps => { - const opportunityIdField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.id.universalIdentifier, - }); - - const opportunityOwnerField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.owner.universalIdentifier, - }); - - const opportunityObject = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: STANDARD_OBJECTS.opportunity.universalIdentifier, - }); - - const widget = findWidgetByUniversalIdentifierOrThrow({ - flatPageLayoutWidgetMaps, - universalIdentifier: - STANDARD_PAGE_LAYOUTS.myFirstDashboard.tabs.tab1.widgets - .opportunitiesByOwner.universalIdentifier, - widgetDescription: 'opportunitiesByOwner', - expectedConfigurationType: WidgetConfigurationType.BAR_CHART, - }); - - const updatedWidget: FlatPageLayoutWidget = - { - ...widget, - objectMetadataId: opportunityObject.id, - configuration: { - ...widget.configuration, - aggregateFieldMetadataId: opportunityIdField.id, - primaryAxisGroupByFieldMetadataId: opportunityOwnerField.id, - secondaryAxisGroupByFieldMetadataId: opportunityOwnerField.id, - }, - }; - - return replaceFlatEntityInFlatEntityMapsOrThrow({ - flatEntity: updatedWidget, - flatEntityMaps: flatPageLayoutWidgetMaps, - }); -}; - -// Widget updater: dealsCreatedThisMonth (AGGREGATE_CHART) -const updateDealsCreatedThisMonthWidget = ({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps, -}: UpdateStandardPageLayoutWidgetsArgs): FlatEntityMaps => { - const opportunityIdField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.id.universalIdentifier, - }); - - const opportunityCreatedAtField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.createdAt.universalIdentifier, - }); - - const opportunityObject = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: STANDARD_OBJECTS.opportunity.universalIdentifier, - }); - - const widget = findWidgetByUniversalIdentifierOrThrow({ - flatPageLayoutWidgetMaps, - universalIdentifier: - STANDARD_PAGE_LAYOUTS.myFirstDashboard.tabs.tab1.widgets - .dealsCreatedThisMonth.universalIdentifier, - widgetDescription: 'dealsCreatedThisMonth', - expectedConfigurationType: WidgetConfigurationType.AGGREGATE_CHART, - }); - - const updatedRecordFilters = widget.configuration.filter?.recordFilters?.map( - (filter: RecordFilter) => ({ - ...filter, - fieldMetadataId: opportunityCreatedAtField.id, - }), - ); - - const updatedWidget: FlatPageLayoutWidget = - { - ...widget, - objectMetadataId: opportunityObject.id, - configuration: { - ...widget.configuration, - aggregateFieldMetadataId: opportunityIdField.id, - filter: isDefined(widget.configuration.filter) - ? { - ...widget.configuration.filter, - recordFilters: updatedRecordFilters, - } - : undefined, - }, - }; - - return replaceFlatEntityInFlatEntityMapsOrThrow({ - flatEntity: updatedWidget, - flatEntityMaps: flatPageLayoutWidgetMaps, - }); -}; - -// Widget updater: dealValueCreatedThisMonth (AGGREGATE_CHART) -const updateDealValueCreatedThisMonthWidget = ({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps, -}: UpdateStandardPageLayoutWidgetsArgs): FlatEntityMaps => { - const opportunityAmountField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.amount.universalIdentifier, - }); - - const opportunityCreatedAtField = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - universalIdentifier: - STANDARD_OBJECTS.opportunity.fields.createdAt.universalIdentifier, - }); - - const opportunityObject = findFlatEntityByUniversalIdentifierOrThrow({ - flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: STANDARD_OBJECTS.opportunity.universalIdentifier, - }); - - const widget = findWidgetByUniversalIdentifierOrThrow({ - flatPageLayoutWidgetMaps, - universalIdentifier: - STANDARD_PAGE_LAYOUTS.myFirstDashboard.tabs.tab1.widgets - .dealValueCreatedThisMonth.universalIdentifier, - widgetDescription: 'dealValueCreatedThisMonth', - expectedConfigurationType: WidgetConfigurationType.AGGREGATE_CHART, - }); - - const updatedRecordFilters = widget.configuration.filter?.recordFilters?.map( - (filter: RecordFilter) => ({ - ...filter, - fieldMetadataId: opportunityCreatedAtField.id, - }), - ); - - const updatedWidget: FlatPageLayoutWidget = - { - ...widget, - objectMetadataId: opportunityObject.id, - configuration: { - ...widget.configuration, - aggregateFieldMetadataId: opportunityAmountField.id, - filter: isDefined(widget.configuration.filter) - ? { - ...widget.configuration.filter, - recordFilters: updatedRecordFilters, - } - : undefined, - }, - }; - - return replaceFlatEntityInFlatEntityMapsOrThrow({ - flatEntity: updatedWidget, - flatEntityMaps: flatPageLayoutWidgetMaps, - }); -}; - -export const updateStandardPageLayoutWidgetsWithWorkspaceFieldIds = ({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps, -}: UpdateStandardPageLayoutWidgetsArgs): FlatEntityMaps => { - let updatedMaps = flatPageLayoutWidgetMaps; - - updatedMaps = updateDealsByCompanyWidget({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps: updatedMaps, - }); - - updatedMaps = updatePipelineValueByStageWidget({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps: updatedMaps, - }); - - updatedMaps = updateRevenueTimelineWidget({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps: updatedMaps, - }); - - updatedMaps = updateOpportunitiesByOwnerWidget({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps: updatedMaps, - }); - - updatedMaps = updateDealsCreatedThisMonthWidget({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps: updatedMaps, - }); - - updatedMaps = updateDealValueCreatedThisMonthWidget({ - flatFieldMetadataMaps, - flatObjectMetadataMaps, - flatPageLayoutWidgetMaps: updatedMaps, - }); - - return updatedMaps; -}; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-delete-all-files.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-delete-all-files.command.ts similarity index 100% rename from packages/twenty-server/src/database/commands/upgrade-version-command/1-16/1-16-delete-all-files.command.ts rename to packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-delete-all-files.command.ts diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-upgrade-version-command.module.ts index dcdbf3ca49..b932a72d56 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-17/1-17-upgrade-version-command.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { DeleteFileRecordsCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-delete-all-files.command'; +import { DeleteFileRecordsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-delete-all-files.command'; import { IdentifyWebhookMetadataCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-identify-webhook-metadata.command'; import { MakeWebhookUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-make-webhook-universal-identifier-and-application-id-not-nullable-migration.command'; import { MigrateAttachmentToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-attachment-to-morph-relations.command'; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade-version-command.module.ts index 6cfcea8aac..5e96efb297 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade-version-command.module.ts @@ -1,10 +1,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { V1_13_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-13/1-13-upgrade-version-command.module'; -import { V1_14_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-14/1-14-upgrade-version-command.module'; -import { V1_15_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-15/1-15-upgrade-version-command.module'; -import { V1_16_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-16/1-16-upgrade-version-command.module'; import { V1_17_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-17/1-17-upgrade-version-command.module'; import { UpgradeCommand } from 'src/database/commands/upgrade-version-command/upgrade.command'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -13,10 +9,6 @@ import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-s @Module({ imports: [ TypeOrmModule.forFeature([WorkspaceEntity]), - V1_13_UpgradeVersionCommandModule, - V1_14_UpgradeVersionCommandModule, - V1_15_UpgradeVersionCommandModule, - V1_16_UpgradeVersionCommandModule, V1_17_UpgradeVersionCommandModule, DataSourceModule, ], 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 7e09940a96..401cf56645 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 @@ -9,44 +9,7 @@ import { UpgradeCommandRunner, type VersionCommands, } from 'src/database/commands/command-runners/upgrade.command-runner'; -import { BackfillPageLayoutUniversalIdentifiersCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-backfill-page-layout-universal-identifiers.command'; -import { CleanEmptyStringNullInTextFieldsCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-clean-empty-string-null-in-text-fields.command'; -import { DeduplicateRoleTargetsCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-deduplicate-role-targets.command'; -import { MigrateStandardInvalidEntitiesCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command'; -import { MigrateTimelineActivityToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-migrate-timeline-activity-to-morph-relations.command'; -import { RenameIndexNameCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-rename-index.command'; -import { UpdateRoleTargetsUniqueConstraintMigrationCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-update-role-targets-unique-constraint-migration.command'; -import { DeleteRemovedAgentsCommand } from 'src/database/commands/upgrade-version-command/1-14/1-14-delete-removed-agents.command'; -import { UpdateCreatedByEnumCommand } from 'src/database/commands/upgrade-version-command/1-14/1-14-update-created-by-enum.command'; -import { AddWorkspaceForeignKeysMigrationCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-add-workspace-foreign-keys-migration.command'; -import { BackfillUpdatedByFieldCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-backfill-updated-by-field.command'; -import { FixNanPositionValuesInNotesCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-fix-nan-position-values-in-notes.command'; -import { MigratePageLayoutWidgetConfigurationCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.command'; -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 { DeleteFileRecordsCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-delete-all-files.command'; -import { FlushV2CacheAndIncrementMetadataVersionCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-flush-v2-cache-and-increment-metadata-version.command'; -import { IdentifyAgentMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-agent-metadata.command'; -import { IdentifyFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-field-metadata.command'; -import { IdentifyIndexMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-index-metadata.command'; -import { IdentifyObjectMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-object-metadata.command'; -import { IdentifyRemainingEntitiesMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-remaining-entities-metadata.command'; -import { IdentifyRoleMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-role-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 { IdentifyViewGroupMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-group-metadata.command'; -import { IdentifyViewMetadataCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-identify-view-metadata.command'; -import { MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-agent-universal-identifier-and-application-id-not-nullable-migration.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 { MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-index-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 { MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-remaining-entities-universal-identifier-and-application-id-not-nullable-migration.command'; -import { MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-role-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 { MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-16/1-16-make-view-group-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 { DeleteFileRecordsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-delete-all-files.command'; import { IdentifyWebhookMetadataCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-identify-webhook-metadata.command'; import { MakeWebhookUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-make-webhook-universal-identifier-and-application-id-not-nullable-migration.command'; import { MigrateAttachmentToMorphRelationsCommand } from 'src/database/commands/upgrade-version-command/1-17/1-17-migrate-attachment-to-morph-relations.command'; @@ -69,53 +32,8 @@ export class UpgradeCommand extends UpgradeCommandRunner { protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, protected readonly dataSourceService: DataSourceService, - // 1.13 Commands - protected readonly migrateTimelineActivityToMorphRelationsCommand: MigrateTimelineActivityToMorphRelationsCommand, - protected readonly deduplicateRoleTargetsCommand: DeduplicateRoleTargetsCommand, - protected readonly updateRoleTargetsUniqueConstraintMigrationCommand: UpdateRoleTargetsUniqueConstraintMigrationCommand, - protected readonly backfillPageLayoutUniversalIdentifiersCommand: BackfillPageLayoutUniversalIdentifiersCommand, - protected readonly migrateStandardInvalidEntitiesCommand: MigrateStandardInvalidEntitiesCommand, - protected readonly cleanEmptyStringNullInTextFieldsCommand: CleanEmptyStringNullInTextFieldsCommand, - protected readonly renameIndexNameCommand: RenameIndexNameCommand, - - // 1.14 Commands - protected readonly updateCreatedByEnumCommand: UpdateCreatedByEnumCommand, - protected readonly deleteRemovedAgentsCommand: DeleteRemovedAgentsCommand, - - // 1.15 Commands - protected readonly migratePageLayoutWidgetConfigurationCommand: MigratePageLayoutWidgetConfigurationCommand, - protected readonly fixNanPositionValuesInNotesCommand: FixNanPositionValuesInNotesCommand, - protected readonly backfillUpdatedByFieldCommand: BackfillUpdatedByFieldCommand, - protected readonly addWorkspaceForeignKeysMigrationCommand: AddWorkspaceForeignKeysMigrationCommand, - - // 1.16 Commands - protected readonly updateTaskOnDeleteActionCommand: UpdateTaskOnDeleteActionCommand, - protected readonly backfillOpportunityOwnerFieldCommand: BackfillOpportunityOwnerFieldCommand, - protected readonly backfillStandardPageLayoutsCommand: BackfillStandardPageLayoutsCommand, - protected readonly identifyAgentMetadataCommand: IdentifyAgentMetadataCommand, - protected readonly identifyFieldMetadataCommand: IdentifyFieldMetadataCommand, - protected readonly identifyIndexMetadataCommand: IdentifyIndexMetadataCommand, - protected readonly identifyObjectMetadataCommand: IdentifyObjectMetadataCommand, - protected readonly identifyRoleMetadataCommand: IdentifyRoleMetadataCommand, - protected readonly identifyViewMetadataCommand: IdentifyViewMetadataCommand, - protected readonly identifyViewFieldMetadataCommand: IdentifyViewFieldMetadataCommand, - protected readonly identifyViewFilterMetadataCommand: IdentifyViewFilterMetadataCommand, - protected readonly makeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - protected readonly identifyViewGroupMetadataCommand: IdentifyViewGroupMetadataCommand, - protected readonly makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - protected readonly makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - protected readonly makeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - protected readonly makeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - protected readonly makeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - protected readonly makeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - protected readonly makeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - protected readonly makeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - protected readonly identifyRemainingEntitiesMetadataCommand: IdentifyRemainingEntitiesMetadataCommand, - protected readonly makeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - protected readonly flushV2CacheAndIncrementMetadataVersionCommand: FlushV2CacheAndIncrementMetadataVersionCommand, - protected readonly deleteFileRecordsCommand: DeleteFileRecordsCommand, - // 1.17 Commands + protected readonly deleteFileRecordsCommand: DeleteFileRecordsCommand, protected readonly migrateAttachmentToMorphRelationsCommand: MigrateAttachmentToMorphRelationsCommand, protected readonly identifyWebhookMetadataCommand: IdentifyWebhookMetadataCommand, protected readonly makeWebhookUniversalIdentifierAndApplicationIdNotNullableMigrationCommand: MakeWebhookUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, @@ -128,67 +46,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { ); // Note: Required empty commands array to allow retrieving previous version - const commands_1120: VersionCommands = []; - - const commands_1130: VersionCommands = [ - this.migrateTimelineActivityToMorphRelationsCommand, - this.deduplicateRoleTargetsCommand, - this.updateRoleTargetsUniqueConstraintMigrationCommand, - this.backfillPageLayoutUniversalIdentifiersCommand, - this.migrateStandardInvalidEntitiesCommand, - this.cleanEmptyStringNullInTextFieldsCommand, - this.renameIndexNameCommand, - ]; - - const commands_1140: VersionCommands = [ - this.updateCreatedByEnumCommand, - this.deleteRemovedAgentsCommand, - ]; - - const commands_1150: VersionCommands = [ - this.migratePageLayoutWidgetConfigurationCommand, - this.fixNanPositionValuesInNotesCommand, - this.backfillUpdatedByFieldCommand, - this.addWorkspaceForeignKeysMigrationCommand, - ]; - - const commands_1160: VersionCommands = [ - this.identifyAgentMetadataCommand, - this.identifyFieldMetadataCommand, - this.identifyObjectMetadataCommand, - this.identifyRoleMetadataCommand, - this.identifyViewMetadataCommand, - this.identifyViewFieldMetadataCommand, - this.identifyViewFilterMetadataCommand, - this.identifyViewGroupMetadataCommand, - this.identifyIndexMetadataCommand, - this.backfillOpportunityOwnerFieldCommand, - this.backfillStandardPageLayoutsCommand, - this - .makeAgentUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - this - .makeFieldMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - this - .makeObjectMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - this - .makeRoleUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - this - .makeViewUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - this - .makeViewFieldUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - this - .makeViewFilterUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - this - .makeViewGroupUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - this - .makeIndexMetadataUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - this.identifyRemainingEntitiesMetadataCommand, - this - .makeRemainingEntitiesUniversalIdentifierAndApplicationIdNotNullableMigrationCommand, - this.flushV2CacheAndIncrementMetadataVersionCommand, - this.updateTaskOnDeleteActionCommand, - this.deleteFileRecordsCommand, - ]; + const commands_1160: VersionCommands = []; const commands_1170: VersionCommands = [ this.migrateAttachmentToMorphRelationsCommand, @@ -199,10 +57,6 @@ export class UpgradeCommand extends UpgradeCommandRunner { ]; this.allCommands = { - '1.12.0': commands_1120, - '1.13.0': commands_1130, - '1.14.0': commands_1140, - '1.15.0': commands_1150, '1.16.0': commands_1160, '1.17.0': commands_1170, }; diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1770040351718-remove-object-metadata-standard-id.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1770040351718-remove-object-metadata-standard-id.ts new file mode 100644 index 0000000000..030c5ab3ff --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1770040351718-remove-object-metadata-standard-id.ts @@ -0,0 +1,19 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm'; + +export class RemoveObjectMetadataStandardId1770040351718 + implements MigrationInterface +{ + name = 'RemoveObjectMetadataStandardId1770040351718'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."objectMetadata" DROP COLUMN "standardId"`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."objectMetadata" ADD "standardId" uuid`, + ); + } +} 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 c297e8c5be..59242f8bab 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 @@ -135,7 +135,6 @@ export const mockPersonFlatObjectMetadata = ( ): FlatObjectMetadata => ({ id: objectMetadataId, icon: 'Icon123', - standardId: '', nameSingular: 'person', namePlural: 'people', labelSingular: 'Person', diff --git a/packages/twenty-server/src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps.ts b/packages/twenty-server/src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps.ts index 431377bbbb..57cb49197f 100644 --- a/packages/twenty-server/src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps.ts +++ b/packages/twenty-server/src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps.ts @@ -18,7 +18,6 @@ const customObjectImageFieldId = 'imageIdentifierFieldMetadataId'; const personFlatObject = getFlatObjectMetadataMock({ id: '20202020-8dec-43d5-b2ff-6eef05095bec', - standardId: '20202020-8dec-43d5-b2ff-6eef05095bec', nameSingular: 'person', namePlural: 'people', labelSingular: 'Person', @@ -43,7 +42,6 @@ const personFlatObject = getFlatObjectMetadataMock({ const companyFlatObject = getFlatObjectMetadataMock({ id: '20202020-c03c-45d6-a4b0-04afe1357c5c', - standardId: '20202020-c03c-45d6-a4b0-04afe1357c5c', nameSingular: 'company', namePlural: 'companies', labelSingular: 'Company', @@ -68,7 +66,6 @@ const companyFlatObject = getFlatObjectMetadataMock({ const customObjectFlatObject = getFlatObjectMetadataMock({ id: '20202020-3d75-4aab-bacd-ee176c5f63ca', - standardId: null, nameSingular: 'regular-custom-object', namePlural: 'regular-custom-objects', labelSingular: 'Regular Custom Object', @@ -93,7 +90,6 @@ const customObjectFlatObject = getFlatObjectMetadataMock({ const nonSearchableFlatObject = getFlatObjectMetadataMock({ id: '20202020-540c-4397-b872-2a90ea2fb809', - standardId: '20202020-540c-4397-b872-2a90ea2fb809', nameSingular: 'non-searchable-object', namePlural: 'non-searchable-objects', labelSingular: '', 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 058fb068b5..87bc33d43a 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 @@ -30,7 +30,6 @@ const mockObjectMetadata: FlatObjectMetadata = { description: null, standardOverrides: null, isUIReadOnly: false, - standardId: null, labelIdentifierFieldMetadataId: null, imageIdentifierFieldMetadataId: null, duplicateCriteria: null, diff --git a/packages/twenty-server/src/engine/core-modules/event-emitter/utils/object-record-changed-values.ts b/packages/twenty-server/src/engine/core-modules/event-emitter/utils/object-record-changed-values.ts index e3d269f2ad..04b47559b1 100644 --- a/packages/twenty-server/src/engine/core-modules/event-emitter/utils/object-record-changed-values.ts +++ b/packages/twenty-server/src/engine/core-modules/event-emitter/utils/object-record-changed-values.ts @@ -1,5 +1,4 @@ import deepEqual from 'deep-equal'; -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; import { FieldMetadataType, type ObjectRecord } from 'twenty-shared/types'; import { fastDeepEqual } from 'twenty-shared/utils'; @@ -7,24 +6,26 @@ import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/typ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; const LARGE_JSON_FIELDS: Record> = { - [STANDARD_OBJECT_IDS.workflowVersion]: new Set(['steps', 'trigger']), - [STANDARD_OBJECT_IDS.workflowAutomatedTrigger]: new Set(['settings']), - [STANDARD_OBJECT_IDS.workflowRun]: new Set(['state']), + [STANDARD_OBJECTS.workflowVersion.universalIdentifier]: new Set([ + 'steps', + 'trigger', + ]), + [STANDARD_OBJECTS.workflowAutomatedTrigger.universalIdentifier]: new Set([ + 'settings', + ]), + [STANDARD_OBJECTS.workflowRun.universalIdentifier]: new Set(['state']), }; const isLargeJsonField = ( - objectMetadataItem: Pick, + objectMetadataItem: Pick, key: string, ): boolean => { - const standardId = objectMetadataItem.standardId; + const universalIdentifier = objectMetadataItem.universalIdentifier; - if (!standardId) { - return false; - } - - return LARGE_JSON_FIELDS[standardId]?.has(key) ?? false; + return LARGE_JSON_FIELDS[universalIdentifier]?.has(key) ?? false; }; export const objectRecordChangedValues = ( diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/__tests__/is-workflow-related-object.util.spec.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/__tests__/is-workflow-related-object.util.spec.ts index 8c0cc48265..81a4255af6 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/__tests__/is-workflow-related-object.util.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/__tests__/is-workflow-related-object.util.spec.ts @@ -1,40 +1,39 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { isWorkflowRelatedObject } from 'src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-related-object.util'; -import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; describe('isWorkflowRelatedObject', () => { it('should return true for workflow-related objects', () => { expect( isWorkflowRelatedObject({ - standardId: STANDARD_OBJECT_IDS.workflow, - } as ObjectMetadataEntity), + universalIdentifier: STANDARD_OBJECTS.workflow.universalIdentifier, + }), ).toBe(true); expect( isWorkflowRelatedObject({ - standardId: STANDARD_OBJECT_IDS.workflowRun, - } as ObjectMetadataEntity), + universalIdentifier: STANDARD_OBJECTS.workflowRun.universalIdentifier, + }), ).toBe(true); expect( isWorkflowRelatedObject({ - standardId: STANDARD_OBJECT_IDS.workflowVersion, - } as ObjectMetadataEntity), + universalIdentifier: + STANDARD_OBJECTS.workflowVersion.universalIdentifier, + }), ).toBe(true); }); it('should return false for non-workflow objects', () => { expect( isWorkflowRelatedObject({ - standardId: STANDARD_OBJECT_IDS.person, - } as ObjectMetadataEntity), + universalIdentifier: STANDARD_OBJECTS.person.universalIdentifier, + }), ).toBe(false); expect( isWorkflowRelatedObject({ - standardId: null, - } as ObjectMetadataEntity), + universalIdentifier: 'some-custom-object-uuid', + }), ).toBe(false); }); }); diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-related-object.util.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-related-object.util.ts index 703f06e7f6..05101280b0 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-related-object.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-related-object.util.ts @@ -1,20 +1,17 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; // All workflow-related standard object IDs that should be filtered out from agent access -const WORKFLOW_STANDARD_OBJECT_IDS = [ - STANDARD_OBJECT_IDS.workflow, - STANDARD_OBJECT_IDS.workflowRun, - STANDARD_OBJECT_IDS.workflowVersion, - STANDARD_OBJECT_IDS.workflowAutomatedTrigger, +const WORKFLOW_STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS = [ + STANDARD_OBJECTS.workflow.universalIdentifier, + STANDARD_OBJECTS.workflowRun.universalIdentifier, + STANDARD_OBJECTS.workflowVersion.universalIdentifier, + STANDARD_OBJECTS.workflowAutomatedTrigger.universalIdentifier, ] as const; export const isWorkflowRelatedObject = (objectMetadata: { - standardId: string | null; + universalIdentifier: string; }): boolean => { - return ( - objectMetadata.standardId !== null && - WORKFLOW_STANDARD_OBJECT_IDS.includes( - objectMetadata.standardId as (typeof WORKFLOW_STANDARD_OBJECT_IDS)[number], - ) + return WORKFLOW_STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.includes( + objectMetadata.universalIdentifier as (typeof WORKFLOW_STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS)[number], ); }; diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-run-object.util.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-run-object.util.ts index 9cdc361f41..295193bd5d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-run-object.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/is-workflow-run-object.util.ts @@ -1,22 +1,18 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; // All workflow-related standard object IDs that should be filtered out from agent access -const WORKFLOW_STANDARD_OBJECT_IDS = [ - STANDARD_OBJECT_IDS.workflow, - STANDARD_OBJECT_IDS.workflowRun, - STANDARD_OBJECT_IDS.workflowVersion, - STANDARD_OBJECT_IDS.workflowAutomatedTrigger, +const WORKFLOW_STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS = [ + STANDARD_OBJECTS.workflow.universalIdentifier, + STANDARD_OBJECTS.workflowRun.universalIdentifier, + STANDARD_OBJECTS.workflowVersion.universalIdentifier, + STANDARD_OBJECTS.workflowAutomatedTrigger.universalIdentifier, ] as const; export const isWorkflowRelatedObject = ( objectMetadata: ObjectMetadataEntity, ): boolean => { - return ( - objectMetadata.standardId !== null && - WORKFLOW_STANDARD_OBJECT_IDS.includes( - objectMetadata.standardId as (typeof WORKFLOW_STANDARD_OBJECT_IDS)[number], - ) + return WORKFLOW_STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.includes( + objectMetadata.universalIdentifier as (typeof WORKFLOW_STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS)[number], ); }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/attachment-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/attachment-flat-object.mock.ts index 51015a6ca8..c4b4cffd2e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/attachment-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/attachment-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const ATTACHMENT_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: '819ed5ff-312f-4423-8e95-02a691cf5c27', - standardId: '20202020-bd3d-4c60-8dca-571c71d4447a', nameSingular: 'attachment', namePlural: 'attachments', labelSingular: 'Attachment', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock.ts index e89b358705..3dccfc9cab 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const COMPANY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: '7f5c2c7a-bb23-46fb-b59d-9b7a52a8d1cc', - standardId: '20202020-b374-4779-a561-80086cb2e17f', nameSingular: 'company', namePlural: 'companies', labelSingular: 'Company', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-flat-object.mock.ts index ff137ec5f8..a67b0f2c44 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const FAVORITE_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2', - standardId: '20202020-ab56-4e05-92a3-e2414a499860', nameSingular: 'favorite', namePlural: 'favorites', labelSingular: 'Favorite', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-folder-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-folder-flat-object.mock.ts index 242aa9200b..bd4349b85a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-folder-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-folder-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const FAVORITE_FOLDER_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: '35763b74-1abe-4c4b-9eab-27693f0ee06d', - standardId: '20202020-7cf8-401f-8211-a9587d27fd2d', nameSingular: 'favoriteFolder', namePlural: 'favoriteFolders', labelSingular: 'Favorite Folder', 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 f9ac28fdf4..a0583c9c3b 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 @@ -38,7 +38,6 @@ export const getFlatObjectMetadataMock = ( namePlural: 'defaultflatObjectMetadataNamePlural', nameSingular: 'defaultflatObjectMetadataNameSingular', shortcut: 'shortcut', - standardId: null, applicationId, standardOverrides: null, targetTableName: '', @@ -62,7 +61,6 @@ export const getStandardFlatObjectMetadataMock = ( overrides: Omit, ) => { return getFlatObjectMetadataMock({ - standardId: faker.string.uuid(), standardOverrides: {}, isCustom: false, isSystem: true, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/note-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/note-flat-object.mock.ts index bd7a90b88f..785e366f24 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/note-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/note-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const NOTE_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: '1253e3e5-5b00-4a34-93b8-02f3dc6e2b7c', - standardId: '20202020-0b00-45cd-b6f6-6cd806fc6804', nameSingular: 'note', namePlural: 'notes', labelSingular: 'Note', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/note-target-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/note-target-flat-object.mock.ts index f1a3c35ae1..b4ddbbcf66 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/note-target-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/note-target-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const NOTE_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: '12e3cb51-c3de-4192-b0d5-965d48d001c0', - standardId: '20202020-fff0-4b44-be82-bda313884400', nameSingular: 'noteTarget', namePlural: 'noteTargets', labelSingular: 'Note Target', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/opportunity-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/opportunity-flat-object.mock.ts index b21fe20537..128313d820 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/opportunity-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/opportunity-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const OPPORTUNITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: 'e6996bbf-dd41-423a-9324-8546f5b22fa7', - standardId: '20202020-9549-49dd-b2b2-883999db8938', nameSingular: 'opportunity', namePlural: 'opportunities', labelSingular: 'Opportunity', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/person-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/person-flat-object.mock.ts index 7cadd3740f..169cd19fb5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/person-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/person-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const PERSON_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: '843e0b67-9619-4628-91c4-2fa62256a611', - standardId: '20202020-e674-48e5-a542-72570eee7213', nameSingular: 'person', namePlural: 'people', labelSingular: 'Person', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock.ts index b68e394091..78f56b91de 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const PET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: 'd34e0f07-1b8c-4de0-938e-599cf05e1f7f', - standardId: null, nameSingular: 'pet', namePlural: 'pets', labelSingular: 'Pet', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock.ts index aab4b63433..f611ce6968 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const ROCKET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: 'd78ec657-74a4-4652-a350-1f44ff62970a', - standardId: null, nameSingular: 'rocket', namePlural: 'rockets', labelSingular: 'Rocket', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/task-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/task-flat-object.mock.ts index 29a44a8bb9..0b4f24cd81 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/task-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/task-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const TASK_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: '3186920d-2a15-4b5f-96c7-2bf6567024b0', - standardId: '20202020-1ba1-48ba-bc83-ef7e5990ed10', nameSingular: 'task', namePlural: 'tasks', labelSingular: 'Task', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/task-target-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/task-target-flat-object.mock.ts index 02c3c631e0..df21072636 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/task-target-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/task-target-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const TASK_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: 'bb2b29b8-7f46-4106-a8ae-3a32df9c9166', - standardId: '20202020-5a9a-44e8-95df-771cd06d0fb1', nameSingular: 'taskTarget', namePlural: 'taskTargets', labelSingular: 'Task Target', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/timeline-activity-flat-object.mock.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/timeline-activity-flat-object.mock.ts index 6e85a0c0cb..3cc376703b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/timeline-activity-flat-object.mock.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/__mocks__/timeline-activity-flat-object.mock.ts @@ -2,7 +2,6 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje export const TIMELINE_ACTIVITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({ id: '3090f830-c4b1-41a1-8e18-815760830bec', - standardId: '20202020-6736-4337-b5c4-8b39fae325a5', nameSingular: 'timelineActivity', namePlural: 'timelineActivities', labelSingular: 'Timeline Activity', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util.ts index ea37a51e10..14351276c3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util.ts @@ -98,7 +98,6 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre namePlural: createObjectInput.namePlural, nameSingular: createObjectInput.nameSingular, shortcut: createObjectInput.shortcut ?? null, - standardId: createObjectInput.standardId ?? null, standardOverrides: null, applicationId: flatApplication.id, universalIdentifier, 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 72e3ca6232..86a6c41bfd 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 @@ -39,9 +39,6 @@ export class ObjectMetadataEntity @PrimaryGeneratedColumn('uuid') id: string; - @Column({ nullable: true, type: 'uuid' }) - standardId: string | null; - @Column({ nullable: false, type: 'uuid' }) dataSourceId: string; diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts index 6fdd60039a..e894c9ff39 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts @@ -1,4 +1,3 @@ -import { type STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; import { FieldMetadataType } from 'twenty-shared/types'; import { capitalize, isDefined } from 'twenty-shared/utils'; @@ -23,6 +22,7 @@ import { TIMELINE_ACTIVITY_STANDARD_FIELD_IDS, } from 'src/engine/workspace-manager/workspace-migration/constant/standard-field-ids'; import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-migration/constant/standard-object-icons'; +import { type STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; const DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS = [ 'timelineActivity', @@ -30,7 +30,7 @@ const DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS = [ 'attachment', 'noteTarget', 'taskTarget', -] as const satisfies (keyof typeof STANDARD_OBJECT_IDS)[]; +] as const satisfies (keyof typeof STANDARD_OBJECTS)[]; const morphIdByRelationObjectNameSingular = { timelineActivity: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.targetMorphId, @@ -47,7 +47,7 @@ const morphIdByRelationObjectNameSingular = { // once we migrate timeline activity to morph relations, we can add it. // another way to check if an object is migrated to morph relations is to check if the feature flag is enabled const DEFAULT_MORPH_RELATIONS_OBJECTS_STANDARD_IDS = - [] as const satisfies (keyof typeof STANDARD_OBJECT_IDS)[]; + [] as const satisfies (keyof typeof STANDARD_OBJECTS)[]; export type BuildDefaultRelationFieldsForCustomObjectArgs = { existingFeatureFlagsMap: FeatureFlagMap; diff --git a/packages/twenty-server/src/engine/metadata-modules/role/services/workspace-roles-permissions-cache.service.ts b/packages/twenty-server/src/engine/metadata-modules/role/services/workspace-roles-permissions-cache.service.ts index b8e54e422f..c04bc51891 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role/services/workspace-roles-permissions-cache.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role/services/workspace-roles-permissions-cache.service.ts @@ -2,7 +2,6 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { PermissionFlagType } from 'twenty-shared/constants'; -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; import { type ObjectsPermissions, type ObjectsPermissionsByRoleId, @@ -16,11 +15,12 @@ import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/wo import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; -const WORKFLOW_STANDARD_OBJECT_IDS = [ - STANDARD_OBJECT_IDS.workflow, - STANDARD_OBJECT_IDS.workflowRun, - STANDARD_OBJECT_IDS.workflowVersion, +const WORKFLOW_STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS = [ + STANDARD_OBJECTS.workflow.universalIdentifier, + STANDARD_OBJECTS.workflowRun.universalIdentifier, + STANDARD_OBJECTS.workflowVersion.universalIdentifier, ] as const; @Injectable() @@ -60,7 +60,11 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide const objectRecordsPermissions: ObjectsPermissions = {}; for (const objectMetadata of workspaceObjectMetadataCollection) { - const { id: objectMetadataId, isSystem, standardId } = objectMetadata; + const { + id: objectMetadataId, + isSystem, + universalIdentifier, + } = objectMetadata; let canRead = role.canReadAllObjectRecords; let canUpdate = role.canUpdateAllObjectRecords; @@ -69,9 +73,8 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide const restrictedFields: RestrictedFieldsPermissions = {}; if ( - standardId && - WORKFLOW_STANDARD_OBJECT_IDS.includes( - standardId as (typeof WORKFLOW_STANDARD_OBJECT_IDS)[number], + WORKFLOW_STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.includes( + universalIdentifier as (typeof WORKFLOW_STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS)[number], ) ) { const hasWorkflowsPermissions = this.hasWorkflowsPermissions(role); @@ -169,7 +172,7 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide select: [ 'id', 'isSystem', - 'standardId', + 'universalIdentifier', 'labelIdentifierFieldMetadataId', ], }); 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 d337cf3805..dabfff5b33 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 @@ -115,7 +115,6 @@ describe('WorkspaceEntityManager', () => { imageIdentifierFieldMetadataId: null, labelIdentifierFieldMetadataId: null, shortcut: null, - standardId: null, standardOverrides: null, applicationId: 'test-application-id', isLabelSyncedWithName: false, @@ -242,8 +241,8 @@ describe('WorkspaceEntityManager', () => { IS_NAVIGATION_MENU_ITEM_ENABLED: false, IS_FILES_FIELD_ENABLED: false, IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED: false, - IS_MARKETPLACE_ENABLED: false, IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: false, + IS_MARKETPLACE_ENABLED: false, }, userWorkspaceRoleMap: {}, eventEmitterService: { 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 ae19329d14..ac800a0d10 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 @@ -35,7 +35,6 @@ describe('getColumnNameToFieldMetadataIdMap', () => { description: null, standardOverrides: null, isUIReadOnly: false, - standardId: null, labelIdentifierFieldMetadataId: null, imageIdentifierFieldMetadataId: null, duplicateCriteria: null, 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 461fa4c3f6..1cc78c8413 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 @@ -35,7 +35,6 @@ describe('getFieldMetadataIdToColumnNamesMap', () => { description: null, standardOverrides: null, isUIReadOnly: false, - standardId: null, labelIdentifierFieldMetadataId: null, imageIdentifierFieldMetadataId: null, duplicateCriteria: null, diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/is-record-matching-rls-row-level-permission-predicate.util.spec.ts b/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/is-record-matching-rls-row-level-permission-predicate.util.spec.ts index 0a7e7bd66d..354dc50b24 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/is-record-matching-rls-row-level-permission-predicate.util.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/__tests__/is-record-matching-rls-row-level-permission-predicate.util.spec.ts @@ -35,7 +35,6 @@ describe('isRecordMatchingRLSRowLevelPermissionPredicate', () => { description: null, standardOverrides: null, isUIReadOnly: false, - standardId: null, labelIdentifierFieldMetadataId: null, imageIdentifierFieldMetadataId: null, duplicateCriteria: null, diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/format-twenty-orm-event-to-database-batch-event.util.ts b/packages/twenty-server/src/engine/twenty-orm/utils/format-twenty-orm-event-to-database-batch-event.util.ts index d474ef3d7d..41caaf9c5c 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/format-twenty-orm-event-to-database-batch-event.util.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/format-twenty-orm-event-to-database-batch-event.util.ts @@ -7,7 +7,6 @@ import { ObjectRecordUpsertEvent, type ObjectRecordDiff, } from 'twenty-shared/database-events'; -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; import { assertUnreachable, isDefined, @@ -27,6 +26,7 @@ import { TwentyORMExceptionCode, } from 'src/engine/twenty-orm/exceptions/twenty-orm.exception'; import { type DatabaseBatchEventInput } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; export const formatTwentyOrmEventToDatabaseBatchEvent = < T extends ObjectLiteral, @@ -47,7 +47,10 @@ export const formatTwentyOrmEventToDatabaseBatchEvent = < recordsAfter?: T[]; recordsBefore?: T[]; }): DatabaseBatchEventInput | undefined => { - if (objectMetadataItem.standardId === STANDARD_OBJECT_IDS.timelineActivity) { + if ( + objectMetadataItem.universalIdentifier === + STANDARD_OBJECTS.timelineActivity.universalIdentifier + ) { return; } diff --git a/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts b/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts index 2331fb8ba7..7767743b75 100644 --- a/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts +++ b/packages/twenty-server/src/engine/utils/should-seed-workspace-favorite.ts @@ -1,6 +1,5 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; export const shouldSeedWorkspaceFavorite = ( objectMetadataId: string, @@ -8,9 +7,13 @@ export const shouldSeedWorkspaceFavorite = ( ): boolean => objectMetadataId !== objectMetadataItems.find( - (item) => item.standardId === STANDARD_OBJECT_IDS.workflowVersion, + (item) => + item.universalIdentifier === + STANDARD_OBJECTS.workflowVersion.universalIdentifier, )?.id && objectMetadataId !== objectMetadataItems.find( - (item) => item.standardId === STANDARD_OBJECT_IDS.workflowRun, + (item) => + item.universalIdentifier === + STANDARD_OBJECTS.workflowRun.universalIdentifier, )?.id; diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts index 1b6a37825a..6422072fa5 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts @@ -1,5 +1,4 @@ import { CalendarStartDay } from 'twenty-shared/constants'; -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant'; @@ -12,6 +11,7 @@ import { PAGE_LAYOUT_TAB_SEEDS } from 'src/engine/workspace-manager/dev-seeder/c import { PAGE_LAYOUT_WIDGET_SEEDS } from 'src/engine/workspace-manager/dev-seeder/core/constants/page-layout-widget-seeds.constant'; import { type SeederFlatPageLayoutWidget } from 'src/engine/workspace-manager/dev-seeder/core/types/seeder-flat-page-layout-widget.type'; import { generateSeedId } from 'src/engine/workspace-manager/dev-seeder/core/utils/generate-seed-id.util'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; const getFieldId = ( object: ObjectMetadataEntity | undefined, @@ -25,13 +25,17 @@ export const getPageLayoutWidgetDataSeedsV2 = ( objectMetadataItems: ObjectMetadataEntity[], ): SeederFlatPageLayoutWidget[] => { const opportunityObject = objectMetadataItems.find( - (obj) => obj.standardId === STANDARD_OBJECT_IDS.opportunity, + (obj) => + obj.universalIdentifier === + STANDARD_OBJECTS.opportunity.universalIdentifier, ); const companyObject = objectMetadataItems.find( - (obj) => obj.standardId === STANDARD_OBJECT_IDS.company, + (obj) => + obj.universalIdentifier === STANDARD_OBJECTS.company.universalIdentifier, ); const personObject = objectMetadataItems.find( - (obj) => obj.standardId === STANDARD_OBJECT_IDS.person, + (obj) => + obj.universalIdentifier === STANDARD_OBJECTS.person.universalIdentifier, ); const opportunityAmountFieldId = getFieldId(opportunityObject, 'amount'); diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds.util.ts index 3485e300fc..7bceab0eac 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds.util.ts @@ -1,5 +1,4 @@ import { CalendarStartDay } from 'twenty-shared/constants'; -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant'; @@ -15,6 +14,7 @@ import { PAGE_LAYOUT_WIDGET_SEEDS } from 'src/engine/workspace-manager/dev-seede import { type SeederFlatPageLayoutWidget } from 'src/engine/workspace-manager/dev-seeder/core/types/seeder-flat-page-layout-widget.type'; import { generateSeedId } from 'src/engine/workspace-manager/dev-seeder/core/utils/generate-seed-id.util'; import { getPageLayoutWidgetDataSeedsV2 } from 'src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; const getFieldId = ( object: ObjectMetadataEntity | undefined, @@ -29,16 +29,21 @@ export const getPageLayoutWidgetDataSeeds = ( isDashboardV2Enabled: boolean, ): SeederFlatPageLayoutWidget[] => { const opportunityObject = objectMetadataItems.find( - (obj) => obj.standardId === STANDARD_OBJECT_IDS.opportunity, + (obj) => + obj.universalIdentifier === + STANDARD_OBJECTS.opportunity.universalIdentifier, ); const companyObject = objectMetadataItems.find( - (obj) => obj.standardId === STANDARD_OBJECT_IDS.company, + (obj) => + obj.universalIdentifier === STANDARD_OBJECTS.company.universalIdentifier, ); const personObject = objectMetadataItems.find( - (obj) => obj.standardId === STANDARD_OBJECT_IDS.person, + (obj) => + obj.universalIdentifier === STANDARD_OBJECTS.person.universalIdentifier, ); const taskObject = objectMetadataItems.find( - (obj) => obj.standardId === STANDARD_OBJECT_IDS.task, + (obj) => + obj.universalIdentifier === STANDARD_OBJECTS.task.universalIdentifier, ); const rocketObject = objectMetadataItems.find( (obj) => obj.nameSingular === 'rocket', diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant.ts index 268f3e00da..59f2303a70 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant.ts @@ -1,5 +1,3 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { ATTACHMENT_STANDARD_FIELD_IDS, BLOCKLIST_STANDARD_FIELD_IDS, @@ -34,7 +32,7 @@ import { export const STANDARD_OBJECTS = { attachment: { - universalIdentifier: STANDARD_OBJECT_IDS.attachment, + universalIdentifier: '20202020-bd3d-4c60-8dca-571c71d4447a', fields: { id: { universalIdentifier: '20202020-a01a-4001-8a01-1d5f8e3c7b2a' }, createdAt: { @@ -104,7 +102,7 @@ export const STANDARD_OBJECTS = { }, }, blocklist: { - universalIdentifier: STANDARD_OBJECT_IDS.blocklist, + universalIdentifier: '20202020-0408-4f38-b8a8-4d5e3e26e24d', fields: { id: { universalIdentifier: '20202020-b01a-4011-8b11-5a9fc27fbf6e' }, createdAt: { @@ -128,7 +126,7 @@ export const STANDARD_OBJECTS = { }, }, calendarChannelEventAssociation: { - universalIdentifier: STANDARD_OBJECT_IDS.calendarChannelEventAssociation, + universalIdentifier: '20202020-491b-4aaa-9825-afd1bae6ae00', fields: { id: { universalIdentifier: '20202020-c01a-4021-8a21-9edf06bfef0a', @@ -169,7 +167,7 @@ export const STANDARD_OBJECTS = { }, }, calendarChannel: { - universalIdentifier: STANDARD_OBJECT_IDS.calendarChannel, + universalIdentifier: '20202020-e8f2-40e1-a39c-c0e0039c5034', fields: { id: { universalIdentifier: '20202020-c02a-4031-8a31-1a2f3b4c5d6e' }, createdAt: { @@ -234,7 +232,7 @@ export const STANDARD_OBJECTS = { }, }, calendarEventParticipant: { - universalIdentifier: STANDARD_OBJECT_IDS.calendarEventParticipant, + universalIdentifier: '20202020-a1c3-47a6-9732-27e5b1e8436d', fields: { id: { universalIdentifier: '20202020-c03a-4041-8a41-5e6f7a8b9cad', @@ -290,7 +288,7 @@ export const STANDARD_OBJECTS = { }, }, calendarEvent: { - universalIdentifier: STANDARD_OBJECT_IDS.calendarEvent, + universalIdentifier: '20202020-8f1d-4eef-9f85-0d1965e27221', fields: { id: { universalIdentifier: '20202020-c04a-4051-8a51-9cadbe0f1e2d' }, createdAt: { @@ -380,7 +378,7 @@ export const STANDARD_OBJECTS = { }, }, company: { - universalIdentifier: STANDARD_OBJECT_IDS.company, + universalIdentifier: '20202020-b374-4779-a561-80086cb2e17f', fields: { id: { universalIdentifier: '20202020-c05a-4061-8a61-1e2f3a4b5c6d' }, createdAt: { @@ -479,7 +477,7 @@ export const STANDARD_OBJECTS = { }, }, connectedAccount: { - universalIdentifier: STANDARD_OBJECT_IDS.connectedAccount, + universalIdentifier: '20202020-977e-46b2-890b-c3002ddfd5c5', fields: { id: { universalIdentifier: '20202020-c06a-4071-8a71-5c6d7e8f9aab' }, createdAt: { @@ -543,7 +541,7 @@ export const STANDARD_OBJECTS = { }, }, dashboard: { - universalIdentifier: STANDARD_OBJECT_IDS.dashboard, + universalIdentifier: '20202020-3840-4b6d-9425-0c5188b05ca8', fields: { id: { universalIdentifier: '20202020-da1a-41d1-8ad1-abcdefabcdef' }, createdAt: { @@ -605,7 +603,7 @@ export const STANDARD_OBJECTS = { }, }, favorite: { - universalIdentifier: STANDARD_OBJECT_IDS.favorite, + universalIdentifier: '20202020-ab56-4e05-92a3-e2414a499860', fields: { id: { universalIdentifier: '20202020-f01a-4091-8a91-ddeeffaabbcc' }, createdAt: { @@ -678,7 +676,7 @@ export const STANDARD_OBJECTS = { }, }, favoriteFolder: { - universalIdentifier: STANDARD_OBJECT_IDS.favoriteFolder, + universalIdentifier: '20202020-7cf8-401f-8211-a9587d27fd2d', fields: { id: { universalIdentifier: '20202020-f02a-40a1-8aa1-1f2e3d4c5b6a' }, createdAt: { @@ -701,7 +699,7 @@ export const STANDARD_OBJECTS = { indexes: {}, }, messageChannelMessageAssociation: { - universalIdentifier: STANDARD_OBJECT_IDS.messageChannelMessageAssociation, + universalIdentifier: '20202020-ad1e-4127-bccb-d83ae04d2ccb', fields: { id: { universalIdentifier: '20202020-b01a-40b1-8ab1-5a6b7c8d9eaf', @@ -752,7 +750,7 @@ export const STANDARD_OBJECTS = { }, }, messageChannel: { - universalIdentifier: STANDARD_OBJECT_IDS.messageChannel, + universalIdentifier: '20202020-fe8c-40bc-a681-b80b771449b7', fields: { id: { universalIdentifier: '20202020-b02a-40c1-8ac1-9eafbacbdced' }, createdAt: { @@ -837,7 +835,7 @@ export const STANDARD_OBJECTS = { }, }, messageFolder: { - universalIdentifier: STANDARD_OBJECT_IDS.messageFolder, + universalIdentifier: '20202020-4955-4fd9-8e59-2dbd373f2a46', fields: { id: { universalIdentifier: '20202020-b03a-40d1-8ad1-dcedfefaabbc' }, createdAt: { @@ -880,7 +878,7 @@ export const STANDARD_OBJECTS = { }, }, messageParticipant: { - universalIdentifier: STANDARD_OBJECT_IDS.messageParticipant, + universalIdentifier: '20202020-a433-4456-aa2d-fd9cb26b774a', fields: { id: { universalIdentifier: '20202020-b04a-40e1-8ae1-1a2b3c4d5e6f' }, createdAt: { @@ -925,7 +923,7 @@ export const STANDARD_OBJECTS = { }, }, messageThread: { - universalIdentifier: STANDARD_OBJECT_IDS.messageThread, + universalIdentifier: '20202020-849a-4c3e-84f5-a25a7d802271', fields: { id: { universalIdentifier: '20202020-b05a-40f1-8af1-5e6f7a8b9cad' }, createdAt: { @@ -961,7 +959,7 @@ export const STANDARD_OBJECTS = { }, }, message: { - universalIdentifier: STANDARD_OBJECT_IDS.message, + universalIdentifier: '20202020-3f6b-4425-80ab-e468899ab4b2', fields: { id: { universalIdentifier: '20202020-b06a-4101-8a01-9cadbedfaeb1' }, createdAt: { @@ -1030,7 +1028,7 @@ export const STANDARD_OBJECTS = { }, }, note: { - universalIdentifier: STANDARD_OBJECT_IDS.note, + universalIdentifier: '20202020-0b00-45cd-b6f6-6cd806fc6804', fields: { id: { universalIdentifier: '20202020-c01a-4111-8a11-dfabcddeef12' }, createdAt: { @@ -1086,7 +1084,7 @@ export const STANDARD_OBJECTS = { }, }, noteTarget: { - universalIdentifier: STANDARD_OBJECT_IDS.noteTarget, + universalIdentifier: '20202020-fff0-4b44-be82-bda313884400', fields: { id: { universalIdentifier: '20202020-c02a-4121-8a21-cddeef123456' }, createdAt: { @@ -1121,7 +1119,7 @@ export const STANDARD_OBJECTS = { }, }, opportunity: { - universalIdentifier: STANDARD_OBJECT_IDS.opportunity, + universalIdentifier: '20202020-9549-49dd-b2b2-883999db8938', fields: { id: { universalIdentifier: '20202020-d01a-4131-8a31-f123456789ab' }, createdAt: { @@ -1253,7 +1251,7 @@ export const STANDARD_OBJECTS = { }, }, person: { - universalIdentifier: STANDARD_OBJECT_IDS.person, + universalIdentifier: '20202020-e674-48e5-a542-72570eee7213', fields: { id: { universalIdentifier: '20202020-e01a-4141-8a41-456789abcdef' }, createdAt: { @@ -1357,7 +1355,7 @@ export const STANDARD_OBJECTS = { }, }, task: { - universalIdentifier: STANDARD_OBJECT_IDS.task, + universalIdentifier: '20202020-1ba1-48ba-bc83-ef7e5990ed10', fields: { id: { universalIdentifier: '20202020-a02a-4151-8a51-89abcdefabcd' }, createdAt: { @@ -1504,7 +1502,7 @@ export const STANDARD_OBJECTS = { }, }, taskTarget: { - universalIdentifier: STANDARD_OBJECT_IDS.taskTarget, + universalIdentifier: '20202020-5a9a-44e8-95df-771cd06d0fb1', fields: { id: { universalIdentifier: '20202020-a03a-4161-8a61-cdefabcdefab' }, createdAt: { @@ -1539,7 +1537,7 @@ export const STANDARD_OBJECTS = { }, }, timelineActivity: { - universalIdentifier: STANDARD_OBJECT_IDS.timelineActivity, + universalIdentifier: '20202020-6736-4337-b5c4-8b39fae325a5', fields: { id: { universalIdentifier: '20202020-a01a-4081-8a81-9aabbccddeff' }, createdAt: { @@ -1663,7 +1661,7 @@ export const STANDARD_OBJECTS = { }, }, workflow: { - universalIdentifier: STANDARD_OBJECT_IDS.workflow, + universalIdentifier: '20202020-62be-406c-b9ca-8caa50d51392', fields: { id: { universalIdentifier: '20202020-f02a-4181-8a81-efabcdefabcd' }, createdAt: { @@ -1731,7 +1729,7 @@ export const STANDARD_OBJECTS = { }, }, workflowAutomatedTrigger: { - universalIdentifier: STANDARD_OBJECT_IDS.workflowAutomatedTrigger, + universalIdentifier: '20202020-3319-4234-a34c-7f3b9d2e4d1f', fields: { id: { universalIdentifier: '20202020-f01a-4171-8a71-abcdefabcdef', @@ -1764,7 +1762,7 @@ export const STANDARD_OBJECTS = { }, }, workflowRun: { - universalIdentifier: STANDARD_OBJECT_IDS.workflowRun, + universalIdentifier: '20202020-4e28-4e95-a9d7-6c00874f843c', fields: { id: { universalIdentifier: '20202020-f03a-4191-8a91-cdefabcdefab' }, createdAt: { @@ -1851,7 +1849,7 @@ export const STANDARD_OBJECTS = { }, }, workflowVersion: { - universalIdentifier: STANDARD_OBJECT_IDS.workflowVersion, + universalIdentifier: '20202020-d65d-4ab9-9344-d77bfb376a3d', fields: { id: { universalIdentifier: '20202020-f04a-41a1-8aa1-abcdefabcdef' }, createdAt: { @@ -1921,7 +1919,7 @@ export const STANDARD_OBJECTS = { }, }, workspaceMember: { - universalIdentifier: STANDARD_OBJECT_IDS.workspaceMember, + universalIdentifier: '20202020-3319-4234-a34c-82d5c0e881a6', fields: { id: { universalIdentifier: '20202020-fb1a-41b1-8ab1-efabcdefabcd' }, createdAt: { diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/object-metadata/create-standard-object-flat-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/object-metadata/create-standard-object-flat-metadata.util.ts index e63eda1c44..94baff9640 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/object-metadata/create-standard-object-flat-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/object-metadata/create-standard-object-flat-metadata.util.ts @@ -62,7 +62,6 @@ export const createStandardObjectFlatMetadata = < return { universalIdentifier, - standardId: universalIdentifier, applicationId: twentyStandardApplicationId, workspaceId, nameSingular, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-company-page-layout.config.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-company-page-layout.config.ts index 1da1a81ff7..0ad41f88d7 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-company-page-layout.config.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-company-page-layout.config.ts @@ -1,6 +1,5 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { TAB_PROPS, WIDGET_PROPS, @@ -87,7 +86,7 @@ export const STANDARD_COMPANY_PAGE_LAYOUT_CONFIG = { layoutName: 'companyRecordPage', name: 'Default Company Layout', type: PageLayoutType.RECORD_PAGE, - objectUniversalIdentifier: STANDARD_OBJECT_IDS.company, + objectUniversalIdentifier: STANDARD_OBJECTS.company.universalIdentifier, universalIdentifier: '20202020-a101-4001-8001-c0aba11c0001', defaultTabUniversalIdentifier: COMPANY_PAGE_TABS.home.universalIdentifier, tabs: COMPANY_PAGE_TABS, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-note-page-layout.config.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-note-page-layout.config.ts index 298376ef29..797f7da549 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-note-page-layout.config.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-note-page-layout.config.ts @@ -1,6 +1,5 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { TAB_PROPS, WIDGET_PROPS, @@ -61,7 +60,7 @@ export const STANDARD_NOTE_PAGE_LAYOUT_CONFIG = { layoutName: 'noteRecordPage', name: 'Default Note Layout', type: PageLayoutType.RECORD_PAGE, - objectUniversalIdentifier: STANDARD_OBJECT_IDS.note, + objectUniversalIdentifier: STANDARD_OBJECTS.note.universalIdentifier, universalIdentifier: '20202020-a104-4004-8004-a0be5a11a004', defaultTabUniversalIdentifier: NOTE_PAGE_TABS.home.universalIdentifier, tabs: NOTE_PAGE_TABS, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-opportunity-page-layout.config.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-opportunity-page-layout.config.ts index bb729088be..4bcae29deb 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-opportunity-page-layout.config.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-opportunity-page-layout.config.ts @@ -1,6 +1,5 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { TAB_PROPS, WIDGET_PROPS, @@ -87,7 +86,7 @@ export const STANDARD_OPPORTUNITY_PAGE_LAYOUT_CONFIG = { layoutName: 'opportunityRecordPage', name: 'Default Opportunity Layout', type: PageLayoutType.RECORD_PAGE, - objectUniversalIdentifier: STANDARD_OBJECT_IDS.opportunity, + objectUniversalIdentifier: STANDARD_OBJECTS.opportunity.universalIdentifier, universalIdentifier: '20202020-a103-4003-8003-0aa0b1ca1003', defaultTabUniversalIdentifier: OPPORTUNITY_PAGE_TABS.home.universalIdentifier, tabs: OPPORTUNITY_PAGE_TABS, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-person-page-layout.config.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-person-page-layout.config.ts index e99dd7ccdd..b5d040e280 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-person-page-layout.config.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-person-page-layout.config.ts @@ -1,6 +1,5 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { TAB_PROPS, WIDGET_PROPS, @@ -87,7 +86,7 @@ export const STANDARD_PERSON_PAGE_LAYOUT_CONFIG = { layoutName: 'personRecordPage', name: 'Default Person Layout', type: PageLayoutType.RECORD_PAGE, - objectUniversalIdentifier: STANDARD_OBJECT_IDS.person, + objectUniversalIdentifier: STANDARD_OBJECTS.person.universalIdentifier, universalIdentifier: '20202020-a102-4002-8002-ae0a1ea11002', defaultTabUniversalIdentifier: PERSON_PAGE_TABS.home.universalIdentifier, tabs: PERSON_PAGE_TABS, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-task-page-layout.config.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-task-page-layout.config.ts index c8b16a27bb..b26a201b79 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-task-page-layout.config.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-task-page-layout.config.ts @@ -1,6 +1,5 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { TAB_PROPS, WIDGET_PROPS, @@ -61,7 +60,7 @@ export const STANDARD_TASK_PAGE_LAYOUT_CONFIG = { layoutName: 'taskRecordPage', name: 'Default Task Layout', type: PageLayoutType.RECORD_PAGE, - objectUniversalIdentifier: STANDARD_OBJECT_IDS.task, + objectUniversalIdentifier: STANDARD_OBJECTS.task.universalIdentifier, universalIdentifier: '20202020-a105-4005-8005-ba5ca11a1005', defaultTabUniversalIdentifier: TASK_PAGE_TABS.home.universalIdentifier, tabs: TASK_PAGE_TABS, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-page-layout.config.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-page-layout.config.ts index 86f76a4bc1..a46bc098fe 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-page-layout.config.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-page-layout.config.ts @@ -1,6 +1,5 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { TAB_PROPS, WIDGET_PROPS, @@ -27,7 +26,7 @@ export const STANDARD_WORKFLOW_PAGE_LAYOUT_CONFIG = { layoutName: 'workflowRecordPage', name: 'Default Workflow Layout', type: PageLayoutType.RECORD_PAGE, - objectUniversalIdentifier: STANDARD_OBJECT_IDS.workflow, + objectUniversalIdentifier: STANDARD_OBJECTS.workflow.universalIdentifier, universalIdentifier: '20202020-a106-4006-8006-a0bcf10aa006', defaultTabUniversalIdentifier: WORKFLOW_PAGE_TABS.flow.universalIdentifier, tabs: WORKFLOW_PAGE_TABS, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-run-page-layout.config.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-run-page-layout.config.ts index a64d043d1b..57a9c01d8b 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-run-page-layout.config.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-run-page-layout.config.ts @@ -1,6 +1,5 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { TAB_PROPS, WIDGET_PROPS, @@ -37,7 +36,7 @@ export const STANDARD_WORKFLOW_RUN_PAGE_LAYOUT_CONFIG = { layoutName: 'workflowRunRecordPage', name: 'Default Workflow Run Layout', type: PageLayoutType.RECORD_PAGE, - objectUniversalIdentifier: STANDARD_OBJECT_IDS.workflowRun, + objectUniversalIdentifier: STANDARD_OBJECTS.workflowRun.universalIdentifier, universalIdentifier: '20202020-a108-4008-8008-a0bcf10ac008', defaultTabUniversalIdentifier: WORKFLOW_RUN_PAGE_TABS.flow.universalIdentifier, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-version-page-layout.config.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-version-page-layout.config.ts index 064289f25c..6db4c1b642 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-version-page-layout.config.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout-config/standard-workflow-version-page-layout.config.ts @@ -1,6 +1,5 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; - import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { TAB_PROPS, WIDGET_PROPS, @@ -37,7 +36,8 @@ export const STANDARD_WORKFLOW_VERSION_PAGE_LAYOUT_CONFIG = { layoutName: 'workflowVersionRecordPage', name: 'Default Workflow Version Layout', type: PageLayoutType.RECORD_PAGE, - objectUniversalIdentifier: STANDARD_OBJECT_IDS.workflowVersion, + objectUniversalIdentifier: + STANDARD_OBJECTS.workflowVersion.universalIdentifier, universalIdentifier: '20202020-a107-4007-8007-a0bcf10ab007', defaultTabUniversalIdentifier: WORKFLOW_VERSION_PAGE_TABS.flow.universalIdentifier, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout/create-standard-page-layout-flat-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout/create-standard-page-layout-flat-metadata.util.ts index 534293a178..07270f9d3b 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout/create-standard-page-layout-flat-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/page-layout/create-standard-page-layout-flat-metadata.util.ts @@ -1,8 +1,8 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type'; import { type PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { STANDARD_PAGE_LAYOUTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout.constant'; import { type StandardObjectMetadataRelatedEntityIds } from 'src/engine/workspace-manager/twenty-standard-application/utils/get-standard-object-metadata-related-entity-ids.util'; import { type StandardPageLayoutMetadataRelatedEntityIds } from 'src/engine/workspace-manager/twenty-standard-application/utils/get-standard-page-layout-metadata-related-entity-ids.util'; @@ -27,8 +27,8 @@ export type CreateStandardPageLayoutArgs = { export const findObjectNameByUniversalIdentifier = ( objectUniversalIdentifier: string, ): string => { - for (const [objectName, objectId] of Object.entries(STANDARD_OBJECT_IDS)) { - if (objectId === objectUniversalIdentifier) { + for (const [objectName, objectConfig] of Object.entries(STANDARD_OBJECTS)) { + if (objectConfig.universalIdentifier === objectUniversalIdentifier) { return objectName; } } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/utils/from-universal-flat-object-metadata-to-flat-object-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/utils/from-universal-flat-object-metadata-to-flat-object-metadata.util.ts index 6cbbd3abaf..7bdc1289c5 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/utils/from-universal-flat-object-metadata-to-flat-object-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/utils/from-universal-flat-object-metadata-to-flat-object-metadata.util.ts @@ -104,7 +104,6 @@ export const fromUniversalFlatObjectMetadataToFlatObjectMetadata = ({ id: generatedId, workspaceId, applicationId, - standardId: null, universalIdentifier, applicationUniversalIdentifier, labelIdentifierFieldMetadataId, diff --git a/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts b/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts index cc4c0b96be..6a5db29729 100644 --- a/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts +++ b/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts @@ -1,4 +1,3 @@ -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; import { assertIsDefinedOrThrow } from 'twenty-shared/utils'; import { type WorkspacePreQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface'; @@ -13,6 +12,7 @@ import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadat import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { type MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; @WorkspaceQueryHook(`connectedAccount.destroyOne`) @@ -62,7 +62,7 @@ export class ConnectedAccountDeleteOnePreQueryHook const flatObjectMetadata = findFlatEntityByUniversalIdentifierOrThrow({ flatEntityMaps: flatObjectMetadataMaps, - universalIdentifier: STANDARD_OBJECT_IDS.messageChannel, + universalIdentifier: STANDARD_OBJECTS.messageChannel.universalIdentifier, }); // TODO: handle cascade events for delete diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts index 377fc85ba6..b448d58a72 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts @@ -2,7 +2,6 @@ import { Test, type TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import axios from 'axios'; -import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; import { ConnectedAccountProvider, FieldActorSource, @@ -10,6 +9,7 @@ import { import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; +import { STANDARD_OBJECTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant'; import { type CompanyToCreate, CreateCompanyService, @@ -128,7 +128,7 @@ describe('CreateCompanyService', () => { useValue: { findOne: jest.fn().mockResolvedValue({ id: 'mock-object-metadata-id', - standardId: STANDARD_OBJECT_IDS.company, + universalIdentifier: STANDARD_OBJECTS.company.universalIdentifier, workspaceId, nameSingular: 'company', namePlural: 'companies', diff --git a/packages/twenty-server/src/utils/__test__/get-object-metadata-entity.mock.ts b/packages/twenty-server/src/utils/__test__/get-object-metadata-entity.mock.ts index 5a48de52c8..0efc214030 100644 --- a/packages/twenty-server/src/utils/__test__/get-object-metadata-entity.mock.ts +++ b/packages/twenty-server/src/utils/__test__/get-object-metadata-entity.mock.ts @@ -43,7 +43,6 @@ export const getMockObjectMetadataEntity = ( labelSingular: 'Default mock plural singular', objectPermissions: [], shortcut: null, - standardId: null, universalIdentifier: faker.string.uuid(), applicationId: faker.string.uuid(), application: {} as ApplicationEntity, diff --git a/packages/twenty-shared/src/metadata/standard-object-ids.ts b/packages/twenty-shared/src/metadata/standard-object-ids.ts index 9b1ed07242..e7ec9b9f3e 100644 --- a/packages/twenty-shared/src/metadata/standard-object-ids.ts +++ b/packages/twenty-shared/src/metadata/standard-object-ids.ts @@ -5,6 +5,7 @@ * For readability keys can be edited but the values should not be changed. */ +// TODO remove once fieldMetadata standard id has been deprecated export const STANDARD_OBJECT_IDS = { // TODO: check if activity, activityTarget and comment can be deleted activityTarget: '20202020-2945-440e-8d1a-f84672d33d5e',