Refactor SingleRecordPicker to handle morphs (#14956)

## Scope

- Refactor SingleRecordPicker to handle morphItem and match Multiple one
- fix query generation to take morph into account


Left: 
- double check SingleRecordPicker in Workflows / Form
- Fix tests / stories
This commit is contained in:
Charles Bochet
2025-10-07 20:07:27 +02:00
committed by GitHub
parent 8829eb5a4a
commit 84f1559832
28 changed files with 460 additions and 585 deletions
@@ -1,3 +1,4 @@
import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
import {
type GenerateDepthOneRecordGqlFields,
generateDepthOneRecordGqlFields,
@@ -11,13 +12,13 @@ type ComputeDepthOneRecordGqlFieldsFromRecordArgs =
export const computeDepthOneRecordGqlFieldsFromRecord = ({
objectMetadataItem,
record,
}: ComputeDepthOneRecordGqlFieldsFromRecordArgs) => {
}: ComputeDepthOneRecordGqlFieldsFromRecordArgs): RecordGqlFields => {
const depthOneRecordGqlFields = generateDepthOneRecordGqlFields({
objectMetadataItem,
});
const recordKeys = Object.keys(record);
return Object.keys(depthOneRecordGqlFields).reduce<Record<string, boolean>>(
return Object.keys(depthOneRecordGqlFields).reduce<RecordGqlFields>(
(acc, key) => {
return {
...acc,
@@ -1,6 +1,8 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { isDefined } from 'twenty-shared/utils';
import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
import { FieldMetadataType, RelationType } from 'twenty-shared/types';
import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils';
export type GenerateDepthOneRecordGqlFields = {
objectMetadataItem: ObjectMetadataItem;
@@ -8,28 +10,64 @@ export type GenerateDepthOneRecordGqlFields = {
export const generateDepthOneRecordGqlFields = ({
objectMetadataItem,
}: GenerateDepthOneRecordGqlFields) =>
objectMetadataItem.readableFields.reduce<Record<string, true>>(
(acc, field) => {
return {
...acc,
...(isDefined(field.settings?.joinColumnName)
? {
[field.settings.joinColumnName]: true,
}
: {}),
[field.name]:
// TODO: Remove once we have made the workflows lighter
(objectMetadataItem.nameSingular ===
CoreObjectNameSingular.Workflow ||
objectMetadataItem.nameSingular ===
CoreObjectNameSingular.WorkflowVersion ||
objectMetadataItem.nameSingular ===
CoreObjectNameSingular.WorkflowRun) &&
(field.name === 'versions' || field.name === 'runs')
? { id: true, name: true }
: true,
};
}: GenerateDepthOneRecordGqlFields) => {
const gqlFields = objectMetadataItem.readableFields.flatMap(
(fieldMetadata) => {
const isManyToOneMorphRelation =
fieldMetadata.type === FieldMetadataType.MORPH_RELATION &&
fieldMetadata.settings.relationType === RelationType.MANY_TO_ONE;
const isManyToOneRelation =
fieldMetadata.type === FieldMetadataType.RELATION &&
fieldMetadata.settings.relationType === RelationType.MANY_TO_ONE;
if (isManyToOneRelation) {
return [`${fieldMetadata.name}Id`, `${fieldMetadata.name}`];
}
if (isManyToOneMorphRelation) {
if (!isDefined(fieldMetadata.morphRelations)) {
throw new Error(
`Field ${fieldMetadata.name} is missing, please refresh the page. If the problem persists, please contact support.`,
);
}
const morphGqlFields = fieldMetadata.morphRelations.map(
(morphRelation) => ({
gqlField: computeMorphRelationFieldName({
fieldName: fieldMetadata.name,
relationType: morphRelation.type,
targetObjectMetadataNameSingular:
morphRelation.targetObjectMetadata.nameSingular,
targetObjectMetadataNamePlural:
morphRelation.targetObjectMetadata.namePlural,
}),
fieldMetadata,
}),
);
return morphGqlFields.flatMap((morphGqlField) => [
morphGqlField.gqlField,
`${morphGqlField.gqlField}Id`,
]);
}
return [fieldMetadata.name];
},
{},
);
return gqlFields.reduce<RecordGqlFields>((acc, field) => {
return {
...acc,
[field]:
// TODO: Remove once we have made the workflows lighter
(objectMetadataItem.nameSingular === CoreObjectNameSingular.Workflow ||
objectMetadataItem.nameSingular ===
CoreObjectNameSingular.WorkflowVersion ||
objectMetadataItem.nameSingular ===
CoreObjectNameSingular.WorkflowRun) &&
(field === 'versions' || field === 'runs')
? { id: true, name: true }
: true,
};
}, {});
};