feat: populate relation join column (#10212)

Fix
https://github.com/twentyhq/core-team-issues/issues/241#issue-2793030259
This commit is contained in:
Jérémy M
2025-02-25 11:24:05 +01:00
committed by GitHub
parent dde70ee3b0
commit a1eea40cf7
49 changed files with 677 additions and 496 deletions

View File

@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import { FieldMetadataType } from 'twenty-shared';
import {
DataSource,
FindOptionsRelations,
@ -22,7 +23,7 @@ import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/typ
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { isRelationFieldMetadata } from 'src/engine/utils/is-relation-field-metadata.util';
import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
@Injectable()
export class ProcessNestedRelationsV2Helper {
@ -96,7 +97,9 @@ export class ProcessNestedRelationsV2Helper {
const sourceFieldMetadata =
parentObjectMetadataItem.fieldsByName[sourceFieldName];
if (!isRelationFieldMetadata(sourceFieldMetadata)) {
if (
!isFieldMetadataOfType(sourceFieldMetadata, FieldMetadataType.RELATION)
) {
// TODO: Maybe we should throw an error here ?
return;
}

View File

@ -1,6 +1,6 @@
import { Injectable, Logger } from '@nestjs/common';
import { isDefined } from 'twenty-shared';
import { FieldMetadataType, isDefined } from 'twenty-shared';
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
@ -21,7 +21,7 @@ import { CompositeInputTypeDefinitionFactory } from 'src/engine/api/graphql/work
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { FileService } from 'src/engine/core-modules/file/services/file.service';
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
import { isRelationFieldMetadata } from 'src/engine/utils/is-relation-field-metadata.util';
import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
// TODO: find a way to prevent conflict between handlers executing logic on object relations
// And this factory that is also executing logic on object relations
@ -151,7 +151,9 @@ export class QueryResultGettersFactory {
objectMetadataMapItem.fieldsByName[recordFieldName],
)
.filter(isDefined)
.filter((fieldMetadata) => isRelationFieldMetadata(fieldMetadata));
.filter((fieldMetadata) =>
isFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION),
);
const relationFieldsProcessedMap = {} as Record<
string,

View File

@ -5,6 +5,7 @@ import {
GraphQLFieldConfigMap,
GraphQLObjectType,
} from 'graphql';
import { FieldMetadataType } from 'twenty-shared';
import { WorkspaceBuildSchemaOptions } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
@ -14,7 +15,7 @@ import { RelationTypeV2Factory } from 'src/engine/api/graphql/workspace-schema-b
import { TypeDefinitionsStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/type-definitions.storage';
import { getResolverArgs } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util';
import { objectContainsRelationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/object-contains-relation-field';
import { isRelationFieldMetadata } from 'src/engine/utils/is-relation-field-metadata.util';
import { isFieldMetadataOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
import { ArgsFactory } from './args.factory';
@ -107,7 +108,7 @@ export class ExtendObjectTypeDefinitionV2Factory {
for (const fieldMetadata of objectMetadata.fields) {
// Ignore non-relation fields as they are already defined
if (!isRelationFieldMetadata(fieldMetadata)) {
if (!isFieldMetadataOfType(fieldMetadata, FieldMetadataType.RELATION)) {
continue;
}

View File

@ -45,7 +45,7 @@ export interface TypeOptions<T = any> {
isArray?: boolean;
arrayDepth?: number;
defaultValue?: T;
settings?: FieldMetadataSettings<FieldMetadataType | 'default'>;
settings?: FieldMetadataSettings<FieldMetadataType>;
isIdField?: boolean;
}
@ -55,7 +55,7 @@ const StringArrayScalarType = new GraphQLList(GraphQLString);
export class TypeMapperService {
mapToScalarType(
fieldMetadataType: FieldMetadataType,
settings?: FieldMetadataSettings<FieldMetadataType | 'default'>,
settings?: FieldMetadataSettings<FieldMetadataType>,
isIdField?: boolean,
): GraphQLScalarType | undefined {
if (isIdField || settings?.isForeignKey) {
@ -90,7 +90,7 @@ export class TypeMapperService {
mapToFilterType(
fieldMetadataType: FieldMetadataType,
settings?: FieldMetadataSettings<FieldMetadataType | 'default'>,
settings?: FieldMetadataSettings<FieldMetadataType>,
isIdField?: boolean,
): GraphQLInputObjectType | GraphQLScalarType | undefined {
if (isIdField || settings?.isForeignKey) {