diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/core-query-builder.factory.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/core-query-builder.factory.ts index 8ff0eff86..73811490c 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/core-query-builder.factory.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/core-query-builder.factory.ts @@ -19,9 +19,12 @@ import { parseCoreBatchPath } from 'src/engine/api/rest/core/query-builder/utils import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils'; import { Query } from 'src/engine/api/rest/core/types/query.type'; import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service'; -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 { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service'; +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 { getObjectMetadataMapItemByNamePlural } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-plural.util'; +import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; +import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; @Injectable() export class CoreQueryBuilderFactory { @@ -38,25 +41,35 @@ export class CoreQueryBuilderFactory { private readonly updateVariablesFactory: UpdateVariablesFactory, private readonly getVariablesFactory: GetVariablesFactory, private readonly findDuplicatesVariablesFactory: FindDuplicatesVariablesFactory, - private readonly objectMetadataService: ObjectMetadataService, private readonly accessTokenService: AccessTokenService, private readonly domainManagerService: DomainManagerService, + private readonly workspaceCacheStorageService: WorkspaceCacheStorageService, ) {} async getObjectMetadata( request: Request, parsedObject: string, ): Promise<{ - objectMetadataItems: ObjectMetadataEntity[]; - objectMetadataItem: ObjectMetadataEntity; + objectMetadataMaps: ObjectMetadataMaps; + objectMetadataMapItem: ObjectMetadataItemWithFieldMaps; }> { const { workspace } = await this.accessTokenService.validateTokenByRequest(request); - const objectMetadataItems = - await this.objectMetadataService.findManyWithinWorkspace(workspace.id); + const currentCacheVersion = + await this.workspaceCacheStorageService.getMetadataVersion(workspace.id); - if (!objectMetadataItems.length) { + if (currentCacheVersion === undefined) { + throw new BadRequestException('No cacheVersion'); + } + + const objectMetadataMaps = + await this.workspaceCacheStorageService.getObjectMetadataMaps( + workspace.id, + currentCacheVersion, + ); + + if (!objectMetadataMaps) { throw new BadRequestException( `No object was found for the workspace associated with this API key. You may generate a new one here ${this.domainManagerService .buildWorkspaceURL({ @@ -67,19 +80,21 @@ export class CoreQueryBuilderFactory { ); } - const [objectMetadata] = objectMetadataItems.filter( - (object) => object.namePlural === parsedObject, + const objectMetadataItem = getObjectMetadataMapItemByNamePlural( + objectMetadataMaps, + parsedObject, ); - if (!objectMetadata) { - const [wrongObjectMetadata] = objectMetadataItems.filter( - (object) => object.nameSingular === parsedObject, + if (!objectMetadataItem) { + const wrongObjectMetadataItem = getObjectMetadataMapItemByNameSingular( + objectMetadataMaps, + parsedObject, ); let hint = 'eg: companies'; - if (wrongObjectMetadata) { - hint = `Did you mean '${wrongObjectMetadata.namePlural}'?`; + if (wrongObjectMetadataItem) { + hint = `Did you mean '${wrongObjectMetadataItem.namePlural}'?`; } throw new BadRequestException( @@ -88,8 +103,8 @@ export class CoreQueryBuilderFactory { } return { - objectMetadataItems, - objectMetadataItem: objectMetadata, + objectMetadataMaps, + objectMetadataMapItem: objectMetadataItem, }; } @@ -101,12 +116,14 @@ export class CoreQueryBuilderFactory { if (!id) { throw new BadRequestException( - `delete ${objectMetadata.objectMetadataItem.nameSingular} query invalid. Id missing. eg: /rest/${objectMetadata.objectMetadataItem.namePlural}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`, + `delete ${objectMetadata.objectMetadataMapItem.nameSingular} query invalid. Id missing. eg: /rest/${objectMetadata.objectMetadataMapItem.namePlural}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`, ); } return { - query: this.deleteQueryFactory.create(objectMetadata.objectMetadataItem), + query: this.deleteQueryFactory.create( + objectMetadata.objectMetadataMapItem, + ), variables: this.deleteVariablesFactory.create(id), }; } @@ -144,7 +161,7 @@ export class CoreQueryBuilderFactory { if (!id) { throw new BadRequestException( - `update ${objectMetadata.objectMetadataItem.nameSingular} query invalid. Id missing. eg: /rest/${objectMetadata.objectMetadataItem.namePlural}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`, + `update ${objectMetadata.objectMetadataMapItem.nameSingular} query invalid. Id missing. eg: /rest/${objectMetadata.objectMetadataMapItem.namePlural}/0d4389ef-ea9c-4ae8-ada1-1cddc440fb56`, ); } diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/core-query-builder.module.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/core-query-builder.module.ts index 7dc9a0b08..7557d506b 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/core-query-builder.module.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/core-query-builder.module.ts @@ -2,12 +2,12 @@ import { Module } from '@nestjs/common'; import { CoreQueryBuilderFactory } from 'src/engine/api/rest/core/query-builder/core-query-builder.factory'; import { coreQueryBuilderFactories } from 'src/engine/api/rest/core/query-builder/factories/factories'; -import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module'; import { AuthModule } from 'src/engine/core-modules/auth/auth.module'; import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/domain-manager.module'; +import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; @Module({ - imports: [ObjectMetadataModule, AuthModule, DomainManagerModule], + imports: [AuthModule, DomainManagerModule, WorkspaceCacheStorageModule], providers: [...coreQueryBuilderFactories, CoreQueryBuilderFactory], exports: [CoreQueryBuilderFactory], }) diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/create-many-query.factory.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/create-many-query.factory.ts index e0a0879cb..a0fa2a6b0 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/create-many-query.factory.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/create-many-query.factory.ts @@ -3,25 +3,33 @@ import { Injectable } from '@nestjs/common'; import { capitalize } from 'twenty-shared'; import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils'; +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'; @Injectable() export class CreateManyQueryFactory { - create(objectMetadata, depth?: number): string { + create( + objectMetadata: { + objectMetadataMaps: ObjectMetadataMaps; + objectMetadataMapItem: ObjectMetadataItemWithFieldMaps; + }, + depth?: number, + ): string { const objectNamePlural = capitalize( - objectMetadata.objectMetadataItem.namePlural, + objectMetadata.objectMetadataMapItem.namePlural, ); const objectNameSingular = capitalize( - objectMetadata.objectMetadataItem.nameSingular, + objectMetadata.objectMetadataMapItem.nameSingular, ); return ` mutation Create${objectNamePlural}($data: [${objectNameSingular}CreateInput!]) { create${objectNamePlural}(data: $data) { id - ${objectMetadata.objectMetadataItem.fields + ${objectMetadata.objectMetadataMapItem.fields .map((field) => mapFieldMetadataToGraphqlQuery( - objectMetadata.objectMetadataItems, + objectMetadata.objectMetadataMaps, field, depth, ), diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/create-one-query.factory.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/create-one-query.factory.ts index da55ed985..2f9ef9b99 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/create-one-query.factory.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/create-one-query.factory.ts @@ -3,22 +3,30 @@ import { Injectable } from '@nestjs/common'; import { capitalize } from 'twenty-shared'; import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils'; +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'; @Injectable() export class CreateOneQueryFactory { - create(objectMetadata, depth?: number): string { + create( + objectMetadata: { + objectMetadataMaps: ObjectMetadataMaps; + objectMetadataMapItem: ObjectMetadataItemWithFieldMaps; + }, + depth?: number, + ): string { const objectNameSingular = capitalize( - objectMetadata.objectMetadataItem.nameSingular, + objectMetadata.objectMetadataMapItem.nameSingular, ); return ` mutation Create${objectNameSingular}($data: ${objectNameSingular}CreateInput!) { create${objectNameSingular}(data: $data) { id - ${objectMetadata.objectMetadataItem.fields + ${objectMetadata.objectMetadataMapItem.fields .map((field) => mapFieldMetadataToGraphqlQuery( - objectMetadata.objectMetadataItems, + objectMetadata.objectMetadataMaps, field, depth, ), diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/delete-query.factory.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/delete-query.factory.ts index 835a7366d..81c9a2dda 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/delete-query.factory.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/delete-query.factory.ts @@ -2,10 +2,12 @@ import { Injectable } from '@nestjs/common'; import { capitalize } from 'twenty-shared'; +import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; + @Injectable() export class DeleteQueryFactory { - create(objectMetadataItem): string { - const objectNameSingular = capitalize(objectMetadataItem.nameSingular); + create(objectMetadataMapItem: ObjectMetadataItemWithFieldMaps): string { + const objectNameSingular = capitalize(objectMetadataMapItem.nameSingular); return ` mutation Delete${objectNameSingular}($id: ID!) { diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-duplicates-query.factory.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-duplicates-query.factory.ts index 939021a7b..412bb4cea 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-duplicates-query.factory.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-duplicates-query.factory.ts @@ -3,11 +3,20 @@ import { Injectable } from '@nestjs/common'; import { capitalize } from 'twenty-shared'; import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils'; +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'; @Injectable() export class FindDuplicatesQueryFactory { - create(objectMetadata, depth?: number): string { - const objectNameSingular = objectMetadata.objectMetadataItem.nameSingular; + create( + objectMetadata: { + objectMetadataMaps: ObjectMetadataMaps; + objectMetadataMapItem: ObjectMetadataItemWithFieldMaps; + }, + depth?: number, + ): string { + const objectNameSingular = + objectMetadata.objectMetadataMapItem.nameSingular; return ` query FindDuplicate${capitalize( @@ -22,10 +31,10 @@ export class FindDuplicatesQueryFactory { } edges{ node { - ${objectMetadata.objectMetadataItem.fields + ${objectMetadata.objectMetadataMapItem.fields .map((field) => mapFieldMetadataToGraphqlQuery( - objectMetadata.objectMetadataItems, + objectMetadata.objectMetadataMaps, field, depth, ), diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-many-query.factory.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-many-query.factory.ts index 1b0507fae..aface1280 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-many-query.factory.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-many-query.factory.ts @@ -3,14 +3,22 @@ import { Injectable } from '@nestjs/common'; import { capitalize } from 'twenty-shared'; import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils'; +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'; @Injectable() export class FindManyQueryFactory { - create(objectMetadata, depth?: number): string { + create( + objectMetadata: { + objectMetadataMaps: ObjectMetadataMaps; + objectMetadataMapItem: ObjectMetadataItemWithFieldMaps; + }, + depth?: number, + ): string { const objectNameSingular = capitalize( - objectMetadata.objectMetadataItem.nameSingular, + objectMetadata.objectMetadataMapItem.nameSingular, ); - const objectNamePlural = objectMetadata.objectMetadataItem.namePlural; + const objectNamePlural = objectMetadata.objectMetadataMapItem.namePlural; return ` query FindMany${capitalize(objectNamePlural)}( @@ -32,10 +40,10 @@ export class FindManyQueryFactory { edges { node { id - ${objectMetadata.objectMetadataItem.fields + ${objectMetadata.objectMetadataMapItem.fields .map((field) => mapFieldMetadataToGraphqlQuery( - objectMetadata.objectMetadataItems, + objectMetadata.objectMetadataMaps, field, depth, ), diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-one-query.factory.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-one-query.factory.ts index 281b40981..cf9e00c93 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-one-query.factory.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/find-one-query.factory.ts @@ -3,11 +3,20 @@ import { Injectable } from '@nestjs/common'; import { capitalize } from 'twenty-shared'; import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils'; +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'; @Injectable() export class FindOneQueryFactory { - create(objectMetadata, depth?: number): string { - const objectNameSingular = objectMetadata.objectMetadataItem.nameSingular; + create( + objectMetadata: { + objectMetadataMaps: ObjectMetadataMaps; + objectMetadataMapItem: ObjectMetadataItemWithFieldMaps; + }, + depth?: number, + ): string { + const objectNameSingular = + objectMetadata.objectMetadataMapItem.nameSingular; return ` query FindOne${capitalize(objectNameSingular)}( @@ -15,10 +24,10 @@ export class FindOneQueryFactory { ) { ${objectNameSingular}(filter: $filter) { id - ${objectMetadata.objectMetadataItem.fields + ${objectMetadata.objectMetadataMapItem.fields .map((field) => mapFieldMetadataToGraphqlQuery( - objectMetadata.objectMetadataItems, + objectMetadata.objectMetadataMaps, field, depth, ), diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/update-query.factory.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/update-query.factory.ts index 04d9d9007..284dd1a67 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/update-query.factory.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/factories/update-query.factory.ts @@ -3,11 +3,20 @@ import { Injectable } from '@nestjs/common'; import { capitalize } from 'twenty-shared'; import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils'; +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'; @Injectable() export class UpdateQueryFactory { - create(objectMetadata, depth?: number): string { - const objectNameSingular = objectMetadata.objectMetadataItem.nameSingular; + create( + objectMetadata: { + objectMetadataMaps: ObjectMetadataMaps; + objectMetadataMapItem: ObjectMetadataItemWithFieldMaps; + }, + depth?: number, + ): string { + const objectNameSingular = + objectMetadata.objectMetadataMapItem.nameSingular; return ` mutation Update${capitalize( @@ -15,10 +24,10 @@ export class UpdateQueryFactory { )}($id: ID!, $data: ${capitalize(objectNameSingular)}UpdateInput!) { update${capitalize(objectNameSingular)}(id: $id, data: $data) { id - ${objectMetadata.objectMetadataItem.fields + ${Object.values(objectMetadata.objectMetadataMapItem.fieldsById) .map((field) => mapFieldMetadataToGraphqlQuery( - objectMetadata.objectMetadataItems, + objectMetadata.objectMetadataMaps, field, depth, ), 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 55743d1a3..d54571175 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,5 +1,7 @@ import { FieldMetadataType } from 'twenty-shared'; +import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; + import { fieldCurrencyMock, fieldNumberMock, @@ -8,19 +10,85 @@ import { } from 'src/engine/api/__mocks__/object-metadata-item.mock'; import { mapFieldMetadataToGraphqlQuery } from 'src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils'; import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; +import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map'; +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'; describe('mapFieldMetadataToGraphqlQuery', () => { + const typedFieldNumberMock: FieldMetadataInterface = { + id: 'field-number-id', + name: fieldNumberMock.name, + type: fieldNumberMock.type, + label: 'Field Number', + objectMetadataId: 'object-metadata-id', + isNullable: fieldNumberMock.isNullable, + defaultValue: fieldNumberMock.defaultValue, + }; + + const typedFieldTextMock: FieldMetadataInterface = { + id: 'field-text-id', + name: fieldTextMock.name, + type: fieldTextMock.type, + label: 'Field Text', + objectMetadataId: 'object-metadata-id', + isNullable: fieldTextMock.isNullable, + defaultValue: fieldTextMock.defaultValue, + }; + + const typedFieldCurrencyMock: FieldMetadataInterface = { + id: 'field-currency-id', + name: fieldCurrencyMock.name, + type: fieldCurrencyMock.type, + label: 'Field Currency', + objectMetadataId: 'object-metadata-id', + isNullable: fieldCurrencyMock.isNullable, + defaultValue: fieldCurrencyMock.defaultValue, + }; + + const fieldsById: FieldMetadataMap = { + 'field-number-id': typedFieldNumberMock, + 'field-text-id': typedFieldTextMock, + 'field-currency-id': typedFieldCurrencyMock, + }; + + const fieldsByName: FieldMetadataMap = { + [typedFieldNumberMock.name]: typedFieldNumberMock, + [typedFieldTextMock.name]: typedFieldTextMock, + [typedFieldCurrencyMock.name]: typedFieldCurrencyMock, + }; + + const typedObjectMetadataItem: ObjectMetadataItemWithFieldMaps = { + ...objectMetadataItemMock, + fieldsById, + fieldsByName, + }; + + const objectMetadataMapsMock: ObjectMetadataMaps = { + byId: { + [objectMetadataItemMock.id]: typedObjectMetadataItem, + }, + idByNameSingular: { + [objectMetadataItemMock.nameSingular]: objectMetadataItemMock.id, + }, + }; + it('should map properly', () => { expect( - mapFieldMetadataToGraphqlQuery([objectMetadataItemMock], fieldNumberMock), + mapFieldMetadataToGraphqlQuery( + objectMetadataMapsMock, + typedFieldNumberMock, + ), ).toEqual('fieldNumber'); expect( - mapFieldMetadataToGraphqlQuery([objectMetadataItemMock], fieldTextMock), + mapFieldMetadataToGraphqlQuery( + objectMetadataMapsMock, + typedFieldTextMock, + ), ).toEqual('fieldText'); expect( mapFieldMetadataToGraphqlQuery( - [objectMetadataItemMock], - fieldCurrencyMock, + objectMetadataMapsMock, + typedFieldCurrencyMock, ), ).toEqual(` fieldCurrency @@ -30,19 +98,24 @@ describe('mapFieldMetadataToGraphqlQuery', () => { } `); }); + describe('should handle all field metadata types', () => { Object.values(FieldMetadataType).forEach((fieldMetadataType) => { it(`with field type ${fieldMetadataType}`, () => { - const field = { + const field: FieldMetadataInterface = { + id: 'test-field-id', type: fieldMetadataType, name: 'toObjectMetadataName', + label: 'Test Field', + objectMetadataId: 'object-metadata-id', fromRelationMetadata: { relationType: RelationMetadataType.ONE_TO_MANY, - }, + toObjectMetadataId: objectMetadataItemMock.id, + } as any, }; expect( - mapFieldMetadataToGraphqlQuery([objectMetadataItemMock], field), + mapFieldMetadataToGraphqlQuery(objectMetadataMapsMock, field), ).toBeDefined(); }); }); diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts index 885d215c1..39803c5d6 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts @@ -1,13 +1,16 @@ import { FieldMetadataType } from 'twenty-shared'; +import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; + import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; +import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; const DEFAULT_DEPTH_VALUE = 1; // TODO: Should be properly type and based on composite type definitions export const mapFieldMetadataToGraphqlQuery = ( - objectMetadataItems, - field, + objectMetadataMaps: ObjectMetadataMaps, + field: FieldMetadataInterface, maxDepthForRelations = DEFAULT_DEPTH_VALUE, ): string | undefined => { if (maxDepthForRelations < 0) { @@ -41,19 +44,25 @@ export const mapFieldMetadataToGraphqlQuery = ( fieldType === FieldMetadataType.RELATION && field.toRelationMetadata?.relationType === RelationMetadataType.ONE_TO_MANY ) { - const relationMetadataItem = objectMetadataItems.find( - (objectMetadataItem) => - objectMetadataItem.id === - (field.toRelationMetadata as any)?.fromObjectMetadataId, - ); + const fromObjectMetadataId = field.toRelationMetadata?.fromObjectMetadataId; + + if (!fromObjectMetadataId) { + return ''; + } + + const relationMetadataItem = objectMetadataMaps.byId[fromObjectMetadataId]; + + if (!relationMetadataItem) { + return ''; + } return `${field.name} { id - ${(relationMetadataItem?.fields ?? []) + ${Object.values(relationMetadataItem.fieldsById) .map((field) => mapFieldMetadataToGraphqlQuery( - objectMetadataItems, + objectMetadataMaps, field, maxDepthForRelations - 1, ), @@ -66,21 +75,27 @@ export const mapFieldMetadataToGraphqlQuery = ( field.fromRelationMetadata?.relationType === RelationMetadataType.ONE_TO_MANY ) { - const relationMetadataItem = objectMetadataItems.find( - (objectMetadataItem) => - objectMetadataItem.id === - (field.fromRelationMetadata as any)?.toObjectMetadataId, - ); + const toObjectMetadataId = field.fromRelationMetadata?.toObjectMetadataId; + + if (!toObjectMetadataId) { + return ''; + } + + const relationMetadataItem = objectMetadataMaps.byId[toObjectMetadataId]; + + if (!relationMetadataItem) { + return ''; + } return `${field.name} { edges { node { id - ${(relationMetadataItem?.fields ?? []) + ${Object.values(relationMetadataItem.fieldsById) .map((field) => mapFieldMetadataToGraphqlQuery( - objectMetadataItems, + objectMetadataMaps, field, maxDepthForRelations - 1, ), diff --git a/packages/twenty-server/src/engine/api/rest/core/rest-api-core-v2.service.ts b/packages/twenty-server/src/engine/api/rest/core/rest-api-core-v2.service.ts index 58cb5d36b..58236ded8 100644 --- a/packages/twenty-server/src/engine/api/rest/core/rest-api-core-v2.service.ts +++ b/packages/twenty-server/src/engine/api/rest/core/rest-api-core-v2.service.ts @@ -106,7 +106,7 @@ export class RestApiCoreServiceV2 { } const objectMetadataNameSingular = - objectMetadata.objectMetadataItem.nameSingular; + objectMetadata.objectMetadataMapItem.nameSingular; const repository = await this.twentyORMGlobalManager.getRepositoryForWorkspace( workspace.id, diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-plural.util.ts b/packages/twenty-server/src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-plural.util.ts new file mode 100644 index 000000000..f08747fe4 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-plural.util.ts @@ -0,0 +1,13 @@ +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'; + +export const getObjectMetadataMapItemByNamePlural = ( + objectMetadataMaps: ObjectMetadataMaps, + namePlural: string, +): ObjectMetadataItemWithFieldMaps | undefined => { + const objectMetadataItems = Object.values(objectMetadataMaps.byId); + + return objectMetadataItems.find( + (objectMetadata) => objectMetadata.namePlural === namePlural, + ); +};