check on label metadata (#12852)

Better catching label input

- there were absolutely no check on label when creating the target field
while doing a relation : we crearted these checks here.

- We keep the label quite open to special char as discussed with Felix.
so mostly checking length of label.
  - We check that label does not already exists on the targetted object


- making sure the Target fieldinput label is checked before we create
it. The previous checks are not enough since the label goes through
anoteher merthod before going in the database

- validate-metadata-name-is-camel-case.utils.ts : making sure we can use
this error message for metadata name and for target label

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: prastoin <paul@twenty.com>
This commit is contained in:
Guillim
2025-06-24 20:20:37 +02:00
committed by GitHub
parent 02ce53bd2f
commit fb0cf11499
6 changed files with 40 additions and 15 deletions

View File

@ -44,6 +44,7 @@ import { isSelectOrMultiSelectFieldMetadata } from 'src/engine/metadata-modules/
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-on-delete-action.type';
import { 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 { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception';
import { validateFieldNameAvailabilityOrThrow } from 'src/engine/metadata-modules/utils/validate-field-name-availability.utils';
import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
@ -82,6 +83,7 @@ type ValidateFieldMetadataArgs<T extends UpdateFieldInput | CreateFieldInput> =
fieldMetadataInput: T;
objectMetadata: ObjectMetadataItemWithFieldMaps;
existingFieldMetadata?: FieldMetadataInterface;
objectMetadataMaps: ObjectMetadataMaps;
};
@Injectable()
@ -208,6 +210,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
existingFieldMetadata,
fieldMetadataInput: fieldMetadataForUpdate,
objectMetadata: objectMetadataItemWithFieldMaps,
objectMetadataMaps,
});
const isLabelSyncedWithName =
@ -528,6 +531,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
fieldMetadataType,
objectMetadata,
existingFieldMetadata,
objectMetadataMaps,
}: ValidateFieldMetadataArgs<T>): Promise<T> {
if (fieldMetadataInput.name) {
try {
@ -602,6 +606,21 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
await this.fieldMetadataValidationService.validateRelationCreationPayloadOrThrow(
relationCreationPayload,
);
const computedMetadataNameFromLabel = computeMetadataNameFromLabel(
relationCreationPayload.targetFieldLabel,
);
validateMetadataNameOrThrow(computedMetadataNameFromLabel);
const objectMetadataTarget =
objectMetadataMaps.byId[
relationCreationPayload.targetObjectMetadataId
];
validateFieldNameAvailabilityOrThrow(
computedMetadataNameFromLabel,
objectMetadataTarget,
);
}
}
@ -728,6 +747,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
fieldMetadataInput: CreateFieldInput,
objectMetadata: ObjectMetadataItemWithFieldMaps,
fieldMetadataRepository: Repository<FieldMetadataEntity>,
objectMetadataMaps: ObjectMetadataMaps,
): Promise<FieldMetadataEntity[]> {
if (!fieldMetadataInput.isRemoteCreation) {
assertMutationNotOnRemoteObject(objectMetadata);
@ -747,6 +767,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
relationCreationPayload: fieldMetadataInput.relationCreationPayload,
},
objectMetadata,
objectMetadataMaps,
});
if (fieldMetadataForCreate.isLabelSyncedWithName === true) {
@ -773,13 +794,15 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
);
}
const targetFieldMetadataName = computeMetadataNameFromLabel(
relationCreationPayload.targetFieldLabel,
);
const targetFieldMetadataToCreate =
this.prepareCustomFieldMetadataForCreation({
objectMetadataId: relationCreationPayload.targetObjectMetadataId,
type: FieldMetadataType.RELATION,
name: computeMetadataNameFromLabel(
relationCreationPayload.targetFieldLabel,
),
name: targetFieldMetadataName,
label: relationCreationPayload.targetFieldLabel,
icon: relationCreationPayload.targetFieldIcon,
workspaceId: fieldMetadataForCreate.workspaceId,
@ -900,6 +923,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
fieldMetadataInput,
objectMetadata,
fieldMetadataRepository,
objectMetadataMaps,
);
createdFieldMetadatas.push(...createdFieldMetadataItems);