Fix user friendly message for non available object name (#14210)

Fixes https://github.com/twentyhq/twenty/issues/13577.

When naming or renaming an object, we are already checking that the name
was available compared to other existing objects.
But we also need to check that the relation fields that will be created
or updated are available as well: we create relation fields on
Attachment, Note, Favorite, Task and TimelineActivites, that bear the
name of the object. Thus, it is not possible to create an object that
has the same name as one of the fields on Attachment, Note etc: name,
createdAt, ... are not valid names.
This commit is contained in:
Marie
2025-09-01 18:35:40 +02:00
committed by GitHub
parent 3f7f78eef8
commit 0d5fedfb0a
13 changed files with 208 additions and 67 deletions
@@ -40,6 +40,7 @@ import {
} from 'src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util';
import { SearchVectorService } from 'src/engine/metadata-modules/search-vector/search-vector.service';
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
import { validateMetadataIdentifierFieldMetadataIds } from 'src/engine/metadata-modules/utils/validate-metadata-identifier-field-metadata-id.utils';
import { validateNameAndLabelAreSyncOrThrow } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util';
import { validatesNoOtherObjectWithSameNameExistsOrThrows } from 'src/engine/metadata-modules/utils/validate-no-other-object-with-same-name-exists-or-throw.util';
@@ -221,10 +222,18 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
if (createObjectInput.isRemote) {
throw new Error('Remote objects are not supported yet');
} else {
const fieldsById = createdObjectMetadata.fields.reduce(
(acc, field) => ({
...acc,
[field.id]: field,
}),
{},
);
const createdRelatedObjectMetadataCollection =
await this.objectMetadataFieldRelationService.createRelationsAndForeignKeysMetadata(
createObjectInput.workspaceId,
createdObjectMetadata,
{ ...createdObjectMetadata, fieldsById },
objectMetadataMaps,
queryRunner,
);
@@ -374,12 +383,14 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
});
const { didUpdateLabelOrIcon } =
await this.handleObjectNameAndLabelUpdates(
await this.handleObjectNameAndLabelUpdates({
existingObjectMetadata,
existingObjectMetadataCombinedWithUpdateInput,
objectMetadataForUpdate:
existingObjectMetadataCombinedWithUpdateInput,
inputPayload,
queryRunner,
);
objectMetadataMaps,
});
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrationsWithinTransaction(
workspaceId,
@@ -602,11 +613,17 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
await this.objectMetadataRepository.delete({ workspaceId });
}
private async handleObjectNameAndLabelUpdates(
private async handleObjectNameAndLabelUpdates({
existingObjectMetadata,
objectMetadataForUpdate,
inputPayload,
queryRunner,
objectMetadataMaps,
}: {
existingObjectMetadata: Pick<
ObjectMetadataItemWithFieldMaps,
'nameSingular' | 'isCustom' | 'id' | 'labelPlural' | 'icon' | 'fieldsById'
>,
>;
objectMetadataForUpdate: Pick<
ObjectMetadataItemWithFieldMaps,
| 'nameSingular'
@@ -617,10 +634,11 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
| 'labelPlural'
| 'icon'
| 'fieldsById'
>,
inputPayload: UpdateObjectPayload,
queryRunner: QueryRunner,
): Promise<{ didUpdateLabelOrIcon: boolean }> {
>;
inputPayload: UpdateObjectPayload;
queryRunner: QueryRunner;
objectMetadataMaps: ObjectMetadataMaps;
}): Promise<{ didUpdateLabelOrIcon: boolean }> {
const newTargetTableName = computeObjectTargetTable(
objectMetadataForUpdate,
);
@@ -638,9 +656,12 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
const relationMetadataCollection =
await this.objectMetadataFieldRelationService.updateRelationsAndForeignKeysMetadata(
objectMetadataForUpdate.workspaceId,
objectMetadataForUpdate,
queryRunner,
{
workspaceId: objectMetadataForUpdate.workspaceId,
updatedObjectMetadata: objectMetadataForUpdate,
queryRunner,
objectMetadataMaps,
},
);
await this.objectMetadataMigrationService.updateRelationMigrations(