diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command.ts new file mode 100644 index 000000000..09c88c124 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command.ts @@ -0,0 +1,67 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { IsNull, Not, Repository } from 'typeorm'; + +import { + ActiveOrSuspendedWorkspacesMigrationCommandRunner, + RunOnWorkspaceArgs, +} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; + +@Command({ + name: 'upgrade:1-1:fix-update-standard-fields-is-label-synced-with-name', + description: + 'Fix isLabelSyncedWithName property for standard fields to match actual label-name synchronization state', +}) +export class FixUpdateStandardFieldsIsLabelSyncedWithName extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, + @InjectRepository(FieldMetadataEntity, 'core') + private readonly fieldMetadataRepository: Repository, + ) { + super(workspaceRepository, twentyORMGlobalManager); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + this.logger.log(`Updating standard fields for workspace ${workspaceId}`); + const workspaceStandardFields = await this.fieldMetadataRepository.find({ + where: { + workspaceId, + isCustom: false, + standardId: Not(IsNull()), + }, + }); + + let updatedFields = 0; + + for (const field of workspaceStandardFields) { + const isLabelSyncedWithName = + computeMetadataNameFromLabel(field.label) === field.name; + + if (field.isLabelSyncedWithName === isLabelSyncedWithName) { + continue; + } + + if (!options.dryRun) { + await this.fieldMetadataRepository.update(field.id, { + isLabelSyncedWithName, + }); + } + updatedFields++; + this.logger.log(`Updated isLabelSyncedMetadata for field ${field.id}`); + } + + this.logger.log( + `Updated ${updatedFields} field.s for workspace ${workspaceId}`, + ); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-1/1-1-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-1/1-1-upgrade-version-command.module.ts new file mode 100644 index 000000000..1afff9a08 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-1/1-1-upgrade-version-command.module.ts @@ -0,0 +1,35 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { FixUpdateStandardFieldsIsLabelSyncedWithName } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command'; +import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity'; +import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; +import { User } from 'src/engine/core-modules/user/user.entity'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +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 { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module'; +import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; +import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module'; + +@Module({ + imports: [ + TypeOrmModule.forFeature( + [ + Workspace, + AppToken, + User, + UserWorkspace, + FieldMetadataEntity, + ObjectMetadataEntity, + ], + 'core', + ), + WorkspaceDataSourceModule, + WorkspaceMigrationRunnerModule, + WorkspaceMetadataVersionModule, + ], + providers: [FixUpdateStandardFieldsIsLabelSyncedWithName], + exports: [FixUpdateStandardFieldsIsLabelSyncedWithName], +}) +export class V1_1_UpgradeVersionCommandModule {} 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 52f78687d..b33066fd4 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 @@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { V0_54_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-54/0-54-upgrade-version-command.module'; import { V0_55_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/0-55/0-55-upgrade-version-command.module'; +import { V1_1_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-1/1-1-upgrade-version-command.module'; import { DatabaseMigrationService, UpgradeCommand, @@ -15,6 +16,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp TypeOrmModule.forFeature([Workspace], 'core'), V0_54_UpgradeVersionCommandModule, V0_55_UpgradeVersionCommandModule, + V1_1_UpgradeVersionCommandModule, WorkspaceSyncMetadataModule, ], providers: [DatabaseMigrationService, UpgradeCommand], 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 1b8e18205..3ef1e4f1e 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 @@ -175,12 +175,18 @@ export class UpgradeCommand extends UpgradeCommandRunner { beforeSyncMetadata: [], }; + const commands_110: VersionCommands = { + afterSyncMetadata: [], + beforeSyncMetadata: [], + }; + this.allCommands = { '0.53.0': commands_053, '0.54.0': commands_054, '0.55.0': commands_055, '0.60.0': commands_060, '1.0.0': commands_100, + '1.1.0': commands_110, }; } diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts index a1815f77d..14a33beb6 100644 --- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts +++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts @@ -6,7 +6,7 @@ import { FieldMetadataOptions } from 'src/engine/metadata-modules/field-metadata import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface'; import { generateDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/generate-default-value'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util'; import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage'; import { TypedReflect } from 'src/utils/typed-reflect'; @@ -19,12 +19,8 @@ export interface WorkspaceFieldOptions< > { standardId: string; type: T; - label: - | MessageDescriptor - | ((objectMetadata: ObjectMetadataEntity) => MessageDescriptor); - description?: - | MessageDescriptor - | ((objectMetadata: ObjectMetadataEntity) => MessageDescriptor); + label: MessageDescriptor; + description?: MessageDescriptor; icon?: string; defaultValue?: FieldMetadataDefaultValue; options?: FieldMetadataOptions; @@ -76,30 +72,18 @@ export function WorkspaceField( const defaultValue = (options.defaultValue ?? generateDefaultValue(options.type)) as FieldMetadataDefaultValue | null; + const name = propertyKey.toString(); + const label = options.label.message ?? ''; + const isLabelSyncedWithName = computeMetadataNameFromLabel(label) === name; metadataArgsStorage.addFields({ target: object.constructor, standardId: options.standardId, - name: propertyKey.toString(), - label: - typeof options.label === 'function' - ? (objectMetadata: ObjectMetadataEntity) => - ( - options.label as ( - obj: ObjectMetadataEntity, - ) => MessageDescriptor - )(objectMetadata).message ?? '' - : (options.label.message ?? ''), + name, + label, type: options.type, - description: - typeof options.description === 'function' - ? (objectMetadata: ObjectMetadataEntity) => - ( - options.description as ( - obj: ObjectMetadataEntity, - ) => MessageDescriptor - )(objectMetadata).message ?? '' - : (options.description?.message ?? ''), + isLabelSyncedWithName, + description: options.description?.message ?? '', icon: options.icon, defaultValue, options: options.options, diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts index 1edf84c77..dd34ce27f 100644 --- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts +++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts @@ -5,14 +5,13 @@ import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metada import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util'; import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage'; import { TypedReflect } from 'src/utils/typed-reflect'; interface WorkspaceRelationBaseOptions { standardId: string; - label: - | MessageDescriptor - | ((objectMetadata: ObjectMetadataEntity) => MessageDescriptor); + label: MessageDescriptor; description?: | MessageDescriptor | ((objectMetadata: ObjectMetadataEntity) => MessageDescriptor); @@ -64,20 +63,15 @@ export function WorkspaceRelation( object, propertyKey.toString(), ); + const name = propertyKey.toString(); + const label = options.label.message ?? ''; + const isLabelSyncedWithName = computeMetadataNameFromLabel(label) === name; metadataArgsStorage.addRelations({ target: object.constructor, standardId: options.standardId, - name: propertyKey.toString(), - label: - typeof options.label === 'function' - ? (objectMetadata: ObjectMetadataEntity) => - ( - options.label as ( - obj: ObjectMetadataEntity, - ) => MessageDescriptor - )(objectMetadata).message ?? '' - : (options.label.message ?? ''), + name, + label, type: options.type, description: typeof options.description === 'function' @@ -96,6 +90,7 @@ export function WorkspaceRelation( isNullable, isSystem, gate, + isLabelSyncedWithName, }); }; } diff --git a/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-field-metadata-args.interface.ts b/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-field-metadata-args.interface.ts index 3f460f14c..e4f054816 100644 --- a/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-field-metadata-args.interface.ts +++ b/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-field-metadata-args.interface.ts @@ -105,4 +105,6 @@ export interface WorkspaceFieldMetadataArgs { * Is active field. */ readonly asExpression?: string; + + readonly isLabelSyncedWithName: boolean; } diff --git a/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface.ts b/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface.ts index eb5a7c126..7d7406e32 100644 --- a/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface.ts +++ b/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface.ts @@ -84,4 +84,6 @@ export interface WorkspaceRelationMetadataArgs { * Is active field. */ readonly isActive?: boolean; + + readonly isLabelSyncedWithName: boolean; } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field.factory.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field.factory.ts index c5870f52b..70c1f5df1 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field.factory.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-field.factory.ts @@ -121,7 +121,7 @@ export class StandardFieldFactory { * Create field metadata */ private createFieldMetadata( - workspaceEntityMetadataArgs: WorkspaceEntityMetadataArgs | undefined, + _workspaceEntityMetadataArgs: WorkspaceEntityMetadataArgs | undefined, workspaceFieldMetadataArgs: WorkspaceFieldMetadataArgs, context: WorkspaceSyncContext, ): PartialFieldMetadata[] { @@ -153,7 +153,7 @@ export class StandardFieldFactory { isActive: workspaceFieldMetadataArgs.isActive ?? true, asExpression: workspaceFieldMetadataArgs.asExpression, generatedType: workspaceFieldMetadataArgs.generatedType, - isLabelSyncedWithName: true, + isLabelSyncedWithName: workspaceFieldMetadataArgs.isLabelSyncedWithName, }, ]; } @@ -192,7 +192,8 @@ export class StandardFieldFactory { isNullable: true, isUnique: false, isActive: workspaceRelationMetadataArgs.isActive ?? true, - isLabelSyncedWithName: true, + isLabelSyncedWithName: + workspaceRelationMetadataArgs.isLabelSyncedWithName, }); return fieldMetadataCollection;