Fix standard object computed metadata (#12883)
# Introduction close https://github.com/twentyhq/twenty/issues/12879 This PR has a global impact all on workspaces It should be crash tested in local using an anon extract of the db
This commit is contained in:
@ -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<Workspace>,
|
||||||
|
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||||
|
@InjectRepository(FieldMetadataEntity, 'core')
|
||||||
|
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||||
|
) {
|
||||||
|
super(workspaceRepository, twentyORMGlobalManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async runOnWorkspace({
|
||||||
|
workspaceId,
|
||||||
|
options,
|
||||||
|
}: RunOnWorkspaceArgs): Promise<void> {
|
||||||
|
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}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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 {}
|
||||||
@ -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_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 { 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 {
|
import {
|
||||||
DatabaseMigrationService,
|
DatabaseMigrationService,
|
||||||
UpgradeCommand,
|
UpgradeCommand,
|
||||||
@ -15,6 +16,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
|
|||||||
TypeOrmModule.forFeature([Workspace], 'core'),
|
TypeOrmModule.forFeature([Workspace], 'core'),
|
||||||
V0_54_UpgradeVersionCommandModule,
|
V0_54_UpgradeVersionCommandModule,
|
||||||
V0_55_UpgradeVersionCommandModule,
|
V0_55_UpgradeVersionCommandModule,
|
||||||
|
V1_1_UpgradeVersionCommandModule,
|
||||||
WorkspaceSyncMetadataModule,
|
WorkspaceSyncMetadataModule,
|
||||||
],
|
],
|
||||||
providers: [DatabaseMigrationService, UpgradeCommand],
|
providers: [DatabaseMigrationService, UpgradeCommand],
|
||||||
|
|||||||
@ -175,12 +175,18 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
|||||||
beforeSyncMetadata: [],
|
beforeSyncMetadata: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const commands_110: VersionCommands = {
|
||||||
|
afterSyncMetadata: [],
|
||||||
|
beforeSyncMetadata: [],
|
||||||
|
};
|
||||||
|
|
||||||
this.allCommands = {
|
this.allCommands = {
|
||||||
'0.53.0': commands_053,
|
'0.53.0': commands_053,
|
||||||
'0.54.0': commands_054,
|
'0.54.0': commands_054,
|
||||||
'0.55.0': commands_055,
|
'0.55.0': commands_055,
|
||||||
'0.60.0': commands_060,
|
'0.60.0': commands_060,
|
||||||
'1.0.0': commands_100,
|
'1.0.0': commands_100,
|
||||||
|
'1.1.0': commands_110,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 { 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 { 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 { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
|
||||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||||
|
|
||||||
@ -19,12 +19,8 @@ export interface WorkspaceFieldOptions<
|
|||||||
> {
|
> {
|
||||||
standardId: string;
|
standardId: string;
|
||||||
type: T;
|
type: T;
|
||||||
label:
|
label: MessageDescriptor;
|
||||||
| MessageDescriptor
|
description?: MessageDescriptor;
|
||||||
| ((objectMetadata: ObjectMetadataEntity) => MessageDescriptor);
|
|
||||||
description?:
|
|
||||||
| MessageDescriptor
|
|
||||||
| ((objectMetadata: ObjectMetadataEntity) => MessageDescriptor);
|
|
||||||
icon?: string;
|
icon?: string;
|
||||||
defaultValue?: FieldMetadataDefaultValue<T>;
|
defaultValue?: FieldMetadataDefaultValue<T>;
|
||||||
options?: FieldMetadataOptions<T>;
|
options?: FieldMetadataOptions<T>;
|
||||||
@ -76,30 +72,18 @@ export function WorkspaceField<T extends FieldMetadataType>(
|
|||||||
|
|
||||||
const defaultValue = (options.defaultValue ??
|
const defaultValue = (options.defaultValue ??
|
||||||
generateDefaultValue(options.type)) as FieldMetadataDefaultValue | null;
|
generateDefaultValue(options.type)) as FieldMetadataDefaultValue | null;
|
||||||
|
const name = propertyKey.toString();
|
||||||
|
const label = options.label.message ?? '';
|
||||||
|
const isLabelSyncedWithName = computeMetadataNameFromLabel(label) === name;
|
||||||
|
|
||||||
metadataArgsStorage.addFields({
|
metadataArgsStorage.addFields({
|
||||||
target: object.constructor,
|
target: object.constructor,
|
||||||
standardId: options.standardId,
|
standardId: options.standardId,
|
||||||
name: propertyKey.toString(),
|
name,
|
||||||
label:
|
label,
|
||||||
typeof options.label === 'function'
|
|
||||||
? (objectMetadata: ObjectMetadataEntity) =>
|
|
||||||
(
|
|
||||||
options.label as (
|
|
||||||
obj: ObjectMetadataEntity,
|
|
||||||
) => MessageDescriptor
|
|
||||||
)(objectMetadata).message ?? ''
|
|
||||||
: (options.label.message ?? ''),
|
|
||||||
type: options.type,
|
type: options.type,
|
||||||
description:
|
isLabelSyncedWithName,
|
||||||
typeof options.description === 'function'
|
description: options.description?.message ?? '',
|
||||||
? (objectMetadata: ObjectMetadataEntity) =>
|
|
||||||
(
|
|
||||||
options.description as (
|
|
||||||
obj: ObjectMetadataEntity,
|
|
||||||
) => MessageDescriptor
|
|
||||||
)(objectMetadata).message ?? ''
|
|
||||||
: (options.description?.message ?? ''),
|
|
||||||
icon: options.icon,
|
icon: options.icon,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
options: options.options,
|
options: options.options,
|
||||||
|
|||||||
@ -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 { 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 { 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 { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
|
||||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||||
|
|
||||||
interface WorkspaceRelationBaseOptions<TClass> {
|
interface WorkspaceRelationBaseOptions<TClass> {
|
||||||
standardId: string;
|
standardId: string;
|
||||||
label:
|
label: MessageDescriptor;
|
||||||
| MessageDescriptor
|
|
||||||
| ((objectMetadata: ObjectMetadataEntity) => MessageDescriptor);
|
|
||||||
description?:
|
description?:
|
||||||
| MessageDescriptor
|
| MessageDescriptor
|
||||||
| ((objectMetadata: ObjectMetadataEntity) => MessageDescriptor);
|
| ((objectMetadata: ObjectMetadataEntity) => MessageDescriptor);
|
||||||
@ -64,20 +63,15 @@ export function WorkspaceRelation<TClass extends object>(
|
|||||||
object,
|
object,
|
||||||
propertyKey.toString(),
|
propertyKey.toString(),
|
||||||
);
|
);
|
||||||
|
const name = propertyKey.toString();
|
||||||
|
const label = options.label.message ?? '';
|
||||||
|
const isLabelSyncedWithName = computeMetadataNameFromLabel(label) === name;
|
||||||
|
|
||||||
metadataArgsStorage.addRelations({
|
metadataArgsStorage.addRelations({
|
||||||
target: object.constructor,
|
target: object.constructor,
|
||||||
standardId: options.standardId,
|
standardId: options.standardId,
|
||||||
name: propertyKey.toString(),
|
name,
|
||||||
label:
|
label,
|
||||||
typeof options.label === 'function'
|
|
||||||
? (objectMetadata: ObjectMetadataEntity) =>
|
|
||||||
(
|
|
||||||
options.label as (
|
|
||||||
obj: ObjectMetadataEntity,
|
|
||||||
) => MessageDescriptor
|
|
||||||
)(objectMetadata).message ?? ''
|
|
||||||
: (options.label.message ?? ''),
|
|
||||||
type: options.type,
|
type: options.type,
|
||||||
description:
|
description:
|
||||||
typeof options.description === 'function'
|
typeof options.description === 'function'
|
||||||
@ -96,6 +90,7 @@ export function WorkspaceRelation<TClass extends object>(
|
|||||||
isNullable,
|
isNullable,
|
||||||
isSystem,
|
isSystem,
|
||||||
gate,
|
gate,
|
||||||
|
isLabelSyncedWithName,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,4 +105,6 @@ export interface WorkspaceFieldMetadataArgs {
|
|||||||
* Is active field.
|
* Is active field.
|
||||||
*/
|
*/
|
||||||
readonly asExpression?: string;
|
readonly asExpression?: string;
|
||||||
|
|
||||||
|
readonly isLabelSyncedWithName: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,4 +84,6 @@ export interface WorkspaceRelationMetadataArgs {
|
|||||||
* Is active field.
|
* Is active field.
|
||||||
*/
|
*/
|
||||||
readonly isActive?: boolean;
|
readonly isActive?: boolean;
|
||||||
|
|
||||||
|
readonly isLabelSyncedWithName: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -121,7 +121,7 @@ export class StandardFieldFactory {
|
|||||||
* Create field metadata
|
* Create field metadata
|
||||||
*/
|
*/
|
||||||
private createFieldMetadata(
|
private createFieldMetadata(
|
||||||
workspaceEntityMetadataArgs: WorkspaceEntityMetadataArgs | undefined,
|
_workspaceEntityMetadataArgs: WorkspaceEntityMetadataArgs | undefined,
|
||||||
workspaceFieldMetadataArgs: WorkspaceFieldMetadataArgs,
|
workspaceFieldMetadataArgs: WorkspaceFieldMetadataArgs,
|
||||||
context: WorkspaceSyncContext,
|
context: WorkspaceSyncContext,
|
||||||
): PartialFieldMetadata[] {
|
): PartialFieldMetadata[] {
|
||||||
@ -153,7 +153,7 @@ export class StandardFieldFactory {
|
|||||||
isActive: workspaceFieldMetadataArgs.isActive ?? true,
|
isActive: workspaceFieldMetadataArgs.isActive ?? true,
|
||||||
asExpression: workspaceFieldMetadataArgs.asExpression,
|
asExpression: workspaceFieldMetadataArgs.asExpression,
|
||||||
generatedType: workspaceFieldMetadataArgs.generatedType,
|
generatedType: workspaceFieldMetadataArgs.generatedType,
|
||||||
isLabelSyncedWithName: true,
|
isLabelSyncedWithName: workspaceFieldMetadataArgs.isLabelSyncedWithName,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -192,7 +192,8 @@ export class StandardFieldFactory {
|
|||||||
isNullable: true,
|
isNullable: true,
|
||||||
isUnique: false,
|
isUnique: false,
|
||||||
isActive: workspaceRelationMetadataArgs.isActive ?? true,
|
isActive: workspaceRelationMetadataArgs.isActive ?? true,
|
||||||
isLabelSyncedWithName: true,
|
isLabelSyncedWithName:
|
||||||
|
workspaceRelationMetadataArgs.isLabelSyncedWithName,
|
||||||
});
|
});
|
||||||
|
|
||||||
return fieldMetadataCollection;
|
return fieldMetadataCollection;
|
||||||
|
|||||||
Reference in New Issue
Block a user