fix(front): resolve labelIdentifier per target for morph relation depth=1 (#20305)
## Summary
On the show page, morph relations were showing "Untitled" entries for
targets whose `labelIdentifier` is not `name` (for example
`Note.title`). The GraphQL response only contained `id` for those
records.
`generateDepthRecordGqlFieldsFromFields` was hardcoding the morph
depth=1 sub-selection to `{ id, name }` for every target instead of
resolving each target's `labelIdentifier` (and `imageIdentifier`) from
`objectMetadataItems`, the way the non-morph relation branch already
does. The morph branch was also ignoring
`shouldOnlyLoadRelationIdentifiers`.
<img width="1300" height="860" alt="image"
src="https://github.com/user-attachments/assets/ebdb5287-0b4c-4a96-95a2-33b19b31446e"
/>
This commit is contained in:
+21
-69
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`generateActivityTargetGqlFields snapshot tests should match snapshot for Note with loadRelations="activity" 1`] = `
|
||||
{
|
||||
@@ -24,13 +24,13 @@ exports[`generateActivityTargetGqlFields snapshot tests should match snapshot fo
|
||||
"position": true,
|
||||
"searchVector": true,
|
||||
"targetCompany": {
|
||||
"domainName": true,
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetCompanyId": true,
|
||||
"targetEmploymentHistory": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetEmploymentHistoryId": true,
|
||||
"targetOpportunity": {
|
||||
@@ -39,6 +39,7 @@ exports[`generateActivityTargetGqlFields snapshot tests should match snapshot fo
|
||||
},
|
||||
"targetOpportunityId": true,
|
||||
"targetPerson": {
|
||||
"avatarFile": true,
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
@@ -49,7 +50,6 @@ exports[`generateActivityTargetGqlFields snapshot tests should match snapshot fo
|
||||
},
|
||||
"targetPetCareAgreement": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetPetCareAgreementId": true,
|
||||
"targetPetId": true,
|
||||
@@ -76,45 +76,21 @@ exports[`generateActivityTargetGqlFields snapshot tests should match snapshot fo
|
||||
"id": true,
|
||||
"position": true,
|
||||
"searchVector": true,
|
||||
"targetCompany": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetCompany": true,
|
||||
"targetCompanyId": true,
|
||||
"targetEmploymentHistory": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetEmploymentHistory": true,
|
||||
"targetEmploymentHistoryId": true,
|
||||
"targetOpportunity": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetOpportunity": true,
|
||||
"targetOpportunityId": true,
|
||||
"targetPerson": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetPerson": true,
|
||||
"targetPersonId": true,
|
||||
"targetPet": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetPetCareAgreement": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetPet": true,
|
||||
"targetPetCareAgreement": true,
|
||||
"targetPetCareAgreementId": true,
|
||||
"targetPetId": true,
|
||||
"targetRocket": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetRocket": true,
|
||||
"targetRocketId": true,
|
||||
"targetSurveyResult": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetSurveyResult": true,
|
||||
"targetSurveyResultId": true,
|
||||
"updatedAt": true,
|
||||
"updatedBy": true,
|
||||
@@ -140,13 +116,13 @@ exports[`generateActivityTargetGqlFields snapshot tests should match snapshot fo
|
||||
"position": true,
|
||||
"searchVector": true,
|
||||
"targetCompany": {
|
||||
"domainName": true,
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetCompanyId": true,
|
||||
"targetEmploymentHistory": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetEmploymentHistoryId": true,
|
||||
"targetOpportunity": {
|
||||
@@ -155,6 +131,7 @@ exports[`generateActivityTargetGqlFields snapshot tests should match snapshot fo
|
||||
},
|
||||
"targetOpportunityId": true,
|
||||
"targetPerson": {
|
||||
"avatarFile": true,
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
@@ -165,7 +142,6 @@ exports[`generateActivityTargetGqlFields snapshot tests should match snapshot fo
|
||||
},
|
||||
"targetPetCareAgreement": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetPetCareAgreementId": true,
|
||||
"targetPetId": true,
|
||||
@@ -197,45 +173,21 @@ exports[`generateActivityTargetGqlFields snapshot tests should match snapshot fo
|
||||
"id": true,
|
||||
"position": true,
|
||||
"searchVector": true,
|
||||
"targetCompany": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetCompany": true,
|
||||
"targetCompanyId": true,
|
||||
"targetEmploymentHistory": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetEmploymentHistory": true,
|
||||
"targetEmploymentHistoryId": true,
|
||||
"targetOpportunity": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetOpportunity": true,
|
||||
"targetOpportunityId": true,
|
||||
"targetPerson": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetPerson": true,
|
||||
"targetPersonId": true,
|
||||
"targetPet": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetPetCareAgreement": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetPet": true,
|
||||
"targetPetCareAgreement": true,
|
||||
"targetPetCareAgreementId": true,
|
||||
"targetPetId": true,
|
||||
"targetRocket": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetRocket": true,
|
||||
"targetRocketId": true,
|
||||
"targetSurveyResult": {
|
||||
"id": true,
|
||||
"name": true,
|
||||
},
|
||||
"targetSurveyResult": true,
|
||||
"targetSurveyResultId": true,
|
||||
"updatedAt": true,
|
||||
"updatedBy": true,
|
||||
|
||||
+39
-30
@@ -6,9 +6,8 @@ import {
|
||||
} from 'twenty-shared/types';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
|
||||
import { getImageIdentifierFieldMetadataItem } from '@/object-metadata/utils/getImageIdentifierFieldMetadataItem';
|
||||
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
|
||||
import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields';
|
||||
import { buildIdentifierGqlFields } from '@/object-record/graphql/record-gql-fields/utils/buildIdentifierGqlFields';
|
||||
import { generateActivityTargetGqlFields } from '@/object-record/graphql/record-gql-fields/utils/generateActivityTargetGqlFields';
|
||||
import { generateJunctionRelationGqlFields } from '@/object-record/graphql/record-gql-fields/utils/generateJunctionRelationGqlFields';
|
||||
import { isJunctionRelationField } from '@/object-record/record-field/ui/utils/junction/isJunctionRelationField';
|
||||
@@ -96,21 +95,9 @@ export const generateDepthRecordGqlFieldsFromFields = ({
|
||||
}
|
||||
}
|
||||
|
||||
const labelIdentifierFieldMetadataItem =
|
||||
getLabelIdentifierFieldMetadataItem(targetObjectMetadataItem);
|
||||
|
||||
const imageIdentifierFieldMetadataItem =
|
||||
getImageIdentifierFieldMetadataItem(targetObjectMetadataItem);
|
||||
|
||||
const relationIdentifierSubGqlFields = {
|
||||
id: true,
|
||||
...(isDefined(labelIdentifierFieldMetadataItem)
|
||||
? { [labelIdentifierFieldMetadataItem.name]: true }
|
||||
: {}),
|
||||
...(isDefined(imageIdentifierFieldMetadataItem)
|
||||
? { [imageIdentifierFieldMetadataItem.name]: true }
|
||||
: {}),
|
||||
};
|
||||
const relationIdentifierSubGqlFields = buildIdentifierGqlFields(
|
||||
targetObjectMetadataItem,
|
||||
);
|
||||
|
||||
const manyToOneGqlFields = {
|
||||
[`${fieldMetadata.name}Id`]: true,
|
||||
@@ -138,17 +125,33 @@ export const generateDepthRecordGqlFieldsFromFields = ({
|
||||
}
|
||||
|
||||
const morphGqlFields = fieldMetadata.morphRelations.map(
|
||||
(morphRelation) => ({
|
||||
gqlField: computeMorphRelationFieldName({
|
||||
fieldName: fieldMetadata.name,
|
||||
relationType: morphRelation.type,
|
||||
targetObjectMetadataNameSingular:
|
||||
morphRelation.targetObjectMetadata.nameSingular,
|
||||
targetObjectMetadataNamePlural:
|
||||
morphRelation.targetObjectMetadata.namePlural,
|
||||
}),
|
||||
fieldMetadata,
|
||||
}),
|
||||
(morphRelation) => {
|
||||
const morphTargetObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id === morphRelation.targetObjectMetadata.id,
|
||||
);
|
||||
|
||||
if (!morphTargetObjectMetadataItem) {
|
||||
throw new Error(
|
||||
`Target object metadata item not found for ${fieldMetadata.name} (morph target ${morphRelation.targetObjectMetadata.nameSingular})`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
gqlField: computeMorphRelationFieldName({
|
||||
fieldName: fieldMetadata.name,
|
||||
relationType: morphRelation.type,
|
||||
targetObjectMetadataNameSingular:
|
||||
morphRelation.targetObjectMetadata.nameSingular,
|
||||
targetObjectMetadataNamePlural:
|
||||
morphRelation.targetObjectMetadata.namePlural,
|
||||
}),
|
||||
fieldMetadata,
|
||||
relationIdentifierSubGqlFields: buildIdentifierGqlFields(
|
||||
morphTargetObjectMetadataItem,
|
||||
),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -156,8 +159,14 @@ export const generateDepthRecordGqlFieldsFromFields = ({
|
||||
...morphGqlFields.reduce(
|
||||
(morphGqlFields, morphGqlField) => ({
|
||||
...morphGqlFields,
|
||||
...(depth === 1
|
||||
? { [`${morphGqlField.gqlField}`]: { id: true, name: true } }
|
||||
...(depth === 1 && shouldOnlyLoadRelationIdentifiers
|
||||
? {
|
||||
[`${morphGqlField.gqlField}`]:
|
||||
morphGqlField.relationIdentifierSubGqlFields,
|
||||
}
|
||||
: {}),
|
||||
...(depth === 1 && !shouldOnlyLoadRelationIdentifiers
|
||||
? { [`${morphGqlField.gqlField}`]: true }
|
||||
: {}),
|
||||
...(relationType === RelationType.MANY_TO_ONE
|
||||
? { [`${morphGqlField.gqlField}Id`]: true }
|
||||
|
||||
Reference in New Issue
Block a user