Morph many to one picker (#14155)

This PR follows the multiSelect PR merged previously. It will enable
morph relation Many to One to be handled from the table, using a
singleSelect picker

Main point : I decided to change the singleSelect API to take an array
of **objectMetadataName** instead of only one to deal with both our
usecases.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Guillim
2025-10-07 17:25:11 +02:00
committed by GitHub
parent 9f9c2d85ca
commit f60817a1e1
138 changed files with 2116 additions and 1763 deletions
@@ -0,0 +1,53 @@
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField';
import { gql } from '@apollo/client';
import { type ObjectPermissions } from 'twenty-shared/types';
import { capitalize } from 'twenty-shared/utils';
export const generateUpdateOneRecordMutation = ({
objectMetadataItem,
objectMetadataItems,
recordGqlFields,
computeReferences,
objectPermissionsByObjectMetadataId,
}: {
objectMetadataItem: ObjectMetadataItem;
objectMetadataItems: ObjectMetadataItem[];
recordGqlFields?: RecordGqlOperationGqlRecordFields;
computeReferences: boolean;
objectPermissionsByObjectMetadataId: Record<
string,
ObjectPermissions & { objectMetadataId: string }
>;
}) => {
const appliedRecordGqlFields =
recordGqlFields ??
generateDepthOneRecordGqlFields({
objectMetadataItem,
});
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
const mutationResponseField = getUpdateOneRecordMutationResponseField(
objectMetadataItem.nameSingular,
);
const updateOneRecordMutation = gql`
mutation UpdateOne${capitalizedObjectName}($idToUpdate: UUID!, $input: ${capitalizedObjectName}UpdateInput!){
${mutationResponseField}(id: $idToUpdate, data: $input) ${mapObjectMetadataToGraphQLQuery(
{
objectMetadataItems,
objectMetadataItem,
computeReferences,
recordGqlFields: appliedRecordGqlFields,
objectPermissionsByObjectMetadataId,
},
)}
}
`;
return updateOneRecordMutation;
};
@@ -6,7 +6,7 @@ import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObject
import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
import { isNonCompositeField } from '@/object-record/object-filter-dropdown/utils/isNonCompositeField';
import { type ObjectPermissions } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils';
import { type FieldMetadataItem } from '../types/FieldMetadataItem';
type MapFieldMetadataToGraphQLQueryArgs = {
@@ -49,7 +49,14 @@ export const mapFieldMetadataToGraphQLQuery = ({
) {
let gqlMorphField = '';
for (const morphRelation of fieldMetadata.morphRelations ?? []) {
const relationFieldName = morphRelation.sourceFieldMetadata.name;
const relationFieldName = computeMorphRelationFieldName({
fieldName: fieldMetadata.name,
relationType: fieldMetadata.settings?.relationType,
targetObjectMetadataNameSingular:
morphRelation.targetObjectMetadata.nameSingular,
targetObjectMetadataNamePlural:
morphRelation.targetObjectMetadata.namePlural,
});
const relationMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.id === morphRelation.targetObjectMetadata.id,
@@ -80,6 +87,10 @@ export const mapFieldMetadataToGraphQLQuery = ({
}
if (fieldMetadata.settings?.relationType === RelationType.ONE_TO_MANY) {
if (gqlField !== relationFieldName) {
continue;
}
gqlMorphField += `${relationFieldName}
{
edges {
@@ -102,6 +113,10 @@ export const mapFieldMetadataToGraphQLQuery = ({
continue;
}
if (gqlField !== relationFieldName) {
continue;
}
gqlMorphField += `${relationFieldName}
${mapObjectMetadataToGraphQLQuery({
objectMetadataItems,
@@ -5,7 +5,7 @@ import { shouldFieldBeQueried } from '@/object-metadata/utils/shouldFieldBeQueri
import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
import { isRecordGqlFieldsNode } from '@/object-record/graphql/utils/isRecordGraphlFieldsNode';
import { FieldMetadataType, type ObjectPermissions } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils';
type MapObjectMetadataToGraphQLQueryArgs = {
objectMetadataItems: ObjectMetadataItem[];
@@ -71,7 +71,14 @@ export const mapObjectMetadataToGraphQLQuery = ({
}
return fieldMetadata.morphRelations.map((morphRelation) => ({
gqlField: morphRelation.sourceFieldMetadata.name,
gqlField: computeMorphRelationFieldName({
fieldName: fieldMetadata.name,
relationType: morphRelation.type,
targetObjectMetadataNameSingular:
morphRelation.targetObjectMetadata.nameSingular,
targetObjectMetadataNamePlural:
morphRelation.targetObjectMetadata.namePlural,
}),
fieldMetadata: fieldMetadata,
}));
});
@@ -99,19 +106,30 @@ export const mapObjectMetadataToGraphQLQuery = ({
}
return fieldMetadata.morphRelations.map((morphRelation) => ({
gqlField: morphRelation.sourceFieldMetadata.name,
gqlField: computeMorphRelationFieldName({
fieldName: fieldMetadata.name,
relationType: morphRelation.type,
targetObjectMetadataNameSingular:
morphRelation.targetObjectMetadata.nameSingular,
targetObjectMetadataNamePlural:
morphRelation.targetObjectMetadata.namePlural,
}),
fieldMetadata,
}));
});
const gqlFieldWithFieldMetadataThatCouldBeQueried = [
const gqlFieldsWithFieldMetadata = [
...activeReadableFields,
...manyToOneRelationGqlFieldWithFieldMetadata,
].sort((gqlFieldWithFieldMetadataA, gqlFieldWithFieldMetadataB) =>
gqlFieldWithFieldMetadataA.gqlField.localeCompare(
gqlFieldWithFieldMetadataB.gqlField,
),
);
];
const gqlFieldWithFieldMetadataThatCouldBeQueried =
gqlFieldsWithFieldMetadata.sort(
(gqlFieldWithFieldMetadataA, gqlFieldWithFieldMetadataB) =>
gqlFieldWithFieldMetadataA.gqlField.localeCompare(
gqlFieldWithFieldMetadataB.gqlField,
),
);
const gqlFieldWithFieldMetadataThatSouldBeQueried =
gqlFieldWithFieldMetadataThatCouldBeQueried.filter(
@@ -128,7 +146,6 @@ export const mapObjectMetadataToGraphQLQuery = ({
__ref
}`;
}
const mappedFields = gqlFieldWithFieldMetadataThatSouldBeQueried
.map((gqlFieldWithFieldMetadata) => {
const currentRecordGqlFields =