From ee6180a76f7f8afcdcaea1e957a508a38729f196 Mon Sep 17 00:00:00 2001 From: Marie <51697796+ijreilly@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:24:10 +0200 Subject: [PATCH] [Fix] Prevent fields name conflicts with composite subfields names (#6713) At field creation we are checking the availability of the name by comparing it to the other fields' names' on the object; but for composite fields the fields' names' as indicated in the repository do not exactly match the column names' on the tables (e.g "createdBy" field is actually represented by columns createdByName, createdBySource etc.). In this PR we prevent the conflict with the standard composite fields' names. There is still room for errors with the custom composite fields: for example a custom composite field "address" of type address on a custom object "listing" will introduce the columns addressAddressStreet1, addressAddressStreet2 etc. while we won't prevent the user from later creating a custom field named "addressAddressStreet1". For now I decided not to tackle this as this seem extremely edgy + would impact performance on creation of all fields while never actually useful (I think). --- .../__mocks__/object-metadata-item.mock.ts | 24 ++++---- .../factories/args-alias.factory.ts | 4 +- .../factories/field-alias.factory.ts | 6 +- .../type-definitions.generator.ts | 28 ++++----- ...ld-metadata-to-graphql-query.utils.spec.ts | 10 ---- .../query-builder/utils/check-fields.utils.ts | 4 +- .../utils/check-order-by.utils.ts | 6 +- .../__tests__/order-by-input.factory.spec.ts | 10 ++-- .../utils/__tests__/components.utils.spec.ts | 13 ++-- .../open-api/utils/components.utils.ts | 4 +- .../field-metadata/composite-types/index.ts | 2 +- .../field-metadata/field-metadata.service.ts | 38 ++++++++---- .../validate-object-metadata-input.util.ts | 8 +-- .../relation-metadata.service.ts | 20 +++++-- .../validate-field-name-availability.spec.ts | 60 +++++++++++++++++++ ...> validate-metadata-name-validity.spec.ts} | 29 +++++---- .../exceptions/invalid-string.exception.ts | 7 +++ .../name-not-available.exception.ts | 7 +++ .../exceptions/name-too-long.exception.ts | 7 +++ .../validate-field-name-availability.utils.ts | 38 ++++++++++++ .../validate-metadata-name-validity.utils.ts | 14 +++++ .../utils/validate-metadata-name.utils.ts | 28 --------- .../composite-column-action.factory.ts | 16 ++--- .../factories/entity-schema-column.factory.ts | 6 +- .../repository/workspace.repository.ts | 6 +- .../services/database-structure.service.ts | 22 +++---- .../services/field-metadata-health.service.ts | 24 ++++---- 27 files changed, 275 insertions(+), 166 deletions(-) create mode 100644 packages/twenty-server/src/engine/metadata-modules/utils/__tests__/validate-field-name-availability.spec.ts rename packages/twenty-server/src/engine/metadata-modules/utils/__tests__/{validate-metadata-name.spec.ts => validate-metadata-name-validity.spec.ts} (51%) create mode 100644 packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-string.exception.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/utils/exceptions/name-not-available.exception.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/utils/exceptions/name-too-long.exception.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/utils/validate-field-name-availability.utils.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-validity.utils.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name.utils.ts diff --git a/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts b/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts index 37ee2af4e7e4..1a20e09c9c55 100644 --- a/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts +++ b/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts @@ -2,6 +2,12 @@ import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/com import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +export const FIELD_LINKS_MOCK_NAME = 'fieldLinks'; +export const FIELD_CURRENCY_MOCK_NAME = 'fieldCurrency'; +export const FIELD_ADDRESS_MOCK_NAME = 'fieldAddress'; +export const FIELD_ACTOR_MOCK_NAME = 'fieldActor'; +export const FIELD_FULL_NAME_MOCK_NAME = 'fieldFullName'; + export const fieldNumberMock = { name: 'fieldNumber', type: FieldMetadataType.NUMBER, @@ -16,15 +22,8 @@ export const fieldTextMock = { defaultValue: null, }; -export const fieldLinkMock = { - name: 'fieldLink', - type: FieldMetadataType.LINK, - isNullable: false, - defaultValue: { label: '', url: '' }, -}; - export const fieldCurrencyMock = { - name: 'fieldCurrency', + name: FIELD_CURRENCY_MOCK_NAME, type: FieldMetadataType.CURRENCY, isNullable: true, defaultValue: { amountMicros: null, currencyCode: "''" }, @@ -89,7 +88,7 @@ export const fieldRelationMock = { }; const fieldLinksMock = { - name: 'fieldLinks', + name: FIELD_LINKS_MOCK_NAME, type: FieldMetadataType.LINKS, isNullable: false, defaultValue: [ @@ -147,7 +146,7 @@ const fieldNumericMock = { }; const fieldFullNameMock = { - name: 'fieldFullName', + name: FIELD_FULL_NAME_MOCK_NAME, type: FieldMetadataType.FULL_NAME, isNullable: true, defaultValue: { firstName: '', lastName: '' }, @@ -168,7 +167,7 @@ const fieldPositionMock = { }; const fieldAddressMock = { - name: 'fieldAddress', + name: FIELD_ADDRESS_MOCK_NAME, type: FieldMetadataType.ADDRESS, isNullable: true, defaultValue: { @@ -198,7 +197,7 @@ const fieldRichTextMock = { }; const fieldActorMock = { - name: 'fieldActor', + name: FIELD_ACTOR_MOCK_NAME, type: FieldMetadataType.ACTOR, isNullable: true, defaultValue: { @@ -217,7 +216,6 @@ export const fields = [ fieldBooleanMock, fieldNumberMock, fieldNumericMock, - fieldLinkMock, fieldLinksMock, fieldCurrencyMock, fieldFullNameMock, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/args-alias.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/args-alias.factory.ts index 5703a9747035..f5ccbf97515d 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/args-alias.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/args-alias.factory.ts @@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; @@ -52,7 +52,7 @@ export class ArgsAliasFactory { isCompositeFieldMetadataType(fieldMetadata.type) ) { // Get composite type definition - const compositeType = compositeTypeDefintions.get(fieldMetadata.type); + const compositeType = compositeTypeDefinitions.get(fieldMetadata.type); if (!compositeType) { this.logger.error( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/field-alias.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/field-alias.factory.ts index 67a22534f875..e50028109e6f 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/field-alias.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/field-alias.factory.ts @@ -2,13 +2,13 @@ import { Injectable, Logger } from '@nestjs/common'; import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; -import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { createCompositeFieldKey } from 'src/engine/api/graphql/workspace-query-builder/utils/composite-field-metadata.util'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { computeColumnName, computeCompositeColumnName, } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; +import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; @Injectable() export class FieldAliasFactory { @@ -23,7 +23,7 @@ export class FieldAliasFactory { } // If it's a composite field, we need to get the definition - const compositeType = compositeTypeDefintions.get(fieldMetadata.type); + const compositeType = compositeTypeDefinitions.get(fieldMetadata.type); if (!compositeType) { this.logger.error( diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/type-definitions.generator.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/type-definitions.generator.ts index 142db1f7a686..57e959dfc34c 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/type-definitions.generator.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/type-definitions.generator.ts @@ -1,27 +1,27 @@ import { Injectable, Logger } from '@nestjs/common'; -import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface'; +import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; -import { EnumTypeDefinitionFactory } from 'src/engine/api/graphql/workspace-schema-builder/factories/enum-type-definition.factory'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; -import { CompositeObjectTypeDefinitionFactory } from 'src/engine/api/graphql/workspace-schema-builder/factories/composite-object-type-definition.factory'; -import { CompositeInputTypeDefinitionFactory } from 'src/engine/api/graphql/workspace-schema-builder/factories/composite-input-type-definition.factory'; import { CompositeEnumTypeDefinitionFactory } from 'src/engine/api/graphql/workspace-schema-builder/factories/composite-enum-type-definition.factory'; +import { CompositeInputTypeDefinitionFactory } from 'src/engine/api/graphql/workspace-schema-builder/factories/composite-input-type-definition.factory'; +import { CompositeObjectTypeDefinitionFactory } from 'src/engine/api/graphql/workspace-schema-builder/factories/composite-object-type-definition.factory'; +import { EnumTypeDefinitionFactory } from 'src/engine/api/graphql/workspace-schema-builder/factories/enum-type-definition.factory'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; -import { TypeDefinitionsStorage } from './storages/type-definitions.storage'; -import { - ObjectTypeDefinitionFactory, - ObjectTypeDefinitionKind, -} from './factories/object-type-definition.factory'; +import { ConnectionTypeDefinitionFactory } from './factories/connection-type-definition.factory'; +import { EdgeTypeDefinitionFactory } from './factories/edge-type-definition.factory'; +import { ExtendObjectTypeDefinitionFactory } from './factories/extend-object-type-definition.factory'; import { InputTypeDefinitionFactory, InputTypeDefinitionKind, } from './factories/input-type-definition.factory'; +import { + ObjectTypeDefinitionFactory, + ObjectTypeDefinitionKind, +} from './factories/object-type-definition.factory'; import { WorkspaceBuildSchemaOptions } from './interfaces/workspace-build-schema-optionts.interface'; -import { ConnectionTypeDefinitionFactory } from './factories/connection-type-definition.factory'; -import { EdgeTypeDefinitionFactory } from './factories/edge-type-definition.factory'; -import { ExtendObjectTypeDefinitionFactory } from './factories/extend-object-type-definition.factory'; +import { TypeDefinitionsStorage } from './storages/type-definitions.storage'; import { objectContainsRelationField } from './utils/object-contains-relation-field'; @Injectable() @@ -55,7 +55,7 @@ export class TypeDefinitionsGenerator { * GENERATE COMPOSITE TYPE OBJECTS */ private generateCompositeTypeDefs(options: WorkspaceBuildSchemaOptions) { - const compositeTypeCollection = [...compositeTypeDefintions.values()]; + const compositeTypeCollection = [...compositeTypeDefinitions.values()]; this.logger.log( `Generating composite type objects: [${compositeTypeCollection diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/__tests__/map-field-metadata-to-graphql-query.utils.spec.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/__tests__/map-field-metadata-to-graphql-query.utils.spec.ts index 473c6f8408d8..4fec6c7ab443 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/__tests__/map-field-metadata-to-graphql-query.utils.spec.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/__tests__/map-field-metadata-to-graphql-query.utils.spec.ts @@ -1,6 +1,5 @@ import { fieldCurrencyMock, - fieldLinkMock, fieldNumberMock, fieldTextMock, objectMetadataItemMock, @@ -17,15 +16,6 @@ describe('mapFieldMetadataToGraphqlQuery', () => { expect( mapFieldMetadataToGraphqlQuery([objectMetadataItemMock], fieldTextMock), ).toEqual('fieldText'); - expect( - mapFieldMetadataToGraphqlQuery([objectMetadataItemMock], fieldLinkMock), - ).toEqual(` - fieldLink - { - label - url - } - `); expect( mapFieldMetadataToGraphqlQuery( [objectMetadataItemMock], diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/check-fields.utils.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/check-fields.utils.ts index 00f5ec990c6c..df9141b99a4a 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/check-fields.utils.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/check-fields.utils.ts @@ -2,7 +2,7 @@ import { BadRequestException } from '@nestjs/common'; import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; @@ -13,7 +13,7 @@ export const checkFields = ( const fieldMetadataNames = objectMetadata.fields .map((field) => { if (isCompositeFieldMetadataType(field.type)) { - const compositeType = compositeTypeDefintions.get(field.type); + const compositeType = compositeTypeDefinitions.get(field.type); if (!compositeType) { throw new BadRequestException( diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/check-order-by.utils.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/check-order-by.utils.ts index ce14a02bd276..851282b6727b 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/check-order-by.utils.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/check-order-by.utils.ts @@ -1,9 +1,9 @@ import { BadRequestException } from '@nestjs/common'; -import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; import { Record } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface'; +import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; @@ -14,7 +14,7 @@ export const checkArrayFields = ( const fieldMetadataNames = objectMetadata.fields .map((field) => { if (isCompositeFieldMetadataType(field.type)) { - const compositeType = compositeTypeDefintions.get(field.type); + const compositeType = compositeTypeDefinitions.get(field.type); if (!compositeType) { throw new BadRequestException( diff --git a/packages/twenty-server/src/engine/api/rest/input-factories/__tests__/order-by-input.factory.spec.ts b/packages/twenty-server/src/engine/api/rest/input-factories/__tests__/order-by-input.factory.spec.ts index 63b3179ee68d..9f6923c23802 100644 --- a/packages/twenty-server/src/engine/api/rest/input-factories/__tests__/order-by-input.factory.spec.ts +++ b/packages/twenty-server/src/engine/api/rest/input-factories/__tests__/order-by-input.factory.spec.ts @@ -54,7 +54,7 @@ describe('OrderByInputFactory', () => { ]); }); - it('should handler complex fields', () => { + it('should handle complex fields', () => { const request: any = { query: { order_by: 'fieldCurrency.amountMicros', @@ -66,7 +66,7 @@ describe('OrderByInputFactory', () => { ]); }); - it('should handler complex fields with direction', () => { + it('should handle complex fields with direction', () => { const request: any = { query: { order_by: 'fieldCurrency.amountMicros[DescNullsLast]', @@ -78,17 +78,17 @@ describe('OrderByInputFactory', () => { ]); }); - it('should handler multiple complex fields with direction', () => { + it('should handle multiple complex fields with direction', () => { const request: any = { query: { order_by: - 'fieldCurrency.amountMicros[DescNullsLast],fieldLink.label[AscNullsLast]', + 'fieldCurrency.amountMicros[DescNullsLast],fieldText.label[AscNullsLast]', }, }; expect(service.create(request, objectMetadata)).toEqual([ { fieldCurrency: { amountMicros: OrderByDirection.DescNullsLast } }, - { fieldLink: { label: OrderByDirection.AscNullsLast } }, + { fieldText: { label: OrderByDirection.AscNullsLast } }, ]); }); diff --git a/packages/twenty-server/src/engine/core-modules/open-api/utils/__tests__/components.utils.spec.ts b/packages/twenty-server/src/engine/core-modules/open-api/utils/__tests__/components.utils.spec.ts index a99a07ef36ed..3275b8392d01 100644 --- a/packages/twenty-server/src/engine/core-modules/open-api/utils/__tests__/components.utils.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/open-api/utils/__tests__/components.utils.spec.ts @@ -7,9 +7,11 @@ import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/fi import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; describe('computeSchemaComponents', () => { - it('should test all field types', () => { + it('should test all non-deprecated field types', () => { expect(fields.map((field) => field.type)).toEqual( - Object.keys(FieldMetadataType), + Object.keys(FieldMetadataType).filter( + (key) => key !== FieldMetadataType.LINK, + ), ); }); it('should compute schema components', () => { @@ -55,13 +57,6 @@ describe('computeSchemaComponents', () => { fieldNumeric: { type: 'number', }, - fieldLink: { - properties: { - label: { type: 'string' }, - url: { type: 'string' }, - }, - type: 'object', - }, fieldLinks: { properties: { primaryLinkLabel: { type: 'string' }, diff --git a/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts b/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts index ea26a3118349..bd7248cc0939 100644 --- a/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts +++ b/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts @@ -9,7 +9,7 @@ import { computeOrderByParameters, computeStartingAfterParameters, } from 'src/engine/core-modules/open-api/utils/parameters.utils'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { capitalize } from 'src/utils/capitalize'; @@ -74,7 +74,7 @@ const getSchemaComponentsProperties = ( case FieldMetadataType.ACTOR: itemProperty = { type: 'object', - properties: compositeTypeDefintions + properties: compositeTypeDefinitions .get(field.type) ?.properties?.reduce((properties, property) => { properties[property.name] = getFieldProperties(property.type); diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts index 442fe63566ee..7f1a2f129b86 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts @@ -13,7 +13,7 @@ export type CompositeFieldsDefinitionFunction = ( fieldMetadata?: FieldMetadataInterface, ) => FieldMetadataInterface[]; -export const compositeTypeDefintions = new Map< +export const compositeTypeDefinitions = new Map< FieldMetadataType, CompositeType >([ diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts index 6cb1cdebefad..277db13e4f43 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts @@ -8,7 +8,7 @@ import { v4 as uuidV4 } from 'uuid'; import { TypeORMService } from 'src/database/typeorm/typeorm.service'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; import { DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input'; import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto'; @@ -28,18 +28,19 @@ import { } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; import { generateNullable } from 'src/engine/metadata-modules/field-metadata/utils/generate-nullable'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util'; import { RelationMetadataEntity, RelationMetadataType, } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; +import { InvalidStringException } from 'src/engine/metadata-modules/utils/exceptions/invalid-string.exception'; +import { NameNotAvailableException } from 'src/engine/metadata-modules/utils/exceptions/name-not-available.exception'; +import { NameTooLongException } from 'src/engine/metadata-modules/utils/exceptions/name-too-long.exception'; import { exceedsDatabaseIdentifierMaximumLength } from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils'; -import { - InvalidStringException, - NameTooLongException, - validateMetadataNameOrThrow, -} from 'src/engine/metadata-modules/utils/validate-metadata-name.utils'; +import { validateFieldNameAvailabilityOrThrow } from 'src/engine/metadata-modules/utils/validate-field-name-availability.utils'; +import { validateMetadataNameValidityOrThrow as validateFieldNameValidityOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name-validity.utils'; import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.service'; import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util'; import { @@ -140,7 +141,10 @@ export class FieldMetadataService extends TypeOrmQueryService(fieldMetadataInput); + this.validateFieldMetadataInput( + fieldMetadataInput, + objectMetadata, + ); const fieldAlreadyExists = await fieldMetadataRepository.findOne({ where: { @@ -355,7 +359,10 @@ export class FieldMetadataService extends TypeOrmQueryService(fieldMetadataInput); + this.validateFieldMetadataInput( + fieldMetadataInput, + objectMetadata, + ); const updatableFieldInput = existingFieldMetadata.isCustom === false @@ -485,7 +492,7 @@ export class FieldMetadataService extends TypeOrmQueryService(fieldMetadataInput: T): T { + >(fieldMetadataInput: T, objectMetadata: ObjectMetadataEntity): T { if (fieldMetadataInput.name) { try { - validateMetadataNameOrThrow(fieldMetadataInput.name); + validateFieldNameValidityOrThrow(fieldMetadataInput.name); + validateFieldNameAvailabilityOrThrow( + fieldMetadataInput.name, + objectMetadata, + ); } catch (error) { if (error instanceof InvalidStringException) { throw new FieldMetadataException( @@ -688,6 +699,11 @@ export class FieldMetadataService extends TypeOrmQueryService { const validateNameCharactersOrThrow = (name?: string) => { try { if (name) { - validateMetadataNameOrThrow(name); + validateMetadataNameValidityOrThrow(name); } } catch (error) { if (error instanceof InvalidStringException) { diff --git a/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts index 4e01a8a0ebaf..a08006adbf87 100644 --- a/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts @@ -18,10 +18,9 @@ import { RelationMetadataException, RelationMetadataExceptionCode, } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.exception'; -import { - InvalidStringException, - validateMetadataNameOrThrow, -} from 'src/engine/metadata-modules/utils/validate-metadata-name.utils'; +import { InvalidStringException } from 'src/engine/metadata-modules/utils/exceptions/invalid-string.exception'; +import { validateFieldNameAvailabilityOrThrow } from 'src/engine/metadata-modules/utils/validate-field-name-availability.utils'; +import { validateMetadataNameValidityOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name-validity.utils'; import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.service'; import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util'; import { @@ -63,8 +62,8 @@ export class RelationMetadataService extends TypeOrmQueryService { + const objectMetadata = objectMetadataItemMock; + + it('does not throw if name is not reserved', () => { + const name = 'testName'; + + expect(() => + validateFieldNameAvailabilityOrThrow(name, objectMetadata), + ).not.toThrow(); + }); + + describe('error cases', () => { + it('throws error with LINKS suffixes', () => { + const name = `${FIELD_LINKS_MOCK_NAME}PrimaryLinkLabel`; + + expect(() => + validateFieldNameAvailabilityOrThrow(name, objectMetadata), + ).toThrow(NameNotAvailableException); + }); + it('throws error with CURRENCY suffixes', () => { + const name = `${FIELD_CURRENCY_MOCK_NAME}AmountMicros`; + + expect(() => + validateFieldNameAvailabilityOrThrow(name, objectMetadata), + ).toThrow(NameNotAvailableException); + }); + it('throws error with FULL_NAME suffixes', () => { + const name = `${FIELD_FULL_NAME_MOCK_NAME}FirstName`; + + expect(() => + validateFieldNameAvailabilityOrThrow(name, objectMetadata), + ).toThrow(NameNotAvailableException); + }); + it('throws error with ACTOR suffixes', () => { + const name = `${FIELD_ACTOR_MOCK_NAME}Name`; + + expect(() => + validateFieldNameAvailabilityOrThrow(name, objectMetadata), + ).toThrow(NameNotAvailableException); + }); + it('throws error with ADDRESS suffixes', () => { + const name = `${FIELD_ADDRESS_MOCK_NAME}AddressStreet1`; + + expect(() => + validateFieldNameAvailabilityOrThrow(name, objectMetadata), + ).toThrow(NameNotAvailableException); + }); + }); +}); diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/__tests__/validate-metadata-name.spec.ts b/packages/twenty-server/src/engine/metadata-modules/utils/__tests__/validate-metadata-name-validity.spec.ts similarity index 51% rename from packages/twenty-server/src/engine/metadata-modules/utils/__tests__/validate-metadata-name.spec.ts rename to packages/twenty-server/src/engine/metadata-modules/utils/__tests__/validate-metadata-name-validity.spec.ts index 445620dd4236..6cd9bd2d6478 100644 --- a/packages/twenty-server/src/engine/metadata-modules/utils/__tests__/validate-metadata-name.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/utils/__tests__/validate-metadata-name-validity.spec.ts @@ -1,26 +1,24 @@ -import { - validateMetadataNameOrThrow, - InvalidStringException, - NameTooLongException, -} from 'src/engine/metadata-modules/utils/validate-metadata-name.utils'; +import { InvalidStringException } from 'src/engine/metadata-modules/utils/exceptions/invalid-string.exception'; +import { NameTooLongException } from 'src/engine/metadata-modules/utils/exceptions/name-too-long.exception'; +import { validateMetadataNameValidityOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name-validity.utils'; -describe('validateMetadataNameOrThrow', () => { +describe('validateMetadataNameValidityOrThrow', () => { it('does not throw if string is valid', () => { const input = 'testName'; - expect(validateMetadataNameOrThrow(input)).not.toThrow; + expect(validateMetadataNameValidityOrThrow(input)).not.toThrow; }); it('throws error if string has spaces', () => { const input = 'name with spaces'; - expect(() => validateMetadataNameOrThrow(input)).toThrow( + expect(() => validateMetadataNameValidityOrThrow(input)).toThrow( InvalidStringException, ); }); it('throws error if string starts with capital letter', () => { const input = 'StringStartingWithCapitalLetter'; - expect(() => validateMetadataNameOrThrow(input)).toThrow( + expect(() => validateMetadataNameValidityOrThrow(input)).toThrow( InvalidStringException, ); }); @@ -28,7 +26,7 @@ describe('validateMetadataNameOrThrow', () => { it('throws error if string has non latin characters', () => { const input = 'בְרִבְרִ'; - expect(() => validateMetadataNameOrThrow(input)).toThrow( + expect(() => validateMetadataNameValidityOrThrow(input)).toThrow( InvalidStringException, ); }); @@ -36,7 +34,7 @@ describe('validateMetadataNameOrThrow', () => { it('throws error if starts with digits', () => { const input = '123string'; - expect(() => validateMetadataNameOrThrow(input)).toThrow( + expect(() => validateMetadataNameValidityOrThrow(input)).toThrow( InvalidStringException, ); }); @@ -44,14 +42,15 @@ describe('validateMetadataNameOrThrow', () => { const inputWith63Characters = 'thisIsAstringWithSixtyThreeCharacters11111111111111111111111111'; - expect(validateMetadataNameOrThrow(inputWith63Characters)).not.toThrow; + expect(validateMetadataNameValidityOrThrow(inputWith63Characters)).not + .toThrow; }); it('throws error if string is above 63 characters', () => { const inputWith64Characters = 'thisIsAstringWithSixtyFourCharacters1111111111111111111111111111'; - expect(() => validateMetadataNameOrThrow(inputWith64Characters)).toThrow( - NameTooLongException, - ); + expect(() => + validateMetadataNameValidityOrThrow(inputWith64Characters), + ).toThrow(NameTooLongException); }); }); diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-string.exception.ts b/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-string.exception.ts new file mode 100644 index 000000000000..0404de70b53e --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-string.exception.ts @@ -0,0 +1,7 @@ +export class InvalidStringException extends Error { + constructor(string: string) { + const message = `String "${string}" is not valid`; + + super(message); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/name-not-available.exception.ts b/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/name-not-available.exception.ts new file mode 100644 index 000000000000..308fc6ddd84f --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/name-not-available.exception.ts @@ -0,0 +1,7 @@ +export class NameNotAvailableException extends Error { + constructor(name: string) { + const message = `Name "${name}" is not available`; + + super(message); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/name-too-long.exception.ts b/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/name-too-long.exception.ts new file mode 100644 index 000000000000..0c9cf45f6342 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/name-too-long.exception.ts @@ -0,0 +1,7 @@ +export class NameTooLongException extends Error { + constructor(string: string) { + const message = `String "${string}" exceeds 63 characters limit`; + + super(message); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/validate-field-name-availability.utils.ts b/packages/twenty-server/src/engine/metadata-modules/utils/validate-field-name-availability.utils.ts new file mode 100644 index 000000000000..07db05aacfbd --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/utils/validate-field-name-availability.utils.ts @@ -0,0 +1,38 @@ +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; +import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; +import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { NameNotAvailableException } from 'src/engine/metadata-modules/utils/exceptions/name-not-available.exception'; + +const getReservedCompositeFieldNames = ( + objectMetadata: ObjectMetadataEntity, +) => { + const reservedCompositeFieldsNames: string[] = []; + + for (const field of objectMetadata.fields) { + if (isCompositeFieldMetadataType(field.type)) { + const base = field.name; + const compositeType = compositeTypeDefinitions.get(field.type); + + compositeType?.properties.map((property) => + reservedCompositeFieldsNames.push( + computeCompositeColumnName(base, property), + ), + ); + } + } + + return reservedCompositeFieldsNames; +}; + +export const validateFieldNameAvailabilityOrThrow = ( + name: string, + objectMetadata: ObjectMetadataEntity, +) => { + const reservedCompositeFieldsNames = + getReservedCompositeFieldNames(objectMetadata); + + if (reservedCompositeFieldsNames.includes(name)) { + throw new NameNotAvailableException(name); + } +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-validity.utils.ts b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-validity.utils.ts new file mode 100644 index 000000000000..42cb2fc70945 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-validity.utils.ts @@ -0,0 +1,14 @@ +import { InvalidStringException } from 'src/engine/metadata-modules/utils/exceptions/invalid-string.exception'; +import { NameTooLongException } from 'src/engine/metadata-modules/utils/exceptions/name-too-long.exception'; +import { exceedsDatabaseIdentifierMaximumLength } from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils'; + +const VALID_STRING_PATTERN = /^[a-z][a-zA-Z0-9]*$/; + +export const validateMetadataNameValidityOrThrow = (name: string) => { + if (!name.match(VALID_STRING_PATTERN)) { + throw new InvalidStringException(name); + } + if (exceedsDatabaseIdentifierMaximumLength(name)) { + throw new NameTooLongException(name); + } +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name.utils.ts b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name.utils.ts deleted file mode 100644 index 72835eead45e..000000000000 --- a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name.utils.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { exceedsDatabaseIdentifierMaximumLength } from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils'; - -const VALID_STRING_PATTERN = /^[a-z][a-zA-Z0-9]*$/; - -export const validateMetadataNameOrThrow = (name: string) => { - if (!name.match(VALID_STRING_PATTERN)) { - throw new InvalidStringException(name); - } - if (exceedsDatabaseIdentifierMaximumLength(name)) { - throw new NameTooLongException(name); - } -}; - -export class InvalidStringException extends Error { - constructor(string: string) { - const message = `String "${string}" is not valid`; - - super(message); - } -} - -export class NameTooLongException extends Error { - constructor(string: string) { - const message = `String "${string}" exceeds 63 characters limit`; - - super(message); - } -} diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts index 827092daaff7..2a6ed45b0fcd 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts @@ -2,17 +2,17 @@ import { Injectable, Logger } from '@nestjs/common'; import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; +import { serializeDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-default-value'; +import { ColumnActionAbstractFactory } from 'src/engine/metadata-modules/workspace-migration/factories/column-action-abstract.factory'; +import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util'; import { WorkspaceMigrationColumnActionType, WorkspaceMigrationColumnAlter, WorkspaceMigrationColumnCreate, } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; -import { serializeDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-default-value'; -import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util'; -import { ColumnActionAbstractFactory } from 'src/engine/metadata-modules/workspace-migration/factories/column-action-abstract.factory'; -import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { WorkspaceMigrationException, WorkspaceMigrationExceptionCode, @@ -32,7 +32,7 @@ export class CompositeColumnActionFactory extends ColumnActionAbstractFactory, ): WorkspaceMigrationColumnCreate[] { - const compositeType = compositeTypeDefintions.get(fieldMetadata.type); + const compositeType = compositeTypeDefinitions.get(fieldMetadata.type); if (!compositeType) { this.logger.error( @@ -80,10 +80,10 @@ export class CompositeColumnActionFactory extends ColumnActionAbstractFactory, alteredFieldMetadata: FieldMetadataInterface, ): WorkspaceMigrationColumnAlter[] { - const currentCompositeType = compositeTypeDefintions.get( + const currentCompositeType = compositeTypeDefinitions.get( currentFieldMetadata.type, ); - const alteredCompositeType = compositeTypeDefintions.get( + const alteredCompositeType = compositeTypeDefinitions.get( alteredFieldMetadata.type, ); diff --git a/packages/twenty-server/src/engine/twenty-orm/factories/entity-schema-column.factory.ts b/packages/twenty-server/src/engine/twenty-orm/factories/entity-schema-column.factory.ts index 251e3058d778..6b4564efaaea 100644 --- a/packages/twenty-server/src/engine/twenty-orm/factories/entity-schema-column.factory.ts +++ b/packages/twenty-server/src/engine/twenty-orm/factories/entity-schema-column.factory.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { ColumnType, EntitySchemaColumnOptions } from 'typeorm'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { FieldMetadataEntity, FieldMetadataType, @@ -105,11 +105,11 @@ export class EntitySchemaColumnFactory { fieldMetadata: FieldMetadataEntity, ): EntitySchemaColumnMap { const entitySchemaColumnMap: EntitySchemaColumnMap = {}; - const compositeType = compositeTypeDefintions.get(fieldMetadata.type); + const compositeType = compositeTypeDefinitions.get(fieldMetadata.type); if (!compositeType) { throw new Error( - `Composite type ${fieldMetadata.type} is not defined in compositeTypeDefintions`, + `Composite type ${fieldMetadata.type} is not defined in compositeTypeDefinitions`, ); } diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts index 3adb6e21dc97..6a436ced71aa 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts @@ -24,7 +24,7 @@ import { UpsertOptions } from 'typeorm/repository/UpsertOptions'; import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; @@ -697,7 +697,7 @@ export class WorkspaceRepository< continue; } - const compositeType = compositeTypeDefintions.get(fieldMetadata.type); + const compositeType = compositeTypeDefinitions.get(fieldMetadata.type); if (!compositeType) { continue; @@ -751,7 +751,7 @@ export class WorkspaceRepository< const compositeFieldMetadataMap = new Map( compositeFieldMetadataCollection.flatMap((fieldMetadata) => { - const compositeType = compositeTypeDefintions.get(fieldMetadata.type); + const compositeType = compositeTypeDefinitions.get(fieldMetadata.type); if (!compositeType) return []; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/database-structure.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/database-structure.service.ts index c4fab96efe34..82dd9fdbd258 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/database-structure.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/database-structure.service.ts @@ -3,28 +3,28 @@ import { Injectable } from '@nestjs/common'; import { ColumnType } from 'typeorm'; import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata'; -import { - WorkspaceTableStructure, - WorkspaceTableStructureResult, -} from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-table-definition.interface'; import { FieldMetadataDefaultValue, FieldMetadataFunctionDefaultValue, } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; +import { + WorkspaceTableStructure, + WorkspaceTableStructureResult, +} from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-table-definition.interface'; import { TypeORMService } from 'src/database/typeorm/typeorm.service'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; +import { FieldMetadataDefaultValueFunctionNames } from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input'; import { FieldMetadataEntity, FieldMetadataType, } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util'; -import { serializeFunctionDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-function-default-value.util'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; -import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util'; import { isFunctionDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/is-function-default-value.util'; -import { FieldMetadataDefaultValueFunctionNames } from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; +import { serializeFunctionDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-function-default-value.util'; +import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util'; import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; +import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util'; @Injectable() export class DatabaseStructureService { @@ -192,7 +192,7 @@ export class DatabaseStructureService { }; if (isCompositeFieldMetadataType(fieldMetadata.type)) { - const compositeType = compositeTypeDefintions.get(fieldMetadata.type); + const compositeType = compositeTypeDefinitions.get(fieldMetadata.type); if (!compositeType) { throw new Error( @@ -310,7 +310,7 @@ export class DatabaseStructureService { }; if (isCompositeFieldMetadataType(fieldMetadataType)) { - const compositeType = compositeTypeDefintions.get(fieldMetadataType); + const compositeType = compositeTypeDefinitions.get(fieldMetadataType); if (!compositeType) { throw new Error( diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/field-metadata-health.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/field-metadata-health.service.ts index 28652c50b6ed..994b91ec70ba 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/field-metadata-health.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/field-metadata-health.service.ts @@ -1,34 +1,34 @@ import { Injectable } from '@nestjs/common'; +import { FieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; import { WorkspaceHealthIssue, WorkspaceHealthIssueType, } from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-health-issue.interface'; -import { WorkspaceTableStructure } from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-table-definition.interface'; import { WorkspaceHealthOptions } from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-health-options.interface'; -import { FieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; +import { WorkspaceTableStructure } from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-table-definition.interface'; +import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; import { FieldMetadataEntity, FieldMetadataType, } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { + computeColumnName, + computeCompositeColumnName, +} from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; -import { DatabaseStructureService } from 'src/engine/workspace-manager/workspace-health/services/database-structure.service'; -import { validName } from 'src/engine/workspace-manager/workspace-health/utils/valid-name.util'; -import { validateDefaultValueForType } from 'src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util'; import { EnumFieldMetadataUnionType, isEnumFieldMetadataType, } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util'; -import { validateOptionsForType } from 'src/engine/metadata-modules/field-metadata/utils/validate-options-for-type.util'; import { serializeDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-default-value'; +import { validateDefaultValueForType } from 'src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util'; +import { validateOptionsForType } from 'src/engine/metadata-modules/field-metadata/utils/validate-options-for-type.util'; import { customNamePrefix } from 'src/engine/utils/compute-table-name.util'; import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util'; -import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types'; -import { - computeColumnName, - computeCompositeColumnName, -} from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; +import { DatabaseStructureService } from 'src/engine/workspace-manager/workspace-health/services/database-structure.service'; +import { validName } from 'src/engine/workspace-manager/workspace-health/utils/valid-name.util'; @Injectable() export class FieldMetadataHealthService { @@ -101,7 +101,7 @@ export class FieldMetadataHealthService { let columnNames: string[] = []; if (isCompositeFieldMetadataType(fieldMetadata.type)) { - const compositeType = compositeTypeDefintions.get(fieldMetadata.type); + const compositeType = compositeTypeDefinitions.get(fieldMetadata.type); if (!compositeType) { throw new Error(`Composite type ${fieldMetadata.type} is not defined`);