[fix] Support non latin characters in schema names (#5063)

Fixes #4943

## How was it tested?
Local (front + /metadata)
Unit tests for utils

---------

Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
Marie
2024-04-23 13:37:29 +02:00
committed by GitHub
parent 824786ff04
commit ff39ba5a15
16 changed files with 236 additions and 13 deletions

View File

@ -0,0 +1,9 @@
import { BadRequestException } from '@nestjs/common';
export class InvalidStringException extends BadRequestException {
constructor(string: string) {
const message = `String "${string}" is not valid`;
super(message);
}
}

View File

@ -39,6 +39,8 @@ import {
import { DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util';
import { validateString } from 'src/engine/metadata-modules/remote-server/utils/validate-remote-server-input';
import { InvalidStringException } from 'src/engine/metadata-modules/errors/InvalidStringException';
import {
FieldMetadataEntity,
@ -114,6 +116,8 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
fieldMetadataInput.options = generateRatingOptions();
}
this.validateFieldMetadataInput<CreateFieldInput>(fieldMetadataInput);
const fieldAlreadyExists = await fieldMetadataRepository.findOne({
where: {
name: fieldMetadataInput.name,
@ -293,6 +297,8 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
}
}
this.validateFieldMetadataInput<UpdateFieldInput>(fieldMetadataInput);
const updatableFieldInput =
existingFieldMetadata.isCustom === false
? this.buildUpdatableStandardFieldInput(
@ -533,4 +539,24 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
};
}
}
private validateFieldMetadataInput<
T extends UpdateFieldInput | CreateFieldInput,
>(fieldMetadataInput: T): T {
if (fieldMetadataInput.name) {
try {
validateString(fieldMetadataInput.name);
} catch (error) {
if (error instanceof InvalidStringException) {
throw new BadRequestException(
`Characters used in name "${fieldMetadataInput.name}" are not supported`,
);
} else {
throw error;
}
}
}
return fieldMetadataInput;
}
}

View File

@ -59,6 +59,7 @@ import {
FeatureFlagKeys,
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { validateObjectMetadataInput } from 'src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util';
import { ObjectMetadataEntity } from './object-metadata.entity';
@ -237,6 +238,8 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
objectMetadataInput.workspaceId,
);
validateObjectMetadataInput(objectMetadataInput);
if (
objectMetadataInput.nameSingular.toLowerCase() ===
objectMetadataInput.namePlural.toLowerCase()

View File

@ -0,0 +1,31 @@
import { BadRequestException } from '@nestjs/common';
import { InvalidStringException } from 'src/engine/metadata-modules/errors/InvalidStringException';
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
import { UpdateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
import { validateMetadataName } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
export const validateObjectMetadataInput = <
T extends UpdateObjectInput | CreateObjectInput,
>(
objectMetadataInput: T,
): void => {
try {
if (objectMetadataInput.nameSingular) {
validateMetadataName(objectMetadataInput.nameSingular);
}
if (objectMetadataInput.namePlural) {
validateMetadataName(objectMetadataInput.namePlural);
}
} catch (error) {
if (error instanceof InvalidStringException) {
console.error(error.message);
throw new BadRequestException(
`Characters used in name "${objectMetadataInput.nameSingular}" or "${objectMetadataInput.namePlural}" are not supported`,
);
} else {
throw error;
}
}
};

View File

@ -24,6 +24,8 @@ import {
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
import { InvalidStringException } from 'src/engine/metadata-modules/errors/InvalidStringException';
import { validateMetadataName } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
import {
RelationMetadataEntity,
@ -51,6 +53,20 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
relationMetadataInput,
);
try {
validateMetadataName(relationMetadataInput.fromName);
validateMetadataName(relationMetadataInput.toName);
} catch (error) {
if (error instanceof InvalidStringException) {
console.error(error.message);
throw new BadRequestException(
`Characters used in name "${relationMetadataInput.fromName}" or "${relationMetadataInput.toName}" are not supported`,
);
} else {
throw error;
}
}
await this.validateCreateRelationMetadataInput(
relationMetadataInput,
objectMetadataMap,

View File

@ -0,0 +1,22 @@
import { InvalidStringException } from 'src/engine/metadata-modules/errors/InvalidStringException';
import { validateMetadataName } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
describe('validateMetadataName', () => {
it('does not throw if string is valid', () => {
const input = 'testName';
expect(validateMetadataName(input)).not.toThrow;
});
it('throws error if string has non latin characters', () => {
const input = 'בְרִבְרִ';
expect(() => validateMetadataName(input)).toThrow(InvalidStringException);
});
it('throws error if starts with digits', () => {
const input = '123string';
expect(() => validateMetadataName(input)).toThrow(InvalidStringException);
});
});

View File

@ -0,0 +1,9 @@
import { InvalidStringException } from 'src/engine/metadata-modules/errors/InvalidStringException';
const VALID_STRING_PATTERN = /^[a-zA-Z][a-zA-Z0-9 ]*$/;
export const validateMetadataName = (string: string) => {
if (!string.match(VALID_STRING_PATTERN)) {
throw new InvalidStringException(string);
}
};