From 37473175a009805bde8aca44b9fb2a9036282665 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Fri, 10 Oct 2025 14:31:12 +0200 Subject: [PATCH] Reduce relation loading overload on FE graphql queries (#14991) ## Problem With Twenty usage growing, we are facing challenges on server side to respond to the demand. The current bottleneck we are facing is server CPU. While investigating CPU performances, we figured out that loading all relation fields was the biggest issue. ### About graphql query response size **Example:** on `People Index` Table page: we query: `person.company` and we query **all non relation fields** on company relation field. `{ person { company { id, name, domainName, employees, address ... } }` However we only need **company.id, company.name and company.domainName** to be able to render the Company Chip in the company column (RELATION) in the table. `{ person { company { id, name, domainName } }` We initially assumed that the querying all non relation fields was not an issue because it was not adding additional load on database (which is also partially true: if a field is containing a huge json this will add load on postgres as data needs to be transfered) This assumption is wrong on CPU side: - when loading one to many relations (company.people), this quickly ads up and we end up with response of 20kB quite quickly, even without adding any custom field on person. - even when loading a many to one relation, if the user is storing big data in a given field (note.body for instance, or workflowRun.state), this starts also being an issue. - Worst case scenario are starting to happen: on a production workspace with 20 active workflows. Loading workflow table while displaying workflow runs commands (20 workflows x 60 workflowRuns x workflowRun.state which is a big JSON) result in a response of... 125MB. ### Why is it an issue? When yoga (graphql engine) parses a response to send it back in server response, it's using `JSON.parse `(or stringify depending on the case). Parsing data is CPU instensive (as you need to validate, transform) and this is more or less `O(n)`. This means returning 125MB is very intense on the CPU and will likely use 100% of the CPU for ~1sec in our production servers. NodeJs (nodeV8) is single threaded. It's able to process multiple requests in parallel but to do that, it will cut them in "microTasks" and process each microTasks (that can belong to different requests) one after the other. Exactly like a single threaded CPU would allocate some time to a process and then to the next one, etc. This means that this big response request will actually block the other request. As a result, all requests are slow and our infrastructure is multi tenant so some customers are impacting others. This is even a bigger issue when our health checks start to fail and containers are starting being considered as unhealthy and killed by the orchestrator ## How to fix this 1. On Front end side, we should only query what we need. In this PR, I'm forcing the relations to only query: `id`, `labelIdentifier`, `imageIdentifier` 2. (later) as we are API first, we also need to do something to protect the servers from this side too. To do that, Graphql APIs can associate a cost to each graphql field (maybe 1 for a test fixed, 2 for a JSON field, 10 for relations, etc...) and we can throttle based on that. ## Changes in this PR It's mainly about refactor the tooling to generate `RecordGqlFields` (`generateDepthOneGqlFields`). this tooling is used to generate the list of fields we want to query. Most of the time we want to query a record with one level of nesting. 1) Refactor to remove duplicate code => `generateDepthOne` become `generateDepth` and can handle both `depth = 0 | 1` 2) Introduce `generateDepthRecordGqlFieldsFromFields.ts` which is the base and can give you a list of gqlFields based on FieldMetadataItems 3) Introduce `generateDepthRecordGqlFieldsFromObject` which is a shortcut for an object 4) Introduce `generateDepthRecordGqlFieldsFromRecords` which is the intersection between `generateDepthRecordGqlFieldsFromObject` and a given record (useful for cache tooling) 5) Replace all usages + introduce a hook `useGenerateDepthRecordGqlFieldsFromObject` to ease usage --- ...ThreadMessagesOperationSignatureFactory.ts | 71 +- ...ateOneActivityOperationSignatureFactory.ts | 73 +- ...findActivitiesOperationSignatureFactory.ts | 137 +-- ...ctivityTargetsOperationSignatureFactory.ts | 39 +- .../hooks/__tests__/useCompleteTask.test.tsx | 154 +--- .../hooks/useTimelineActivities.ts | 13 +- .../favorites/hooks/__mocks__/useFavorites.ts | 802 +----------------- .../utils/generateCreateOneRecordMutation.ts | 50 ++ .../utils/generateUpdateOneRecordMutation.ts | 6 +- .../getImageIdentifierFieldMetadataItem.ts | 16 + .../utils/isImageIdentifierField.ts | 25 + .../utils/mapFieldMetadataToGraphQLQuery.ts | 2 +- .../utils/mapObjectMetadataToGraphQLQuery.ts | 2 +- .../cache/hooks/useCreateOneRecordInCache.ts | 11 +- .../cache/hooks/useGetRecordFromCache.ts | 20 +- .../cache/utils/getRecordFromCache.ts | 11 +- .../cache/utils/updateRecordFromCache.ts | 2 +- ...eGenerateDepthRecordGqlFieldsFromObject.ts | 30 + .../types/RecordGqlFields.ts | 0 ...epthRecordGqlFieldsFromObject.test.ts.snap | 81 ++ ...rateDepthRecordGqlFieldsFromObject.test.ts | 25 + .../generateDepthRecordGqlFieldsFromFields.ts | 128 +++ .../generateDepthRecordGqlFieldsFromObject.ts | 23 + .../generateDepthRecordGqlFieldsFromRecord.ts | 34 + .../RecordGqlOperationSignatureFactory.ts | 4 +- ...eDepthOneRecordGqlFieldsFromRecord.test.ts | 49 -- .../generateDepthOneRecordGqlFields.test.ts | 44 - ...OneWithoutRelationsRecordGqlFields.test.ts | 34 - ...omputeDepthOneRecordGqlFieldsFromRecord.ts | 30 - .../utils/generateDepthOneRecordGqlFields.ts | 73 -- ...DepthOneWithoutRelationsRecordGqlFields.ts | 23 - .../graphql/utils/isRecordGraphlFieldsNode.ts | 2 +- .../hooks/__mocks__/personFragments.ts | 153 ---- .../useDeleteOneRecord.test.tsx.snap | 303 ------- .../__tests__/useDeleteManyRecords.test.tsx | 10 +- .../useRecordIndexTableQuery.test.tsx | 439 ---------- .../hooks/useAggregateRecordsQuery.ts | 2 +- .../hooks/useCreateManyRecords.ts | 10 +- .../object-record/hooks/useCreateOneRecord.ts | 12 +- .../hooks/useCreateOneRecordMutation.ts | 34 +- .../object-record/hooks/useFindOneRecord.ts | 11 +- .../hooks/useLazyFindOneRecord.ts | 17 +- .../hooks/useMergeManyRecords.ts | 11 +- .../hooks/useMergeManyRecordsMutation.ts | 13 +- ...eRecordOneToManyFieldAttachTargetRecord.ts | 6 +- .../object-record/hooks/useUpdateOneRecord.ts | 21 +- .../hooks/useUpdateOneRecordV2.ts | 20 +- .../useCombinedFindManyRecords.test.tsx | 2 +- ...useGenerateCombinedFindManyRecordsQuery.ts | 6 +- .../usePerformCombinedFindManyRecords.ts | 11 +- ...binedFindManyRecordsQueryVariables.test.ts | 2 +- .../RecordIndexCalendarDataLoaderEffect.tsx | 13 +- .../hooks/useRecordFieldGqlFields.ts | 102 --- .../hooks/useRecordsFieldVisibleGqlFields.ts | 86 ++ .../__tests__/useToggleEditOnlyInput.test.tsx | 291 +------ .../hooks/useLoadRecordIndexBoardColumn.ts | 13 +- .../hooks/useRecordBoardRecordGqlFields.ts | 77 -- .../hooks/useRecordIndexTableFetchMore.ts | 11 +- .../hooks/useRecordIndexTableQuery.ts | 15 +- ...ordForShowPageOperationSignatureFactory.ts | 92 +- ...penObjectRecordsSpreadsheetImportDialog.ts | 11 +- .../computeOptimisticRecordFromInput.test.ts | 10 +- .../utils/generateAggregateQuery.ts | 2 +- .../PrefetchRunFavoriteQueriesEffect.tsx | 17 +- .../prefetch/constants/PrefetchConfig.ts | 22 - ...avoritesFolderOperationSignatureFactory.ts | 2 +- ...ndAllFavoritesOperationSignatureFactory.ts | 33 +- .../findAllViewsOperationSignatureFactory.ts | 30 - ...ttingsAccountsMessageChannelsContainer.tsx | 16 +- .../components/ListenRecordUpdatesEffect.tsx | 7 +- .../workflow/hooks/useRunWorkflowVersion.tsx | 11 +- .../settings/accounts/SettingsAccounts.tsx | 9 +- .../cache/inMemoryTestingCacheInstance.ts | 6 +- .../jest/generateEmptyJestRecordNode.ts | 6 +- 74 files changed, 1024 insertions(+), 2955 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-metadata/utils/generateCreateOneRecordMutation.ts create mode 100644 packages/twenty-front/src/modules/object-metadata/utils/getImageIdentifierFieldMetadataItem.ts create mode 100644 packages/twenty-front/src/modules/object-metadata/utils/isImageIdentifierField.ts create mode 100644 packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject.ts rename packages/twenty-front/src/modules/object-record/graphql/{ => record-gql-fields}/types/RecordGqlFields.ts (100%) create mode 100644 packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/__tests__/__snapshots__/generateDepthRecordGqlFieldsFromObject.test.ts.snap create mode 100644 packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/__tests__/generateDepthRecordGqlFieldsFromObject.test.ts create mode 100644 packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromFields.ts create mode 100644 packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject.ts create mode 100644 packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord.ts delete mode 100644 packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/computeDepthOneRecordGqlFieldsFromRecord.test.ts delete mode 100644 packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneRecordGqlFields.test.ts delete mode 100644 packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneWithoutRelationsRecordGqlFields.test.ts delete mode 100644 packages/twenty-front/src/modules/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord.ts delete mode 100644 packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneRecordGqlFields.ts delete mode 100644 packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-field/hooks/useRecordFieldGqlFields.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts delete mode 100644 packages/twenty-front/src/modules/prefetch/constants/PrefetchConfig.ts delete mode 100644 packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllViewsOperationSignatureFactory.ts diff --git a/packages/twenty-front/src/modules/activities/emails/graphql/operation-signatures/factories/fetchAllThreadMessagesOperationSignatureFactory.ts b/packages/twenty-front/src/modules/activities/emails/graphql/operation-signatures/factories/fetchAllThreadMessagesOperationSignatureFactory.ts index 3609f83b4c..3fc4d5a430 100644 --- a/packages/twenty-front/src/modules/activities/emails/graphql/operation-signatures/factories/fetchAllThreadMessagesOperationSignatureFactory.ts +++ b/packages/twenty-front/src/modules/activities/emails/graphql/operation-signatures/factories/fetchAllThreadMessagesOperationSignatureFactory.ts @@ -2,40 +2,45 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi import { type RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory'; import { QUERY_MAX_RECORDS } from 'twenty-shared/constants'; -export const fetchAllThreadMessagesOperationSignatureFactory: RecordGqlOperationSignatureFactory = - ({ messageThreadId }: { messageThreadId: string }) => ({ - objectNameSingular: CoreObjectNameSingular.Message, - variables: { - filter: { - messageThreadId: { - eq: messageThreadId || '', - }, +type FetchAllThreadMessagesOperationSignatureFactory = { + messageThreadId: string | null; +}; + +export const fetchAllThreadMessagesOperationSignatureFactory: RecordGqlOperationSignatureFactory< + FetchAllThreadMessagesOperationSignatureFactory +> = ({ messageThreadId }: FetchAllThreadMessagesOperationSignatureFactory) => ({ + objectNameSingular: CoreObjectNameSingular.Message, + variables: { + filter: { + messageThreadId: { + eq: messageThreadId || '', }, - orderBy: [ - { - receivedAt: 'AscNullsLast', - }, - ], - limit: QUERY_MAX_RECORDS, }, - fields: { + orderBy: [ + { + receivedAt: 'AscNullsLast', + }, + ], + limit: QUERY_MAX_RECORDS, + }, + fields: { + id: true, + __typename: true, + createdAt: true, + headerMessageId: true, + subject: true, + text: true, + receivedAt: true, + messageThread: { id: true, - __typename: true, - createdAt: true, - headerMessageId: true, - subject: true, - text: true, - receivedAt: true, - messageThread: { - id: true, - }, - messageParticipants: { - id: true, - role: true, - displayName: true, - handle: true, - person: true, - workspaceMember: true, - }, }, - }); + messageParticipants: { + id: true, + role: true, + displayName: true, + handle: true, + person: true, + workspaceMember: true, + }, + }, +}); diff --git a/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/createOneActivityOperationSignatureFactory.ts b/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/createOneActivityOperationSignatureFactory.ts index 401dbcac81..dc700a9fc3 100644 --- a/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/createOneActivityOperationSignatureFactory.ts +++ b/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/createOneActivityOperationSignatureFactory.ts @@ -1,40 +1,45 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { type RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory'; -export const createOneActivityOperationSignatureFactory: RecordGqlOperationSignatureFactory = - ({ objectNameSingular }: { objectNameSingular: CoreObjectNameSingular }) => - objectNameSingular === CoreObjectNameSingular.Note - ? { - objectNameSingular: CoreObjectNameSingular.Note, - variables: {}, - fields: { +type CreateOneActivityOperationSignatureFactory = { + objectNameSingular: CoreObjectNameSingular; +}; + +export const createOneActivityOperationSignatureFactory: RecordGqlOperationSignatureFactory< + CreateOneActivityOperationSignatureFactory +> = ({ objectNameSingular }: CreateOneActivityOperationSignatureFactory) => + objectNameSingular === CoreObjectNameSingular.Note + ? { + objectNameSingular: CoreObjectNameSingular.Note, + variables: {}, + fields: { + id: true, + __typename: true, + createdAt: true, + updatedAt: true, + attachments: true, + bodyV2: true, + title: true, + }, + } + : { + objectNameSingular: CoreObjectNameSingular.Task, + variables: {}, + fields: { + id: true, + __typename: true, + createdAt: true, + updatedAt: true, + assigneeId: true, + assignee: { id: true, + name: true, __typename: true, - createdAt: true, - updatedAt: true, - attachments: true, - bodyV2: true, - title: true, }, - } - : { - objectNameSingular: CoreObjectNameSingular.Task, - variables: {}, - fields: { - id: true, - __typename: true, - createdAt: true, - updatedAt: true, - assigneeId: true, - assignee: { - id: true, - name: true, - __typename: true, - }, - attachments: true, - bodyV2: true, - title: true, - status: true, - dueAt: true, - }, - }; + attachments: true, + bodyV2: true, + title: true, + status: true, + dueAt: true, + }, + }; diff --git a/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/findActivitiesOperationSignatureFactory.ts b/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/findActivitiesOperationSignatureFactory.ts index 870b0aeaf9..d57555d2f6 100644 --- a/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/findActivitiesOperationSignatureFactory.ts +++ b/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/findActivitiesOperationSignatureFactory.ts @@ -3,72 +3,75 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { type RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory'; -export const findActivitiesOperationSignatureFactory: RecordGqlOperationSignatureFactory = - ({ - objectMetadataItems, - objectNameSingular, - }: { - objectMetadataItems: ObjectMetadataItem[]; - objectNameSingular: CoreObjectNameSingular; - }) => { - const body = { - bodyV2: { - markdown: true, - blocknote: true, - }, - }; +type FindActivitiesOperationSignatureFactory = { + objectMetadataItems: ObjectMetadataItem[]; + objectNameSingular: CoreObjectNameSingular; +}; - return { - objectNameSingular: objectNameSingular, - variables: {}, - fields: { - id: true, - __typename: true, - createdAt: true, - updatedAt: true, - author: { - id: true, - name: true, - __typename: true, - }, - authorId: true, - assigneeId: true, - assignee: { - id: true, - name: true, - __typename: true, - }, - comments: true, - attachments: true, - ...body, - title: true, - status: true, - dueAt: true, - reminderAt: true, - type: true, - ...(objectNameSingular === CoreObjectNameSingular.Note - ? { - noteTargets: { - id: true, - __typename: true, - createdAt: true, - updatedAt: true, - note: true, - noteId: true, - ...generateActivityTargetMorphFieldKeys(objectMetadataItems), - }, - } - : { - taskTargets: { - id: true, - __typename: true, - createdAt: true, - updatedAt: true, - task: true, - taskId: true, - ...generateActivityTargetMorphFieldKeys(objectMetadataItems), - }, - }), - }, - }; +export const findActivitiesOperationSignatureFactory: RecordGqlOperationSignatureFactory< + FindActivitiesOperationSignatureFactory +> = ({ + objectMetadataItems, + objectNameSingular, +}: FindActivitiesOperationSignatureFactory) => { + const body = { + bodyV2: { + markdown: true, + blocknote: true, + }, }; + + return { + objectNameSingular: objectNameSingular, + variables: {}, + fields: { + id: true, + __typename: true, + createdAt: true, + updatedAt: true, + author: { + id: true, + name: true, + __typename: true, + }, + authorId: true, + assigneeId: true, + assignee: { + id: true, + name: true, + __typename: true, + }, + comments: true, + attachments: true, + ...body, + title: true, + status: true, + dueAt: true, + reminderAt: true, + type: true, + ...(objectNameSingular === CoreObjectNameSingular.Note + ? { + noteTargets: { + id: true, + __typename: true, + createdAt: true, + updatedAt: true, + note: true, + noteId: true, + ...generateActivityTargetMorphFieldKeys(objectMetadataItems), + }, + } + : { + taskTargets: { + id: true, + __typename: true, + createdAt: true, + updatedAt: true, + task: true, + taskId: true, + ...generateActivityTargetMorphFieldKeys(objectMetadataItems), + }, + }), + }, + }; +}; diff --git a/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/findActivityTargetsOperationSignatureFactory.ts b/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/findActivityTargetsOperationSignatureFactory.ts index 82d7f3c511..a854b4794d 100644 --- a/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/findActivityTargetsOperationSignatureFactory.ts +++ b/packages/twenty-front/src/modules/activities/graphql/operation-signatures/factories/findActivityTargetsOperationSignatureFactory.ts @@ -4,21 +4,24 @@ import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectN import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { type RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory'; -export const findActivityTargetsOperationSignatureFactory: RecordGqlOperationSignatureFactory = - ({ - objectNameSingular, - objectMetadataItems, - }: { - objectNameSingular: CoreObjectNameSingular; - objectMetadataItems: ObjectMetadataItem[]; - }) => ({ - objectNameSingular: getJoinObjectNameSingular(objectNameSingular), - variables: {}, - fields: { - id: true, - __typename: true, - createdAt: true, - updatedAt: true, - ...generateActivityTargetMorphFieldKeys(objectMetadataItems), - }, - }); +type FindActivityTargetsOperationSignatureFactory = { + objectNameSingular: CoreObjectNameSingular; + objectMetadataItems: ObjectMetadataItem[]; +}; + +export const findActivityTargetsOperationSignatureFactory: RecordGqlOperationSignatureFactory< + FindActivityTargetsOperationSignatureFactory +> = ({ + objectNameSingular, + objectMetadataItems, +}: FindActivityTargetsOperationSignatureFactory) => ({ + objectNameSingular: getJoinObjectNameSingular(objectNameSingular), + variables: {}, + fields: { + id: true, + __typename: true, + createdAt: true, + updatedAt: true, + ...generateActivityTargetMorphFieldKeys(objectMetadataItems), + }, +}); diff --git a/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.tsx b/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.tsx index ac60b994b1..6a9e860a66 100644 --- a/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.tsx +++ b/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.tsx @@ -1,10 +1,12 @@ import { type MockedResponse } from '@apollo/client/testing'; import { act, renderHook } from '@testing-library/react'; -import gql from 'graphql-tag'; import { useCompleteTask } from '@/activities/tasks/hooks/useCompleteTask'; import { type Task } from '@/activities/types/Task'; +import { generateUpdateOneRecordMutation } from '@/object-metadata/utils/generateUpdateOneRecordMutation'; import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; +import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; +import { getMockObjectMetadataItemOrThrow } from '~/testing/utils/getMockObjectMetadataItemOrThrow'; const task: Task = { id: '123', @@ -23,151 +25,17 @@ const task: Task = { __typename: 'Task', }; +const updateOneTaskMutation = generateUpdateOneRecordMutation({ + objectMetadataItem: getMockObjectMetadataItemOrThrow('task'), + objectMetadataItems: generatedMockObjectMetadataItems, + computeReferences: false, + objectPermissionsByObjectMetadataId: {}, +}); + const mocks: MockedResponse[] = [ { request: { - query: gql` - mutation UpdateOneTask($idToUpdate: UUID!, $input: TaskUpdateInput!) { - updateTask(id: $idToUpdate, data: $input) { - __typename - assignee { - __typename - avatarUrl - colorScheme - createdAt - dateFormat - deletedAt - id - locale - name { - firstName - lastName - } - position - timeFormat - timeZone - updatedAt - userEmail - userId - } - assigneeId - attachments { - edges { - node { - __typename - authorId - companyId - createdAt - deletedAt - fullPath - id - name - noteId - opportunityId - personId - petId - rocketId - surveyResultId - taskId - type - updatedAt - } - } - } - bodyV2 { - blocknote - markdown - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - dueAt - favorites { - edges { - node { - __typename - companyId - createdAt - deletedAt - favoriteFolderId - forWorkspaceMemberId - id - noteId - opportunityId - personId - petId - position - rocketId - surveyResultId - taskId - updatedAt - viewId - workflowId - workflowRunId - workflowVersionId - } - } - } - id - position - status - taskTargets { - edges { - node { - __typename - companyId - createdAt - deletedAt - id - opportunityId - personId - petId - rocketId - surveyResultId - taskId - updatedAt - } - } - } - timelineActivities { - edges { - node { - __typename - companyId - createdAt - deletedAt - happensAt - id - linkedObjectMetadataId - linkedRecordCachedName - linkedRecordId - name - noteId - opportunityId - personId - petId - properties - rocketId - surveyResultId - taskId - updatedAt - workflowId - workflowRunId - workflowVersionId - workspaceMemberId - } - } - } - title - updatedAt - } - } - `, + query: updateOneTaskMutation, variables: { idToUpdate: task.id, input: { status: task.status }, diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts b/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts index 43c16432f3..e58c2ab8a6 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts +++ b/packages/twenty-front/src/modules/activities/timeline-activities/hooks/useTimelineActivities.ts @@ -2,9 +2,8 @@ import { useLinkedObjectsTitle } from '@/activities/timeline-activities/hooks/us import { type TimelineActivity } from '@/activities/timeline-activities/types/TimelineActivity'; import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getActivityTargetObjectFieldIdName'; -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { isDefined } from 'twenty-shared/utils'; @@ -16,9 +15,11 @@ export const useTimelineActivities = ( nameSingular: targetableObject.targetObjectNameSingular, }); - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular: CoreObjectNameSingular.TimelineActivity, - }); + const { recordGqlFields: depthOneRecordGqlFields } = + useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular: CoreObjectNameSingular.TimelineActivity, + depth: 1, + }); const { records: timelineActivities, @@ -36,7 +37,7 @@ export const useTimelineActivities = ( createdAt: 'DescNullsFirst', }, ], - recordGqlFields: generateDepthOneRecordGqlFields({ objectMetadataItem }), + recordGqlFields: depthOneRecordGqlFields, fetchPolicy: 'cache-and-network', }); diff --git a/packages/twenty-front/src/modules/favorites/hooks/__mocks__/useFavorites.ts b/packages/twenty-front/src/modules/favorites/hooks/__mocks__/useFavorites.ts index 9409bd04e5..a181744f32 100644 --- a/packages/twenty-front/src/modules/favorites/hooks/__mocks__/useFavorites.ts +++ b/packages/twenty-front/src/modules/favorites/hooks/__mocks__/useFavorites.ts @@ -1,8 +1,12 @@ import { gql } from '@apollo/client'; import { Favorite } from '@/favorites/types/Favorite'; +import { generateCreateOneRecordMutation } from '@/object-metadata/utils/generateCreateOneRecordMutation'; +import { generateUpdateOneRecordMutation } from '@/object-metadata/utils/generateUpdateOneRecordMutation'; import { ColorScheme } from '@/workspace-member/types/WorkspaceMember'; import { AvatarType } from 'twenty-ui/display'; +import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; +import { getMockObjectMetadataItemOrThrow } from '~/testing/utils/getMockObjectMetadataItemOrThrow'; export const mockId = '8f3b2121-f194-4ba4-9fbf-2d5a37126806'; export const favoriteId = 'f088c8c9-05d2-4276-b065-b863cc7d0b33'; @@ -101,788 +105,30 @@ export const sortedFavorites = [ }, ]; -const UPDATE_ONE_FAVORITE_MUTATION = gql` - mutation UpdateOneFavorite($idToUpdate: UUID!, $input: FavoriteUpdateInput!) { - updateFavorite(id: $idToUpdate, data: $input) { - __typename - company { - __typename - accountOwnerId - address { - addressStreet1 - addressStreet2 - addressCity - addressState - addressCountry - addressPostcode - addressLat - addressLng - } - annualRecurringRevenue { - amountMicros - currencyCode - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - domainName { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - employees - id - idealCustomerProfile - introVideo { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - name - position - tagline - updatedAt - visaSponsorship - workPolicy - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - } - companyId - createdAt - deletedAt - favoriteFolder { - __typename - createdAt - deletedAt - id - name - position - updatedAt - } - favoriteFolderId - forWorkspaceMember { - __typename - avatarUrl - colorScheme - createdAt - dateFormat - deletedAt - id - locale - name { - firstName - lastName - } - position - timeFormat - timeZone - updatedAt - userEmail - userId - } - forWorkspaceMemberId - id - note { - __typename - bodyV2 { - blocknote - markdown - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - position - title - updatedAt - } - noteId - opportunity { - __typename - amount { - amountMicros - currencyCode - } - closeDate - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - name - pointOfContactId - position - stage - updatedAt - } - opportunityId - person { - __typename - avatarUrl - city - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - emails { - primaryEmail - additionalEmails - } - id - intro - jobTitle - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - name { - firstName - lastName - } - performanceRating - phones { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - position - updatedAt - whatsapp { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - workPreference - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - } - personId - pet { - __typename - age - averageCostOfKibblePerMonth { - amountMicros - currencyCode - } - bio - birthday - comments - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - extraData - id - interestingFacts - isGoodWithKids - location { - addressStreet1 - addressStreet2 - addressCity - addressState - addressCountry - addressPostcode - addressLat - addressLng - } - makesOwnerThinkOf { - firstName - lastName - } - name - pictures { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - position - soundSwag - species - traits - updatedAt - vetEmail { - primaryEmail - additionalEmails - } - vetPhone { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - } - petId - position - rocket { - __typename - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - name - position - updatedAt - } - rocketId - surveyResult { - __typename - averageEstimatedNumberOfAtomsInTheUniverse - comments - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - name - participants - percentageOfCompletion - position - score - shortNotes - updatedAt - } - surveyResultId - task { - __typename - assigneeId - bodyV2 { - blocknote - markdown - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - dueAt - id - position - status - title - updatedAt - } - taskId - updatedAt - view { - __typename - createdAt - deletedAt - icon - id - isCompact - kanbanAggregateOperation - kanbanAggregateOperationFieldMetadataId - kanbanFieldMetadataId - key - name - objectMetadataId - openRecordIn - position - type - updatedAt - } - viewId - workflow { - __typename - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - lastPublishedVersionId - name - position - statuses - updatedAt - } - workflowId - workflowRun { - __typename - context - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - endedAt - id - name - output - position - startedAt - status - updatedAt - workflowId - workflowVersionId - } - workflowRunId - workflowVersion { - __typename - createdAt - deletedAt - id - name - position - status - steps - trigger - updatedAt - workflowId - } - workflowVersionId - } - } -`; +const favoriteObjectMetadataItem = getMockObjectMetadataItemOrThrow('favorite') + +const UPDATE_ONE_FAVORITE_MUTATION = generateUpdateOneRecordMutation( + { + objectMetadataItem: favoriteObjectMetadataItem, + objectMetadataItems: generatedMockObjectMetadataItems, + computeReferences: false, + objectPermissionsByObjectMetadataId: {}, + }, +) + +const CREATE_ONE_FAVORITE_MUTATION = generateCreateOneRecordMutation( + { + objectMetadataItem: favoriteObjectMetadataItem, + objectMetadataItems: generatedMockObjectMetadataItems, + objectPermissionsByObjectMetadataId: {}, + }, +) + export const mocks = [ { request: { - query: gql` - mutation CreateOneFavorite($input: FavoriteCreateInput!) { - createFavorite(data: $input) { - __typename - company { - __typename - accountOwnerId - address { - addressStreet1 - addressStreet2 - addressCity - addressState - addressCountry - addressPostcode - addressLat - addressLng - } - annualRecurringRevenue { - amountMicros - currencyCode - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - domainName { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - employees - id - idealCustomerProfile - introVideo { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - name - position - tagline - updatedAt - visaSponsorship - workPolicy - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - } - companyId - createdAt - deletedAt - favoriteFolder { - __typename - createdAt - deletedAt - id - name - position - updatedAt - } - favoriteFolderId - forWorkspaceMember { - __typename - avatarUrl - colorScheme - createdAt - dateFormat - deletedAt - id - locale - name { - firstName - lastName - } - position - timeFormat - timeZone - updatedAt - userEmail - userId - } - forWorkspaceMemberId - id - note { - __typename - bodyV2 { - blocknote - markdown - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - position - title - updatedAt - } - noteId - opportunity { - __typename - amount { - amountMicros - currencyCode - } - closeDate - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - name - pointOfContactId - position - stage - updatedAt - } - opportunityId - person { - __typename - avatarUrl - city - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - emails { - primaryEmail - additionalEmails - } - id - intro - jobTitle - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - name { - firstName - lastName - } - performanceRating - phones { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - position - updatedAt - whatsapp { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - workPreference - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - } - personId - pet { - __typename - age - averageCostOfKibblePerMonth { - amountMicros - currencyCode - } - bio - birthday - comments - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - extraData - id - interestingFacts - isGoodWithKids - location { - addressStreet1 - addressStreet2 - addressCity - addressState - addressCountry - addressPostcode - addressLat - addressLng - } - makesOwnerThinkOf { - firstName - lastName - } - name - pictures { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - position - soundSwag - species - traits - updatedAt - vetEmail { - primaryEmail - additionalEmails - } - vetPhone { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - } - petId - position - rocket { - __typename - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - name - position - updatedAt - } - rocketId - surveyResult { - __typename - averageEstimatedNumberOfAtomsInTheUniverse - comments - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - name - participants - percentageOfCompletion - position - score - shortNotes - updatedAt - } - surveyResultId - task { - __typename - assigneeId - bodyV2 { - blocknote - markdown - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - dueAt - id - position - status - title - updatedAt - } - taskId - updatedAt - view { - __typename - createdAt - deletedAt - icon - id - isCompact - kanbanAggregateOperation - kanbanAggregateOperationFieldMetadataId - kanbanFieldMetadataId - key - name - objectMetadataId - openRecordIn - position - type - updatedAt - } - viewId - workflow { - __typename - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - lastPublishedVersionId - name - position - statuses - updatedAt - } - workflowId - workflowRun { - __typename - context - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - endedAt - id - name - output - position - startedAt - status - updatedAt - workflowId - workflowVersionId - } - workflowRunId - workflowVersion { - __typename - createdAt - deletedAt - id - name - position - status - steps - trigger - updatedAt - workflowId - } - workflowVersionId - } - } - `, + query: CREATE_ONE_FAVORITE_MUTATION, variables: { input: { personId: favoriteTargetObjectId, diff --git a/packages/twenty-front/src/modules/object-metadata/utils/generateCreateOneRecordMutation.ts b/packages/twenty-front/src/modules/object-metadata/utils/generateCreateOneRecordMutation.ts new file mode 100644 index 0000000000..c5a706f36c --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/generateCreateOneRecordMutation.ts @@ -0,0 +1,50 @@ +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; +import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; +import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField'; +import { gql } from '@apollo/client'; +import { type ObjectPermissions } from 'twenty-shared/types'; +import { capitalize } from 'twenty-shared/utils'; + +export const generateCreateOneRecordMutation = ({ + objectMetadataItem, + objectMetadataItems, + recordGqlFields, + objectPermissionsByObjectMetadataId, +}: { + objectMetadataItem: ObjectMetadataItem; + objectMetadataItems: ObjectMetadataItem[]; + recordGqlFields?: RecordGqlOperationGqlRecordFields; + objectPermissionsByObjectMetadataId: Record< + string, + ObjectPermissions & { objectMetadataId: string } + >; +}) => { + const appliedRecordGqlFields = + recordGqlFields ?? + generateDepthRecordGqlFieldsFromObject({ + depth: 1, + objectMetadataItems, + objectMetadataItem, + }); + + const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular); + + const mutationResponseField = getCreateOneRecordMutationResponseField( + objectMetadataItem.nameSingular, + ); + + const createOneRecordMutation = gql` + mutation CreateOne${capitalizedObjectName}($input: ${capitalizedObjectName}CreateInput!) { + ${mutationResponseField}(data: $input) ${mapObjectMetadataToGraphQLQuery({ + objectMetadataItems, + objectMetadataItem, + recordGqlFields: appliedRecordGqlFields, + objectPermissionsByObjectMetadataId, + })} + } + `; + + return createOneRecordMutation; +}; diff --git a/packages/twenty-front/src/modules/object-metadata/utils/generateUpdateOneRecordMutation.ts b/packages/twenty-front/src/modules/object-metadata/utils/generateUpdateOneRecordMutation.ts index 5d30f08b5c..2d7bf20407 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/generateUpdateOneRecordMutation.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/generateUpdateOneRecordMutation.ts @@ -1,7 +1,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; 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'; @@ -25,7 +25,9 @@ export const generateUpdateOneRecordMutation = ({ }) => { const appliedRecordGqlFields = recordGqlFields ?? - generateDepthOneRecordGqlFields({ + generateDepthRecordGqlFieldsFromObject({ + depth: 1, + objectMetadataItems, objectMetadataItem, }); diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getImageIdentifierFieldMetadataItem.ts b/packages/twenty-front/src/modules/object-metadata/utils/getImageIdentifierFieldMetadataItem.ts new file mode 100644 index 0000000000..82a4c53093 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/getImageIdentifierFieldMetadataItem.ts @@ -0,0 +1,16 @@ +import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { isImageIdentifierField } from '@/object-metadata/utils/isImageIdentifierField'; + +export const getImageIdentifierFieldMetadataItem = ( + objectMetadataItem: Pick< + ObjectMetadataItem, + 'fields' | 'imageIdentifierFieldMetadataId' | 'nameSingular' + >, +): FieldMetadataItem | undefined => + objectMetadataItem.fields.find((fieldMetadataItem) => + isImageIdentifierField({ + fieldMetadataItem, + objectMetadataItem, + }), + ); diff --git a/packages/twenty-front/src/modules/object-metadata/utils/isImageIdentifierField.ts b/packages/twenty-front/src/modules/object-metadata/utils/isImageIdentifierField.ts new file mode 100644 index 0000000000..3c6ef4ee19 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/isImageIdentifierField.ts @@ -0,0 +1,25 @@ +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; + +export const isImageIdentifierField = ({ + fieldMetadataItem, + objectMetadataItem, +}: { + fieldMetadataItem: Pick; + objectMetadataItem: Pick< + ObjectMetadataItem, + 'imageIdentifierFieldMetadataId' | 'nameSingular' + >; +}) => { + if ( + objectMetadataItem.nameSingular === CoreObjectNameSingular.Company && + fieldMetadataItem.name === 'domainName' + ) { + return true; + } + + return ( + fieldMetadataItem.id === objectMetadataItem.imageIdentifierFieldMetadataId + ); +}; diff --git a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts index 811469a4f6..abbe91b3b9 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts @@ -3,7 +3,7 @@ import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject'; -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; import { isNonCompositeField } from '@/object-record/object-filter-dropdown/utils/isNonCompositeField'; import { type ObjectPermissions } from 'twenty-shared/types'; import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils'; diff --git a/packages/twenty-front/src/modules/object-metadata/utils/mapObjectMetadataToGraphQLQuery.ts b/packages/twenty-front/src/modules/object-metadata/utils/mapObjectMetadataToGraphQLQuery.ts index fecc481050..fd7bf14d69 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/mapObjectMetadataToGraphQLQuery.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/mapObjectMetadataToGraphQLQuery.ts @@ -2,7 +2,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject'; import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery'; import { shouldFieldBeQueried } from '@/object-metadata/utils/shouldFieldBeQueried'; -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; import { isRecordGqlFieldsNode } from '@/object-record/graphql/utils/isRecordGraphlFieldsNode'; import { FieldMetadataType, type ObjectPermissions } from 'twenty-shared/types'; import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils'; diff --git a/packages/twenty-front/src/modules/object-record/cache/hooks/useCreateOneRecordInCache.ts b/packages/twenty-front/src/modules/object-record/cache/hooks/useCreateOneRecordInCache.ts index 4208a25d29..d5257a1fad 100644 --- a/packages/twenty-front/src/modules/object-record/cache/hooks/useCreateOneRecordInCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/hooks/useCreateOneRecordInCache.ts @@ -1,13 +1,12 @@ import gql from 'graphql-tag'; -import { useRecoilValue } from 'recoil'; import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; -import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache'; import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; -import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord'; +import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { prefillRecord } from '@/object-record/utils/prefillRecord'; @@ -21,7 +20,7 @@ export const useCreateOneRecordInCache = ({ const getRecordFromCache = useGetRecordFromCache({ objectNameSingular: objectMetadataItem.nameSingular, }); - const objectMetadataItems = useRecoilValue(objectMetadataItemsState); + const { objectMetadataItems } = useObjectMetadataItems(); const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); const apolloCoreClient = useApolloCoreClient(); @@ -31,9 +30,11 @@ export const useCreateOneRecordInCache = ({ objectMetadataItem, input: record, }); - const recordGqlFields = computeDepthOneRecordGqlFieldsFromRecord({ + const recordGqlFields = generateDepthRecordGqlFieldsFromRecord({ + objectMetadataItems, objectMetadataItem, record: prefilledRecord, + depth: 1, }); const fragment = gql` fragment Create${capitalize( diff --git a/packages/twenty-front/src/modules/object-record/cache/hooks/useGetRecordFromCache.ts b/packages/twenty-front/src/modules/object-record/cache/hooks/useGetRecordFromCache.ts index 048795df26..61b4f6a10f 100644 --- a/packages/twenty-front/src/modules/object-record/cache/hooks/useGetRecordFromCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/hooks/useGetRecordFromCache.ts @@ -1,12 +1,11 @@ import { useCallback } from 'react'; -import { useRecoilValue } from 'recoil'; import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache'; -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; @@ -20,13 +19,18 @@ export const useGetRecordFromCache = ({ const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); + const { objectMetadataItems } = useObjectMetadataItems(); - const appliedRecordGqlFields = - recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem }); - - const objectMetadataItems = useRecoilValue(objectMetadataItemsState); const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); + const { recordGqlFields: depthOneRecordGqlFields } = + useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular, + depth: 1, + }); + + const appliedRecordGqlFields = recordGqlFields ?? depthOneRecordGqlFields; + const apolloCoreClient = useApolloCoreClient(); return useCallback( diff --git a/packages/twenty-front/src/modules/object-record/cache/utils/getRecordFromCache.ts b/packages/twenty-front/src/modules/object-record/cache/utils/getRecordFromCache.ts index 9e3ade0334..18e683741b 100644 --- a/packages/twenty-front/src/modules/object-record/cache/utils/getRecordFromCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/utils/getRecordFromCache.ts @@ -3,8 +3,8 @@ import { type ApolloCache, gql } from '@apollo/client'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode'; -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { type ObjectPermissions } from 'twenty-shared/types'; import { capitalize } from 'twenty-shared/utils'; @@ -35,7 +35,12 @@ export const getRecordFromCache = ({ } const appliedRecordGqlFields = - recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem }); + recordGqlFields ?? + generateDepthRecordGqlFieldsFromObject({ + objectMetadataItem, + depth: 1, + objectMetadataItems, + }); const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular); diff --git a/packages/twenty-front/src/modules/object-record/cache/utils/updateRecordFromCache.ts b/packages/twenty-front/src/modules/object-record/cache/utils/updateRecordFromCache.ts index f9783cdaf9..d66ac29bd1 100644 --- a/packages/twenty-front/src/modules/object-record/cache/utils/updateRecordFromCache.ts +++ b/packages/twenty-front/src/modules/object-record/cache/utils/updateRecordFromCache.ts @@ -4,7 +4,7 @@ import gql from 'graphql-tag'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { type ObjectPermissions } from 'twenty-shared/types'; diff --git a/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject.ts b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject.ts new file mode 100644 index 0000000000..f56ffeeb9c --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject.ts @@ -0,0 +1,30 @@ +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; + +export type GenerateDepthRecordGqlFieldsFromObjectData = { + objectNameSingular: string; + depth: 0 | 1; + shouldOnlyLoadRelationIdentifiers?: boolean; +}; + +export const useGenerateDepthRecordGqlFieldsFromObject = ({ + objectNameSingular, + depth, + shouldOnlyLoadRelationIdentifiers = true, +}: GenerateDepthRecordGqlFieldsFromObjectData) => { + const { objectMetadataItems } = useObjectMetadataItems(); + + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular, + }); + + return { + recordGqlFields: generateDepthRecordGqlFieldsFromObject({ + objectMetadataItems, + objectMetadataItem, + depth, + shouldOnlyLoadRelationIdentifiers, + }), + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/types/RecordGqlFields.ts similarity index 100% rename from packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlFields.ts rename to packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/types/RecordGqlFields.ts diff --git a/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/__tests__/__snapshots__/generateDepthRecordGqlFieldsFromObject.test.ts.snap b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/__tests__/__snapshots__/generateDepthRecordGqlFieldsFromObject.test.ts.snap new file mode 100644 index 0000000000..069e8b0b9c --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/__tests__/__snapshots__/generateDepthRecordGqlFieldsFromObject.test.ts.snap @@ -0,0 +1,81 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`generateDepthRecordGqlFieldsFromObject should generate depth one record gql fields from object 1`] = ` +{ + "accountOwner": { + "avatarUrl": true, + "id": true, + "name": true, + }, + "accountOwnerId": true, + "address": true, + "annualRecurringRevenue": true, + "attachments": { + "id": true, + "name": true, + }, + "createdAt": true, + "createdBy": true, + "deletedAt": true, + "domainName": true, + "employees": true, + "favorites": { + "id": true, + }, + "id": true, + "idealCustomerProfile": true, + "introVideo": true, + "linkedinLink": true, + "name": true, + "noteTargets": { + "id": true, + }, + "opportunities": { + "id": true, + "name": true, + }, + "people": { + "avatarUrl": true, + "id": true, + "name": true, + }, + "position": true, + "searchVector": true, + "tagline": true, + "taskTargets": { + "id": true, + }, + "timelineActivities": { + "id": true, + }, + "updatedAt": true, + "visaSponsorship": true, + "workPolicy": true, + "xLink": true, +} +`; + +exports[`generateDepthRecordGqlFieldsFromObject should generate depth zero record gql fields from object 1`] = ` +{ + "accountOwnerId": true, + "address": true, + "annualRecurringRevenue": true, + "createdAt": true, + "createdBy": true, + "deletedAt": true, + "domainName": true, + "employees": true, + "id": true, + "idealCustomerProfile": true, + "introVideo": true, + "linkedinLink": true, + "name": true, + "position": true, + "searchVector": true, + "tagline": true, + "updatedAt": true, + "visaSponsorship": true, + "workPolicy": true, + "xLink": true, +} +`; diff --git a/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/__tests__/generateDepthRecordGqlFieldsFromObject.test.ts b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/__tests__/generateDepthRecordGqlFieldsFromObject.test.ts new file mode 100644 index 0000000000..7f06bb1c2b --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/__tests__/generateDepthRecordGqlFieldsFromObject.test.ts @@ -0,0 +1,25 @@ +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; +import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; +import { getMockObjectMetadataItemOrThrow } from '~/testing/utils/getMockObjectMetadataItemOrThrow'; + +describe('generateDepthRecordGqlFieldsFromObject', () => { + it('should generate depth one record gql fields from object', () => { + const result = generateDepthRecordGqlFieldsFromObject({ + objectMetadataItem: getMockObjectMetadataItemOrThrow('company'), + objectMetadataItems: generatedMockObjectMetadataItems, + depth: 1, + }); + + expect(result).toMatchSnapshot(); + }); + + it('should generate depth zero record gql fields from object', () => { + const result = generateDepthRecordGqlFieldsFromObject({ + objectMetadataItem: getMockObjectMetadataItemOrThrow('company'), + objectMetadataItems: generatedMockObjectMetadataItems, + depth: 0, + }); + + expect(result).toMatchSnapshot(); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromFields.ts b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromFields.ts new file mode 100644 index 0000000000..90e2bd009b --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromFields.ts @@ -0,0 +1,128 @@ +import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +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 { FieldMetadataType, RelationType } from 'twenty-shared/types'; +import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils'; + +export type GenerateDepthRecordGqlFieldsFromFields = { + objectMetadataItems: ObjectMetadataItem[]; + fields: Pick< + FieldMetadataItem, + 'name' | 'type' | 'settings' | 'morphRelations' | 'relation' + >[]; + depth: 0 | 1; + shouldOnlyLoadRelationIdentifiers?: boolean; +}; + +export const generateDepthRecordGqlFieldsFromFields = ({ + objectMetadataItems, + fields, + depth, + shouldOnlyLoadRelationIdentifiers = true, +}: GenerateDepthRecordGqlFieldsFromFields) => { + const generatedRecordGqlFields: RecordGqlFields = fields.reduce( + (recordGqlFields, fieldMetadata) => { + const isMorphRelation = + fieldMetadata.type === FieldMetadataType.MORPH_RELATION; + + const isRelation = fieldMetadata.type === FieldMetadataType.RELATION; + + const relationType = fieldMetadata.settings?.relationType; + + if (isRelation) { + const targetObjectMetadataItem = objectMetadataItems.find( + (objectMetadataItem) => + objectMetadataItem.id === + fieldMetadata.relation?.targetObjectMetadata.id, + ); + + if (!targetObjectMetadataItem) { + throw new Error( + `Target object metadata item not found for ${fieldMetadata.name}`, + ); + } + + const labelIdentifierFieldMetadataItem = + getLabelIdentifierFieldMetadataItem(targetObjectMetadataItem); + + const imageIdentifierFieldMetadataItem = + getImageIdentifierFieldMetadataItem(targetObjectMetadataItem); + + const relationIdentifierSubGqlFields = { + id: true, + ...(isDefined(labelIdentifierFieldMetadataItem) + ? { [labelIdentifierFieldMetadataItem.name]: true } + : {}), + ...(isDefined(imageIdentifierFieldMetadataItem) + ? { [imageIdentifierFieldMetadataItem.name]: true } + : {}), + }; + + const manyToOneGqlFields = { + [`${fieldMetadata.name}Id`]: true, + }; + + return { + ...recordGqlFields, + ...(depth === 1 && shouldOnlyLoadRelationIdentifiers + ? { [fieldMetadata.name]: relationIdentifierSubGqlFields } + : undefined), + ...(depth === 1 && !shouldOnlyLoadRelationIdentifiers + ? { [fieldMetadata.name]: true } + : undefined), + ...(relationType === RelationType.MANY_TO_ONE + ? manyToOneGqlFields + : undefined), + }; + } + + if (isMorphRelation) { + 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 { + ...recordGqlFields, + ...morphGqlFields.reduce( + (morphGqlFields, morphGqlField) => ({ + ...morphGqlFields, + ...(depth === 1 + ? { [`${morphGqlField.gqlField}`]: { id: true, name: true } } + : {}), + ...(relationType === RelationType.MANY_TO_ONE + ? { [`${morphGqlField.gqlField}Id`]: true } + : {}), + }), + {}, + ), + }; + } + + return { + ...recordGqlFields, + [`${fieldMetadata.name}`]: true, + }; + }, + {}, + ); + + return generatedRecordGqlFields; +}; diff --git a/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject.ts b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject.ts new file mode 100644 index 0000000000..63b2fc9884 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject.ts @@ -0,0 +1,23 @@ +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { generateDepthRecordGqlFieldsFromFields } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromFields'; + +export type GenerateDepthRecordGqlFields = { + objectMetadataItems: ObjectMetadataItem[]; + objectMetadataItem: ObjectMetadataItem; + depth: 0 | 1; + shouldOnlyLoadRelationIdentifiers?: boolean; +}; + +export const generateDepthRecordGqlFieldsFromObject = ({ + objectMetadataItems, + objectMetadataItem, + depth, + shouldOnlyLoadRelationIdentifiers = true, +}: GenerateDepthRecordGqlFields) => { + return generateDepthRecordGqlFieldsFromFields({ + objectMetadataItems, + fields: objectMetadataItem.fields, + depth, + shouldOnlyLoadRelationIdentifiers, + }); +}; diff --git a/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord.ts b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord.ts new file mode 100644 index 0000000000..d88384a3b2 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord.ts @@ -0,0 +1,34 @@ +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; +import { + generateDepthRecordGqlFieldsFromObject, + type GenerateDepthRecordGqlFields, +} from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; +import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; + +type ComputeDepthRecordGqlFieldsFromRecordArgs = + GenerateDepthRecordGqlFields & { + record: Partial; + }; +export const generateDepthRecordGqlFieldsFromRecord = ({ + objectMetadataItem, + objectMetadataItems, + depth, + record, +}: ComputeDepthRecordGqlFieldsFromRecordArgs): RecordGqlFields => { + const depthRecordGqlFields = generateDepthRecordGqlFieldsFromObject({ + objectMetadataItem, + objectMetadataItems, + depth, + }); + const recordKeys = Object.keys(record); + + return Object.keys(depthRecordGqlFields).reduce( + (acc, key) => { + return { + ...acc, + [key]: recordKeys.includes(key), + }; + }, + depthRecordGqlFields, + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignatureFactory.ts b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignatureFactory.ts index 35a397a37c..9de77bed80 100644 --- a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignatureFactory.ts +++ b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationSignatureFactory.ts @@ -1,5 +1,5 @@ import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; -export type RecordGqlOperationSignatureFactory = ( - factoryParams: any, +export type RecordGqlOperationSignatureFactory = ( + factoryParams: FactoryParams, ) => RecordGqlOperationSignature; diff --git a/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/computeDepthOneRecordGqlFieldsFromRecord.test.ts b/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/computeDepthOneRecordGqlFieldsFromRecord.test.ts deleted file mode 100644 index bfbd7b920f..0000000000 --- a/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/computeDepthOneRecordGqlFieldsFromRecord.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord'; -import { - allMockPersonRecords, - getMockPersonObjectMetadataItem, -} from '~/testing/mock-data/people'; - -describe('computeDepthOneRecordGqlFieldsFromRecord', () => { - const objectMetadataItem = getMockPersonObjectMetadataItem(); - it('Should handle basic call', () => { - const personRecord = allMockPersonRecords[0]; - const result = computeDepthOneRecordGqlFieldsFromRecord({ - objectMetadataItem, - record: personRecord, - }); - expect(result).toMatchInlineSnapshot(` -{ - "attachments": false, - "avatarUrl": true, - "calendarEventParticipants": false, - "city": true, - "company": true, - "companyId": false, - "createdAt": true, - "createdBy": true, - "deletedAt": true, - "emails": true, - "favorites": false, - "id": true, - "intro": true, - "jobTitle": true, - "linkedinLink": true, - "messageParticipants": false, - "name": true, - "noteTargets": true, - "performanceRating": true, - "phones": true, - "pointOfContactForOpportunities": false, - "position": true, - "searchVector": false, - "taskTargets": true, - "timelineActivities": false, - "updatedAt": true, - "whatsapp": true, - "workPreference": true, - "xLink": true, -} -`); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneRecordGqlFields.test.ts b/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneRecordGqlFields.test.ts deleted file mode 100644 index 8bde1d2356..0000000000 --- a/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneRecordGqlFields.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; -import { getMockPersonObjectMetadataItem } from '~/testing/mock-data/people'; - -describe('generateDepthOneRecordGqlFields', () => { - const objectMetadataItem = getMockPersonObjectMetadataItem(); - it('Should handle basic call with standalone objectMetadataItem', () => { - const result = generateDepthOneRecordGqlFields({ - objectMetadataItem, - }); - expect(result).toMatchInlineSnapshot(` -{ - "attachments": true, - "avatarUrl": true, - "calendarEventParticipants": true, - "city": true, - "company": true, - "companyId": true, - "createdAt": true, - "createdBy": true, - "deletedAt": true, - "emails": true, - "favorites": true, - "id": true, - "intro": true, - "jobTitle": true, - "linkedinLink": true, - "messageParticipants": true, - "name": true, - "noteTargets": true, - "performanceRating": true, - "phones": true, - "pointOfContactForOpportunities": true, - "position": true, - "searchVector": true, - "taskTargets": true, - "timelineActivities": true, - "updatedAt": true, - "whatsapp": true, - "workPreference": true, - "xLink": true, -} -`); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneWithoutRelationsRecordGqlFields.test.ts b/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneWithoutRelationsRecordGqlFields.test.ts deleted file mode 100644 index 25f9e3ee45..0000000000 --- a/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneWithoutRelationsRecordGqlFields.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { generateDepthOneWithoutRelationsRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields'; -import { getMockPersonObjectMetadataItem } from '~/testing/mock-data/people'; - -describe('generateDepthOneWithoutRelationsRecordGqlFields', () => { - const objectMetadataItem = getMockPersonObjectMetadataItem(); - it('Should handle basic call with standalone objectMetadataItem', () => { - const result = generateDepthOneWithoutRelationsRecordGqlFields({ - objectMetadataItem, - }); - expect(result).toMatchInlineSnapshot(` -{ - "avatarUrl": true, - "city": true, - "createdAt": true, - "createdBy": true, - "deletedAt": true, - "emails": true, - "id": true, - "intro": true, - "jobTitle": true, - "linkedinLink": true, - "name": true, - "performanceRating": true, - "phones": true, - "position": true, - "searchVector": true, - "updatedAt": true, - "whatsapp": true, - "workPreference": true, - "xLink": true, -} -`); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord.ts b/packages/twenty-front/src/modules/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord.ts deleted file mode 100644 index 6d5c40e4c6..0000000000 --- a/packages/twenty-front/src/modules/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; -import { - type GenerateDepthOneRecordGqlFields, - generateDepthOneRecordGqlFields, -} from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; -import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; - -type ComputeDepthOneRecordGqlFieldsFromRecordArgs = - GenerateDepthOneRecordGqlFields & { - record: Partial; - }; -export const computeDepthOneRecordGqlFieldsFromRecord = ({ - objectMetadataItem, - record, -}: ComputeDepthOneRecordGqlFieldsFromRecordArgs): RecordGqlFields => { - const depthOneRecordGqlFields = generateDepthOneRecordGqlFields({ - objectMetadataItem, - }); - const recordKeys = Object.keys(record); - - return Object.keys(depthOneRecordGqlFields).reduce( - (acc, key) => { - return { - ...acc, - [key]: recordKeys.includes(key), - }; - }, - depthOneRecordGqlFields, - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneRecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneRecordGqlFields.ts deleted file mode 100644 index 6e6700abc5..0000000000 --- a/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneRecordGqlFields.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -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; -}; - -export const generateDepthOneRecordGqlFields = ({ - objectMetadataItem, -}: 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((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, - }; - }, {}); -}; diff --git a/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields.ts deleted file mode 100644 index 6f9c4683f3..0000000000 --- a/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { FieldMetadataType } from '~/generated-metadata/graphql'; - -type GenerateDepthOneWithoutRelationsRecordGqlFields = { - objectMetadataItem: ObjectMetadataItem; -}; - -export const generateDepthOneWithoutRelationsRecordGqlFields = ({ - objectMetadataItem, -}: GenerateDepthOneWithoutRelationsRecordGqlFields) => { - return objectMetadataItem.fields - .filter( - (field) => - field.type !== FieldMetadataType.RELATION && - field.type !== FieldMetadataType.MORPH_RELATION, - ) - .reduce>((acc, field) => { - return { - ...acc, - [field.name]: true, - }; - }, {}); -}; diff --git a/packages/twenty-front/src/modules/object-record/graphql/utils/isRecordGraphlFieldsNode.ts b/packages/twenty-front/src/modules/object-record/graphql/utils/isRecordGraphlFieldsNode.ts index 7f1bc54bb0..55a77621ff 100644 --- a/packages/twenty-front/src/modules/object-record/graphql/utils/isRecordGraphlFieldsNode.ts +++ b/packages/twenty-front/src/modules/object-record/graphql/utils/isRecordGraphlFieldsNode.ts @@ -1,4 +1,4 @@ -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; import { isDefined } from 'twenty-shared/utils'; export const isRecordGqlFieldsNode = ( diff --git a/packages/twenty-front/src/modules/object-record/hooks/__mocks__/personFragments.ts b/packages/twenty-front/src/modules/object-record/hooks/__mocks__/personFragments.ts index 5182bf5aff..875032958f 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/__mocks__/personFragments.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/__mocks__/personFragments.ts @@ -56,22 +56,8 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = ` edges { node { __typename - authorId - companyId - createdAt - deletedAt - fullPath id name - noteId - opportunityId - personId - petId - rocketId - surveyResultId - taskId - type - updatedAt } } } @@ -80,75 +66,21 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = ` edges { node { __typename - calendarEventId - createdAt - deletedAt - displayName handle id - isOrganizer - personId - responseStatus - updatedAt - workspaceMemberId } } } city company { __typename - accountOwnerId - address { - addressStreet1 - addressStreet2 - addressCity - addressState - addressCountry - addressPostcode - addressLat - addressLng - } - annualRecurringRevenue { - amountMicros - currencyCode - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt domainName { primaryLinkUrl primaryLinkLabel secondaryLinks } - employees id - idealCustomerProfile - introVideo { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } name - position - tagline - updatedAt - visaSponsorship - workPolicy - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } } companyId createdAt @@ -167,25 +99,7 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = ` edges { node { __typename - companyId - createdAt - deletedAt - favoriteFolderId - forWorkspaceMemberId id - noteId - opportunityId - personId - petId - position - rocketId - surveyResultId - taskId - updatedAt - viewId - workflowId - workflowRunId - workflowVersionId } } } @@ -201,16 +115,8 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = ` edges { node { __typename - createdAt - deletedAt - displayName handle id - messageId - personId - role - updatedAt - workspaceMemberId } } } @@ -222,17 +128,7 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = ` edges { node { __typename - companyId - createdAt - deletedAt id - noteId - opportunityId - personId - petId - rocketId - surveyResultId - updatedAt } } } @@ -247,26 +143,8 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = ` edges { node { __typename - amount { - amountMicros - currencyCode - } - closeDate - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt id name - pointOfContactId - position - stage - updatedAt } } } @@ -275,17 +153,7 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = ` edges { node { __typename - companyId - createdAt - deletedAt id - opportunityId - personId - petId - rocketId - surveyResultId - taskId - updatedAt } } } @@ -293,28 +161,7 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = ` edges { node { __typename - companyId - createdAt - deletedAt - happensAt id - linkedObjectMetadataId - linkedRecordCachedName - linkedRecordId - name - noteId - opportunityId - personId - petId - properties - rocketId - surveyResultId - taskId - updatedAt - workflowId - workflowRunId - workflowVersionId - workspaceMemberId } } } diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/__snapshots__/useDeleteOneRecord.test.tsx.snap b/packages/twenty-front/src/modules/object-record/hooks/__tests__/__snapshots__/useDeleteOneRecord.test.tsx.snap index c8e1d356a3..7707c3ba1c 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/__tests__/__snapshots__/useDeleteOneRecord.test.tsx.snap +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/__snapshots__/useDeleteOneRecord.test.tsx.snap @@ -15,42 +15,14 @@ exports[`useDeleteOneRecord B. Starting from filled cache 1. Should handle succe "city": "ASd", "company": { "__typename": "Company", - "address": { - "__typename": "Address", - "addressCity": "Dublin", - "addressCountry": "Ireland", - "addressLat": null, - "addressLng": null, - "addressPostcode": null, - "addressState": null, - "addressStreet1": "Eutaw Street", - "addressStreet2": null, - }, - "createdAt": "2025-02-16T08:21:51.715Z", - "createdBy": { - "__typename": "Actor", - "context": {}, - "name": "Tim Apple", - "source": "MANUAL", - "workspaceMemberId": "20202020-0687-4c41-b707-ed1bfca972a7", - }, - "deletedAt": null, "domainName": { "__typename": "Links", "primaryLinkLabel": "", "primaryLinkUrl": "https://linkedin.com", "secondaryLinks": [], }, - "employees": null, "id": "20202020-3ec3-4fe3-8997-b76aa0bfa408", - "linkedinLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, "name": "Linkedin", - "position": 1, }, "createdAt": "2025-01-02T09:52:46.814Z", "createdBy": { @@ -153,94 +125,21 @@ exports[`useDeleteOneRecord B. Starting from filled cache 1. Should handle succe { "__typename": "Person", "avatarUrl": null, - "city": "Seattle", - "createdAt": "2025-01-01T09:50:00.000Z", - "createdBy": { - "__typename": "Actor", - "context": null, - "name": "Tim Apple", - "source": "MANUAL", - "workspaceMemberId": "20202020-0687-4c41-b707-ed1bfca972a7", - }, - "deletedAt": null, - "emails": { - "additionalEmails": [], - "primaryEmail": "christoph.calisto@linkedin.com", - }, "id": "20202020-1c0e-494c-a1b6-85b1c6fefaa5", - "intro": "", - "jobTitle": "", - "linkedinLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, "name": { "__typename": "FullName", "firstName": "Christoph", "lastName": "Callisto", }, - "performanceRating": null, - "phones": { - "additionalPhones": [], - "primaryPhoneCallingCode": "+33", - "primaryPhoneCountryCode": "FR", - "primaryPhoneNumber": "781234562", - }, - "position": 1, - "updatedAt": "2025-01-01T09:50:00.000Z", - "whatsapp": { - "additionalPhones": [], - "primaryPhoneCallingCode": "", - "primaryPhoneCountryCode": "", - "primaryPhoneNumber": "", - }, - "workPreference": null, - "xLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, }, { "__typename": "Person", - "city": "Los Angeles", - "createdAt": "2025-01-02T09:48:36.193Z", - "createdBy": { - "__typename": "Actor", - "name": "Tim Apple", - "source": "MANUAL", - "workspaceMemberId": "20202020-0687-4c41-b707-ed1bfca972a7", - }, - "deletedAt": null, "id": "20202020-ac73-4797-824e-87a1f5aea9e0", - "jobTitle": "", - "linkedinLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, "name": { "__typename": "FullName", "firstName": "Sylvie", "lastName": "Palmer", }, - "phones": { - "additionalPhones": [], - "primaryPhoneCallingCode": "+33", - "primaryPhoneCountryCode": "FR", - "primaryPhoneNumber": "781234576", - }, - "position": 2, - "xLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, }, ], "position": 1, @@ -255,42 +154,14 @@ exports[`useDeleteOneRecord B. Starting from filled cache 2. Should handle optim "city": "ASd", "company": { "__typename": "Company", - "address": { - "__typename": "Address", - "addressCity": "Dublin", - "addressCountry": "Ireland", - "addressLat": null, - "addressLng": null, - "addressPostcode": null, - "addressState": null, - "addressStreet1": "Eutaw Street", - "addressStreet2": null, - }, - "createdAt": "2025-02-16T08:21:51.715Z", - "createdBy": { - "__typename": "Actor", - "context": {}, - "name": "Tim Apple", - "source": "MANUAL", - "workspaceMemberId": "20202020-0687-4c41-b707-ed1bfca972a7", - }, - "deletedAt": null, "domainName": { "__typename": "Links", "primaryLinkLabel": "", "primaryLinkUrl": "https://linkedin.com", "secondaryLinks": [], }, - "employees": null, "id": "20202020-3ec3-4fe3-8997-b76aa0bfa408", - "linkedinLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, "name": "Linkedin", - "position": 1, }, "createdAt": "2025-01-02T09:52:46.814Z", "createdBy": { @@ -393,94 +264,21 @@ exports[`useDeleteOneRecord B. Starting from filled cache 2. Should handle optim { "__typename": "Person", "avatarUrl": null, - "city": "Seattle", - "createdAt": "2025-01-01T09:50:00.000Z", - "createdBy": { - "__typename": "Actor", - "context": null, - "name": "Tim Apple", - "source": "MANUAL", - "workspaceMemberId": "20202020-0687-4c41-b707-ed1bfca972a7", - }, - "deletedAt": null, - "emails": { - "additionalEmails": [], - "primaryEmail": "christoph.calisto@linkedin.com", - }, "id": "20202020-1c0e-494c-a1b6-85b1c6fefaa5", - "intro": "", - "jobTitle": "", - "linkedinLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, "name": { "__typename": "FullName", "firstName": "Christoph", "lastName": "Callisto", }, - "performanceRating": null, - "phones": { - "additionalPhones": [], - "primaryPhoneCallingCode": "+33", - "primaryPhoneCountryCode": "FR", - "primaryPhoneNumber": "781234562", - }, - "position": 1, - "updatedAt": "2025-01-01T09:50:00.000Z", - "whatsapp": { - "additionalPhones": [], - "primaryPhoneCallingCode": "", - "primaryPhoneCountryCode": "", - "primaryPhoneNumber": "", - }, - "workPreference": null, - "xLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, }, { "__typename": "Person", - "city": "Los Angeles", - "createdAt": "2025-01-02T09:48:36.193Z", - "createdBy": { - "__typename": "Actor", - "name": "Tim Apple", - "source": "MANUAL", - "workspaceMemberId": "20202020-0687-4c41-b707-ed1bfca972a7", - }, - "deletedAt": null, "id": "20202020-ac73-4797-824e-87a1f5aea9e0", - "jobTitle": "", - "linkedinLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, "name": { "__typename": "FullName", "firstName": "Sylvie", "lastName": "Palmer", }, - "phones": { - "additionalPhones": [], - "primaryPhoneCallingCode": "+33", - "primaryPhoneCountryCode": "FR", - "primaryPhoneNumber": "781234576", - }, - "position": 2, - "xLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, }, ], "position": 1, @@ -495,42 +293,14 @@ exports[`useDeleteOneRecord B. Starting from filled cache 3. Should handle optim "city": "ASd", "company": { "__typename": "Company", - "address": { - "__typename": "Address", - "addressCity": "Dublin", - "addressCountry": "Ireland", - "addressLat": null, - "addressLng": null, - "addressPostcode": null, - "addressState": null, - "addressStreet1": "Eutaw Street", - "addressStreet2": null, - }, - "createdAt": "2025-02-16T08:21:51.715Z", - "createdBy": { - "__typename": "Actor", - "context": {}, - "name": "Tim Apple", - "source": "MANUAL", - "workspaceMemberId": "20202020-0687-4c41-b707-ed1bfca972a7", - }, - "deletedAt": null, "domainName": { "__typename": "Links", "primaryLinkLabel": "", "primaryLinkUrl": "https://linkedin.com", "secondaryLinks": [], }, - "employees": null, "id": "20202020-3ec3-4fe3-8997-b76aa0bfa408", - "linkedinLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, "name": "Linkedin", - "position": 1, }, "createdAt": "2025-01-02T09:52:46.814Z", "createdBy": { @@ -633,94 +403,21 @@ exports[`useDeleteOneRecord B. Starting from filled cache 3. Should handle optim { "__typename": "Person", "avatarUrl": null, - "city": "Seattle", - "createdAt": "2025-01-01T09:50:00.000Z", - "createdBy": { - "__typename": "Actor", - "context": null, - "name": "Tim Apple", - "source": "MANUAL", - "workspaceMemberId": "20202020-0687-4c41-b707-ed1bfca972a7", - }, - "deletedAt": null, - "emails": { - "additionalEmails": [], - "primaryEmail": "christoph.calisto@linkedin.com", - }, "id": "20202020-1c0e-494c-a1b6-85b1c6fefaa5", - "intro": "", - "jobTitle": "", - "linkedinLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, "name": { "__typename": "FullName", "firstName": "Christoph", "lastName": "Callisto", }, - "performanceRating": null, - "phones": { - "additionalPhones": [], - "primaryPhoneCallingCode": "+33", - "primaryPhoneCountryCode": "FR", - "primaryPhoneNumber": "781234562", - }, - "position": 1, - "updatedAt": "2025-01-01T09:50:00.000Z", - "whatsapp": { - "additionalPhones": [], - "primaryPhoneCallingCode": "", - "primaryPhoneCountryCode": "", - "primaryPhoneNumber": "", - }, - "workPreference": null, - "xLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, }, { "__typename": "Person", - "city": "Los Angeles", - "createdAt": "2025-01-02T09:48:36.193Z", - "createdBy": { - "__typename": "Actor", - "name": "Tim Apple", - "source": "MANUAL", - "workspaceMemberId": "20202020-0687-4c41-b707-ed1bfca972a7", - }, - "deletedAt": null, "id": "20202020-ac73-4797-824e-87a1f5aea9e0", - "jobTitle": "", - "linkedinLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, "name": { "__typename": "FullName", "firstName": "Sylvie", "lastName": "Palmer", }, - "phones": { - "additionalPhones": [], - "primaryPhoneCallingCode": "+33", - "primaryPhoneCountryCode": "FR", - "primaryPhoneNumber": "781234576", - }, - "position": 2, - "xLink": { - "__typename": "Links", - "primaryLinkLabel": "", - "primaryLinkUrl": "", - "secondaryLinks": [], - }, }, ], "position": 1, diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteManyRecords.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteManyRecords.test.tsx index e3fcced1c5..fb544c7727 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteManyRecords.test.tsx +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useDeleteManyRecords.test.tsx @@ -2,7 +2,7 @@ import { renderHook, waitFor } from '@testing-library/react'; import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache'; import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache'; -import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord'; +import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord'; import { personIds, personRecords, @@ -18,6 +18,8 @@ import { type MockedResponse } from '@apollo/client/testing'; import { act } from 'react'; import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; import { getMockPersonObjectMetadataItem } from '~/testing/mock-data/people'; +import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; + const getDefaultMocks = ( overrides?: Partial, ): MockedResponse[] => [ @@ -41,7 +43,7 @@ const mockRefetchAggregateQueries = jest.fn(); refetchAggregateQueries: mockRefetchAggregateQueries, }); const objectMetadataItem = getMockPersonObjectMetadataItem(); -const objectMetadataItems = [objectMetadataItem]; +const objectMetadataItems = generatedMockObjectMetadataItems; const expectedCachedRecordsWithDeletedAt = personRecords.map( (personRecord) => ({ ...personRecord, @@ -117,9 +119,11 @@ describe('useDeleteManyRecords', () => { objectMetadataItem, objectMetadataItems, record, - recordGqlFields: computeDepthOneRecordGqlFieldsFromRecord({ + recordGqlFields: generateDepthRecordGqlFieldsFromRecord({ + objectMetadataItems: generatedMockObjectMetadataItems, objectMetadataItem, record, + depth: 1, }), objectPermissionsByObjectMetadataId: {}, }), diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useRecordIndexTableQuery.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useRecordIndexTableQuery.test.tsx index 4eabd1ea6f..2515979e87 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useRecordIndexTableQuery.test.tsx +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useRecordIndexTableQuery.test.tsx @@ -46,27 +46,7 @@ const mocks: MockedResponse[] = [ node { __typename avatarUrl - city - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - emails { - primaryEmail - additionalEmails - } id - intro - jobTitle - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } name { firstName lastName @@ -77,58 +57,13 @@ const mocks: MockedResponse[] = [ __typename company { __typename - accountOwnerId - address { - addressStreet1 - addressStreet2 - addressCity - addressState - addressCountry - addressPostcode - addressLat - addressLng - } - annualRecurringRevenue { - amountMicros - currencyCode - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt domainName { primaryLinkUrl primaryLinkLabel secondaryLinks } - employees id - idealCustomerProfile - introVideo { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } name - position - tagline - updatedAt - visaSponsorship - workPolicy - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } } companyId createdAt @@ -136,208 +71,48 @@ const mocks: MockedResponse[] = [ id note { __typename - bodyV2 { - blocknote - markdown - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt id - position title - updatedAt } noteId opportunity { __typename - amount { - amountMicros - currencyCode - } - closeDate - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt id name - pointOfContactId - position - stage - updatedAt } opportunityId person { __typename avatarUrl - city - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - emails { - primaryEmail - additionalEmails - } id - intro - jobTitle - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } name { firstName lastName } - performanceRating - phones { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - position - updatedAt - whatsapp { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - workPreference - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } } personId pet { __typename - age - averageCostOfKibblePerMonth { - amountMicros - currencyCode - } - bio - birthday - comments - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - extraData id - interestingFacts - isGoodWithKids - location { - addressStreet1 - addressStreet2 - addressCity - addressState - addressCountry - addressPostcode - addressLat - addressLng - } - makesOwnerThinkOf { - firstName - lastName - } name - pictures { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - position - soundSwag - species - traits - updatedAt - vetEmail { - primaryEmail - additionalEmails - } - vetPhone { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } } petId rocket { __typename - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt id name - position - updatedAt } rocketId surveyResult { __typename - averageEstimatedNumberOfAtomsInTheUniverse - comments - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt id name - participants - percentageOfCompletion - position - score - shortNotes - updatedAt } surveyResultId updatedAt } } } - performanceRating - phones { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } position taskTargets { edges { @@ -345,58 +120,13 @@ const mocks: MockedResponse[] = [ __typename company { __typename - accountOwnerId - address { - addressStreet1 - addressStreet2 - addressCity - addressState - addressCountry - addressPostcode - addressLat - addressLng - } - annualRecurringRevenue { - amountMicros - currencyCode - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt domainName { primaryLinkUrl primaryLinkLabel secondaryLinks } - employees id - idealCustomerProfile - introVideo { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } name - position - tagline - updatedAt - visaSponsorship - workPolicy - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } } companyId createdAt @@ -404,217 +134,48 @@ const mocks: MockedResponse[] = [ id opportunity { __typename - amount { - amountMicros - currencyCode - } - closeDate - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt id name - pointOfContactId - position - stage - updatedAt } opportunityId person { __typename avatarUrl - city - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - emails { - primaryEmail - additionalEmails - } id - intro - jobTitle - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } name { firstName lastName } - performanceRating - phones { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - position - updatedAt - whatsapp { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - workPreference - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } } personId pet { __typename - age - averageCostOfKibblePerMonth { - amountMicros - currencyCode - } - bio - birthday - comments - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - extraData id - interestingFacts - isGoodWithKids - location { - addressStreet1 - addressStreet2 - addressCity - addressState - addressCountry - addressPostcode - addressLat - addressLng - } - makesOwnerThinkOf { - firstName - lastName - } name - pictures { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - position - soundSwag - species - traits - updatedAt - vetEmail { - primaryEmail - additionalEmails - } - vetPhone { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } } petId rocket { __typename - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt id name - position - updatedAt } rocketId surveyResult { __typename - averageEstimatedNumberOfAtomsInTheUniverse - comments - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt id name - participants - percentageOfCompletion - position - score - shortNotes - updatedAt } surveyResultId task { __typename - assigneeId - bodyV2 { - blocknote - markdown - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - dueAt id - position - status title - updatedAt } taskId updatedAt } } } - updatedAt - whatsapp { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - workPreference - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } } cursor } diff --git a/packages/twenty-front/src/modules/object-record/hooks/useAggregateRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/hooks/useAggregateRecordsQuery.ts index a4e41e5ab6..eb29a2a053 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useAggregateRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useAggregateRecordsQuery.ts @@ -1,6 +1,6 @@ import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; import { type RecordGqlFieldsAggregate } from '@/object-record/graphql/types/RecordGqlFieldsAggregate'; import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations'; import { generateAggregateQuery } from '@/object-record/utils/generateAggregateQuery'; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecords.ts index f1819dff85..b40626f6d3 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateManyRecords.ts @@ -11,8 +11,8 @@ import { useCreateOneRecordInCache } from '@/object-record/cache/hooks/useCreate import { deleteRecordFromCache } from '@/object-record/cache/utils/deleteRecordFromCache'; import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename'; import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useCreateManyRecordsMutation } from '@/object-record/hooks/useCreateManyRecordsMutation'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries'; @@ -54,12 +54,16 @@ export const useCreateManyRecords = < const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); + const { recordGqlFields: depthOneRecordGqlFields } = + useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular, + depth: 1, + }); const objectMetadataHasCreatedByField = hasObjectMetadataItemFieldCreatedBy(objectMetadataItem); - const computedRecordGqlFields = - recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem }); + const computedRecordGqlFields = recordGqlFields ?? depthOneRecordGqlFields; const { createManyRecordsMutation } = useCreateManyRecordsMutation({ objectNameSingular, diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts index e071ba261e..c69b275e69 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts @@ -11,8 +11,8 @@ import { useCreateOneRecordInCache } from '@/object-record/cache/hooks/useCreate import { deleteRecordFromCache } from '@/object-record/cache/utils/deleteRecordFromCache'; import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename'; import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries'; @@ -23,7 +23,6 @@ import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/g import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; - type useCreateOneRecordProps = { objectNameSingular: string; recordGqlFields?: RecordGqlOperationGqlRecordFields; @@ -46,8 +45,13 @@ export const useCreateOneRecord = < objectNameSingular, }); - const computedRecordGqlFields = - recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem }); + const { recordGqlFields: depthOneRecordGqlFields } = + useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular, + depth: 1, + }); + + const computedRecordGqlFields = recordGqlFields ?? depthOneRecordGqlFields; const { createOneRecordMutation } = useCreateOneRecordMutation({ objectNameSingular, diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts index 7e959d2bda..eabfb87fe3 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecordMutation.ts @@ -1,15 +1,11 @@ -import gql from 'graphql-tag'; import { useRecoilValue } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; -import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; +import { generateCreateOneRecordMutation } from '@/object-metadata/utils/generateCreateOneRecordMutation'; import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation'; import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; -import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField'; -import { capitalize } from 'twenty-shared/utils'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; export const useCreateOneRecordMutation = ({ @@ -23,12 +19,6 @@ export const useCreateOneRecordMutation = ({ objectNameSingular, }); - const appliedRecordGqlFields = - recordGqlFields ?? - generateDepthOneRecordGqlFields({ - objectMetadataItem, - }); - const objectMetadataItems = useRecoilValue(objectMetadataItemsState); const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); @@ -37,22 +27,12 @@ export const useCreateOneRecordMutation = ({ return { createOneRecordMutation: EMPTY_MUTATION }; } - const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular); - - const mutationResponseField = getCreateOneRecordMutationResponseField( - objectMetadataItem.nameSingular, - ); - - const createOneRecordMutation = gql` - mutation CreateOne${capitalizedObjectName}($input: ${capitalizedObjectName}CreateInput!) { - ${mutationResponseField}(data: $input) ${mapObjectMetadataToGraphQLQuery({ - objectMetadataItems, - objectMetadataItem, - recordGqlFields: appliedRecordGqlFields, - objectPermissionsByObjectMetadataId, - })} - } - `; + const createOneRecordMutation = generateCreateOneRecordMutation({ + objectMetadataItem, + objectMetadataItems, + recordGqlFields, + objectPermissionsByObjectMetadataId, + }); return { createOneRecordMutation, diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts index e7f6c2447d..47949e31d1 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts @@ -5,9 +5,9 @@ import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { type ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier'; import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode'; import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery'; import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; @@ -31,10 +31,15 @@ export const useFindOneRecord = ({ objectNameSingular, }); + const { recordGqlFields: depthOneRecordGqlFields } = + useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular, + depth: 1, + }); + const apolloCoreClient = useApolloCoreClient(); - const computedRecordGqlFields = - recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem }); + const computedRecordGqlFields = recordGqlFields ?? depthOneRecordGqlFields; const { findOneRecordQuery } = useFindOneRecordQuery({ objectNameSingular, diff --git a/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts index c9550da7cf..13336faf8a 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useLazyFindOneRecord.ts @@ -1,11 +1,10 @@ import { useLazyQuery, type WatchQueryFetchPolicy } from '@apollo/client'; import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { type ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier'; import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; @@ -26,17 +25,17 @@ export const useLazyFindOneRecord = ({ withSoftDeleted = false, fetchPolicy = 'cache-first', }: UseLazyFindOneRecordParams) => { - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular, - }); - const apolloCoreClient = useApolloCoreClient(); + const { recordGqlFields: depthOneRecordGqlFields } = + useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular, + depth: 1, + }); + const { findOneRecordQuery } = useFindOneRecordQuery({ objectNameSingular, - recordGqlFields: - recordGqlFields ?? - generateDepthOneRecordGqlFields({ objectMetadataItem }), + recordGqlFields: recordGqlFields ?? depthOneRecordGqlFields, withSoftDeleted, }); diff --git a/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecords.ts index 5ecad65934..4f8630cad3 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecords.ts @@ -2,8 +2,8 @@ import { useCallback, useState } from 'react'; import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useFindDuplicateRecordsQuery } from '@/object-record/hooks/useFindDuplicatesRecordsQuery'; import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery'; import { useMergeManyRecordsMutation } from '@/object-record/hooks/useMergeManyRecordsMutation'; @@ -34,8 +34,13 @@ export const useMergeManyRecords = < objectNameSingular, }); - const computedRecordGqlFields = - recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem }); + const { recordGqlFields: depthOneRecordGqlFields } = + useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular, + depth: 1, + }); + + const computedRecordGqlFields = recordGqlFields ?? depthOneRecordGqlFields; const { mergeManyRecordsMutation } = useMergeManyRecordsMutation({ objectNameSingular, diff --git a/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecordsMutation.ts b/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecordsMutation.ts index b867df0709..a068206924 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecordsMutation.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useMergeManyRecordsMutation.ts @@ -5,8 +5,8 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { getMergeManyRecordsMutationResponseField } from '@/object-record/utils/getMergeManyRecordsMutationResponseField'; import { capitalize } from 'twenty-shared/utils'; @@ -22,13 +22,14 @@ export const useMergeManyRecordsMutation = ({ const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); - - const appliedRecordGqlFields = - recordGqlFields ?? - generateDepthOneRecordGqlFields({ - objectMetadataItem, + const { recordGqlFields: depthOneRecordGqlFields } = + useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular, + depth: 1, }); + const appliedRecordGqlFields = recordGqlFields ?? depthOneRecordGqlFields; + const objectMetadataItems = useRecoilValue(objectMetadataItemsState); const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); diff --git a/packages/twenty-front/src/modules/object-record/hooks/useRecordOneToManyFieldAttachTargetRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useRecordOneToManyFieldAttachTargetRecord.ts index b6b3a89733..5f4590f4e0 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useRecordOneToManyFieldAttachTargetRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useRecordOneToManyFieldAttachTargetRecord.ts @@ -3,7 +3,7 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache'; import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache'; -import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord'; +import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useUpdateOneRecordV2 } from '@/object-record/hooks/useUpdateOneRecordV2'; import { CustomError, isDefined } from 'twenty-shared/utils'; @@ -80,9 +80,11 @@ export const useRecordOneToManyFieldAttachTargetRecord = () => { [targetGQLFieldName]: previousRecord, }; - const gqlFields = computeDepthOneRecordGqlFieldsFromRecord({ + const gqlFields = generateDepthRecordGqlFieldsFromRecord({ objectMetadataItem: targetObjectMetadataItem, + objectMetadataItems, record: previousRecordWithRelation, + depth: 1, }); updateRecordFromCache({ diff --git a/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecord.ts index 645147d836..c522706ce2 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecord.ts @@ -7,8 +7,8 @@ import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordF import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename'; import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache'; -import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; +import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useRefetchAggregateQueries } from '@/object-record/hooks/useRefetchAggregateQueries'; import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation'; @@ -42,8 +42,13 @@ export const useUpdateOneRecord = < objectNameSingular, }); - const computedRecordGqlFields = - recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem }); + const { recordGqlFields: depthOneRecordGqlFields } = + useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular, + depth: 1, + }); + + const computedRecordGqlFields = recordGqlFields ?? depthOneRecordGqlFields; const getRecordFromCache = useGetRecordFromCache({ objectNameSingular, @@ -109,9 +114,11 @@ export const useUpdateOneRecord = < isDefined(cachedRecordWithConnection); if (shouldHandleOptimisticCache) { - const recordGqlFields = computeDepthOneRecordGqlFieldsFromRecord({ + const recordGqlFields = generateDepthRecordGqlFieldsFromRecord({ objectMetadataItem, + objectMetadataItems, record: optimisticRecordInput, + depth: 1, }); updateRecordFromCache({ @@ -171,9 +178,11 @@ export const useUpdateOneRecord = < ).filter((diffKey) => !cachedRecordKeys.has(diffKey)); const recordGqlFields = { - ...computeDepthOneRecordGqlFieldsFromRecord({ + ...generateDepthRecordGqlFieldsFromRecord({ objectMetadataItem, + objectMetadataItems, record: cachedRecord, + depth: 1, }), ...buildRecordFromKeysWithSameValue( recordKeysAddedByOptimisticCache, diff --git a/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordV2.ts b/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordV2.ts index 2f210a5a01..659146d492 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordV2.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useUpdateOneRecordV2.ts @@ -7,9 +7,9 @@ import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache'; import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache'; -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; -import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; +import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput'; @@ -57,7 +57,11 @@ export const useUpdateOneRecordV2 = () => { const computedRecordGqlFields = recordGqlFields ?? - generateDepthOneRecordGqlFields({ objectMetadataItem }); + generateDepthRecordGqlFieldsFromObject({ + objectMetadataItem, + objectMetadataItems, + depth: 1, + }); const optimisticRecordInput = optimisticRecord ?? @@ -107,9 +111,11 @@ export const useUpdateOneRecordV2 = () => { isDefined(cachedRecordWithConnection); if (shouldHandleOptimisticCache) { - const recordGqlFields = computeDepthOneRecordGqlFieldsFromRecord({ + const recordGqlFields = generateDepthRecordGqlFieldsFromRecord({ objectMetadataItem, + objectMetadataItems, record: optimisticRecordInput, + depth: 1, }); updateRecordFromCache({ @@ -178,9 +184,11 @@ export const useUpdateOneRecordV2 = () => { ).filter((diffKey) => !cachedRecordKeys.has(diffKey)); const recordGqlFields = { - ...computeDepthOneRecordGqlFieldsFromRecord({ + ...generateDepthRecordGqlFieldsFromRecord({ objectMetadataItem, + objectMetadataItems, record: cachedRecord, + depth: 1, }), ...buildRecordFromKeysWithSameValue( recordKeysAddedByOptimisticCache, diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/__tests__/useCombinedFindManyRecords.test.tsx b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/__tests__/useCombinedFindManyRecords.test.tsx index ea560df86f..70c0ab008a 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/__tests__/useCombinedFindManyRecords.test.tsx +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/__tests__/useCombinedFindManyRecords.test.tsx @@ -3,7 +3,7 @@ import { renderHook, waitFor } from '@testing-library/react'; import { useSetRecoilState } from 'recoil'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; import { useCombinedFindManyRecords } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecords'; import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery'; diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts index b959783208..04d7108cb3 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery.ts @@ -6,11 +6,11 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { getCombinedFindManyRecordsQueryFilteringPart } from '@/object-record/multiple-objects/utils/getCombinedFindManyRecordsQueryFilteringPart'; import isEmpty from 'lodash.isempty'; import { capitalize } from 'twenty-shared/utils'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; export const useGenerateCombinedFindManyRecordsQuery = ({ operationSignatures, @@ -98,7 +98,9 @@ export const useGenerateCombinedFindManyRecordsQuery = ({ objectMetadataItem, recordGqlFields: operationSignature.fields ?? - generateDepthOneRecordGqlFields({ + generateDepthRecordGqlFieldsFromObject({ + objectMetadataItems, + depth: 1, objectMetadataItem, }), objectPermissionsByObjectMetadataId, diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts index 74fd16bfb3..5c60b137a3 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/hooks/usePerformCombinedFindManyRecords.ts @@ -2,23 +2,22 @@ import { type ApolloClient, gql } from '@apollo/client'; import { isUndefined } from '@sniptt/guards'; import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; -import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection'; import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { type CombinedFindManyRecordsQueryResult } from '@/object-record/multiple-objects/types/CombinedFindManyRecordsQueryResult'; import { generateCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables'; import { getCombinedFindManyRecordsQueryFilteringPart } from '@/object-record/multiple-objects/utils/getCombinedFindManyRecordsQueryFilteringPart'; -import { useRecoilValue } from 'recoil'; import { capitalize } from 'twenty-shared/utils'; export const usePerformCombinedFindManyRecords = () => { const apolloCoreClient = useApolloCoreClient(); - const objectMetadataItems = useRecoilValue(objectMetadataItemsState); + const { objectMetadataItems } = useObjectMetadataItems(); const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); @@ -86,8 +85,10 @@ export const usePerformCombinedFindManyRecords = () => { objectMetadataItem, recordGqlFields: operationSignature.fields ?? - generateDepthOneRecordGqlFields({ + generateDepthRecordGqlFieldsFromObject({ objectMetadataItem, + objectMetadataItems, + depth: 1, }), objectPermissionsByObjectMetadataId, })} diff --git a/packages/twenty-front/src/modules/object-record/multiple-objects/utils/__tests__/generateCombinedFindManyRecordsQueryVariables.test.ts b/packages/twenty-front/src/modules/object-record/multiple-objects/utils/__tests__/generateCombinedFindManyRecordsQueryVariables.test.ts index c2a19a075a..8429b1b8b5 100644 --- a/packages/twenty-front/src/modules/object-record/multiple-objects/utils/__tests__/generateCombinedFindManyRecordsQueryVariables.test.ts +++ b/packages/twenty-front/src/modules/object-record/multiple-objects/utils/__tests__/generateCombinedFindManyRecordsQueryVariables.test.ts @@ -1,4 +1,4 @@ -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature'; import { generateCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables'; diff --git a/packages/twenty-front/src/modules/object-record/record-calendar/components/RecordIndexCalendarDataLoaderEffect.tsx b/packages/twenty-front/src/modules/object-record/record-calendar/components/RecordIndexCalendarDataLoaderEffect.tsx index c8a18cec92..3e8ac986b9 100644 --- a/packages/twenty-front/src/modules/object-record/record-calendar/components/RecordIndexCalendarDataLoaderEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-calendar/components/RecordIndexCalendarDataLoaderEffect.tsx @@ -3,14 +3,16 @@ import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext'; import { RecordCalendarComponentInstanceContext } from '@/object-record/record-calendar/states/contexts/RecordCalendarComponentInstanceContext'; import { recordCalendarSelectedRecordIdsComponentSelector } from '@/object-record/record-calendar/states/selectors/recordCalendarSelectedRecordIdsComponentSelector'; -import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordFieldGqlFields'; +import { useRecordsFieldVisibleGqlFields } from '@/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields'; import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams'; +import { recordIndexCalendarFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexCalendarFieldMetadataIdState'; import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { useEffect } from 'react'; +import { useRecoilValue } from 'recoil'; export const RecordIndexCalendarDataLoaderEffect = () => { const recordCalendarId = useAvailableComponentInstanceIdOrThrow( @@ -34,11 +36,18 @@ export const RecordIndexCalendarDataLoaderEffect = () => { contextStoreTargetedRecordsRuleComponentState, ); + const recordIndexCalendarFieldMetadataId = useRecoilValue( + recordIndexCalendarFieldMetadataIdState, + ); + const objectNameSingular = objectMetadataItem.nameSingular; const params = useFindManyRecordIndexTableParams(objectNameSingular); - const recordGqlFields = useRecordFieldGqlFields({ objectMetadataItem }); + const recordGqlFields = useRecordsFieldVisibleGqlFields({ + objectMetadataItem, + additionalFieldMetadataId: recordIndexCalendarFieldMetadataId, + }); const { records } = useFindManyRecords({ ...params, diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/useRecordFieldGqlFields.ts b/packages/twenty-front/src/modules/object-record/record-field/hooks/useRecordFieldGqlFields.ts deleted file mode 100644 index 1c60845d3f..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/useRecordFieldGqlFields.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; -import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; -import { generateDepthOneWithoutRelationsRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields'; -import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector'; -import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { FieldMetadataType } from 'twenty-shared/types'; -import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils'; - -export const useRecordFieldGqlFields = ({ - objectMetadataItem, -}: { - objectMetadataItem: ObjectMetadataItem; -}) => { - const visibleRecordFields = useRecoilComponentValue( - visibleRecordFieldsComponentSelector, - ); - - const { fieldMetadataItemByFieldMetadataItemId } = - useRecordIndexContextOrThrow(); - - const { objectMetadataItem: noteTargetObjectMetadataItem } = - useObjectMetadataItem({ - objectNameSingular: CoreObjectNameSingular.NoteTarget, - }); - - const { objectMetadataItem: taskTargetObjectMetadataItem } = - useObjectMetadataItem({ - objectNameSingular: CoreObjectNameSingular.TaskTarget, - }); - - const allDepthOneWithoutRelationsRecordGqlFields = - generateDepthOneWithoutRelationsRecordGqlFields({ - objectMetadataItem, - }); - - const gqlFieldsList: RecordGqlFields = Object.fromEntries( - visibleRecordFields.flatMap( - (recordField): [string, boolean | RecordGqlFields][] => { - const fieldMetadataItem: FieldMetadataItem | undefined = - fieldMetadataItemByFieldMetadataItemId[ - recordField.fieldMetadataItemId - ]; - - if (!isDefined(fieldMetadataItem)) { - throw new Error( - `Field ${recordField.fieldMetadataItemId} is missing, please refresh the page. If the problem persists, please contact support.`, - ); - } - - // TODO: remove this once we have made the workflowVersion lighter - if ( - fieldMetadataItem.name === 'versions' || - fieldMetadataItem.name === 'workflowVersion' - ) { - return [[fieldMetadataItem.name, { id: true, name: true }]]; - } - - const isMorphRelation = - fieldMetadataItem.type === FieldMetadataType.MORPH_RELATION; - - if (!isMorphRelation) { - return [[fieldMetadataItem.name, true]]; - } - - if (!isDefined(fieldMetadataItem.morphRelations)) { - throw new Error( - `Field ${fieldMetadataItem.name} is missing, please refresh the page. If the problem persists, please contact support.`, - ); - } - - return fieldMetadataItem.morphRelations.map((morphRelation) => { - const computedFieldName = computeMorphRelationFieldName({ - fieldName: fieldMetadataItem.name, - relationType: fieldMetadataItem.settings?.relationType, - targetObjectMetadataNameSingular: - morphRelation.targetObjectMetadata.nameSingular, - targetObjectMetadataNamePlural: - morphRelation.targetObjectMetadata.namePlural, - }); - - return [computedFieldName, true]; - }); - }, - ), - ); - - return { - ...allDepthOneWithoutRelationsRecordGqlFields, - ...gqlFieldsList, - noteTargets: generateDepthOneRecordGqlFields({ - objectMetadataItem: noteTargetObjectMetadataItem, - }), - taskTargets: generateDepthOneRecordGqlFields({ - objectMetadataItem: taskTargetObjectMetadataItem, - }), - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields.ts b/packages/twenty-front/src/modules/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields.ts new file mode 100644 index 0000000000..9601d64ede --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields.ts @@ -0,0 +1,86 @@ +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { getImageIdentifierFieldMetadataItem } from '@/object-metadata/utils/getImageIdentifierFieldMetadataItem'; +import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem'; +import { hasObjectMetadataItemPositionField } from '@/object-metadata/utils/hasObjectMetadataItemPositionField'; +import { generateDepthRecordGqlFieldsFromFields } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromFields'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; +import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector'; +import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { isDefined } from 'twenty-shared/utils'; + +type UseRecordsFieldVisibleGqlFields = { + objectMetadataItem: ObjectMetadataItem; + additionalFieldMetadataId?: string | null; +}; + +export const useRecordsFieldVisibleGqlFields = ({ + objectMetadataItem, + additionalFieldMetadataId, +}: UseRecordsFieldVisibleGqlFields) => { + const visibleRecordFields = useRecoilComponentValue( + visibleRecordFieldsComponentSelector, + ); + + const { fieldMetadataItemByFieldMetadataItemId } = + useRecordIndexContextOrThrow(); + + const { objectMetadataItems } = useObjectMetadataItems(); + const { objectMetadataItem: noteTargetObjectMetadataItem } = + useObjectMetadataItem({ + objectNameSingular: CoreObjectNameSingular.NoteTarget, + }); + + const { objectMetadataItem: taskTargetObjectMetadataItem } = + useObjectMetadataItem({ + objectNameSingular: CoreObjectNameSingular.TaskTarget, + }); + + const allDepthOneGqlFields = generateDepthRecordGqlFieldsFromFields({ + objectMetadataItems, + fields: visibleRecordFields.map( + (field) => + fieldMetadataItemByFieldMetadataItemId[field.fieldMetadataItemId], + ), + depth: 1, + }); + + const labelIdentifierFieldMetadataItem = + getLabelIdentifierFieldMetadataItem(objectMetadataItem); + const imageIdentifierFieldMetadataItem = + getImageIdentifierFieldMetadataItem(objectMetadataItem); + + const hasPosition = hasObjectMetadataItemPositionField(objectMetadataItem); + + const additionalFieldMetadataItem = isDefined(additionalFieldMetadataId) + ? fieldMetadataItemByFieldMetadataItemId[additionalFieldMetadataId] + : undefined; + + return { + id: true, + ...(isDefined(additionalFieldMetadataItem) + ? { [additionalFieldMetadataItem.name]: true } + : {}), + ...(isDefined(labelIdentifierFieldMetadataItem) + ? { [labelIdentifierFieldMetadataItem.name]: true } + : {}), + ...(isDefined(imageIdentifierFieldMetadataItem) + ? { [imageIdentifierFieldMetadataItem.name]: true } + : {}), + ...(hasPosition ? { position: true } : {}), + ...allDepthOneGqlFields, + noteTargets: generateDepthRecordGqlFieldsFromObject({ + objectMetadataItem: noteTargetObjectMetadataItem, + objectMetadataItems, + depth: 1, + }), + taskTargets: generateDepthRecordGqlFieldsFromObject({ + objectMetadataItems, + objectMetadataItem: taskTargetObjectMetadataItem, + depth: 1, + }), + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/__tests__/useToggleEditOnlyInput.test.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/__tests__/useToggleEditOnlyInput.test.tsx index 94202162fa..5f613446a2 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/__tests__/useToggleEditOnlyInput.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/hooks/__tests__/useToggleEditOnlyInput.test.tsx @@ -1,9 +1,9 @@ -import { gql } from '@apollo/client'; import { type MockedResponse } from '@apollo/client/testing'; import { renderHook, waitFor } from '@testing-library/react'; import { act, type ReactNode } from 'react'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { generateUpdateOneRecordMutation } from '@/object-metadata/utils/generateUpdateOneRecordMutation'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { booleanFieldDefinition } from '@/object-record/record-field/ui/__mocks__/fieldDefinitions'; import { @@ -14,290 +14,23 @@ import { import { useToggleEditOnlyInput } from '@/object-record/record-field/ui/hooks/useToggleEditOnlyInput'; import { generateEmptyJestRecordNode } from '~/testing/jest/generateEmptyJestRecordNode'; import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; +import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; +import { getMockObjectMetadataItemOrThrow } from '~/testing/utils/getMockObjectMetadataItemOrThrow'; const recordId = 'recordId'; +const companyObjectMetadataItem = getMockObjectMetadataItemOrThrow('company'); +const updateOneCompanyMutation = generateUpdateOneRecordMutation({ + objectMetadataItem: companyObjectMetadataItem, + objectMetadataItems: generatedMockObjectMetadataItems, + computeReferences: false, + objectPermissionsByObjectMetadataId: {}, +}); + const mocks: MockedResponse[] = [ { request: { - query: gql` - mutation UpdateOneCompany( - $idToUpdate: UUID! - $input: CompanyUpdateInput! - ) { - updateCompany(id: $idToUpdate, data: $input) { - __typename - accountOwner { - __typename - avatarUrl - colorScheme - createdAt - dateFormat - deletedAt - id - locale - name { - firstName - lastName - } - position - timeFormat - timeZone - updatedAt - userEmail - userId - } - accountOwnerId - address { - addressStreet1 - addressStreet2 - addressCity - addressState - addressCountry - addressPostcode - addressLat - addressLng - } - annualRecurringRevenue { - amountMicros - currencyCode - } - attachments { - edges { - node { - __typename - authorId - companyId - createdAt - deletedAt - fullPath - id - name - noteId - opportunityId - personId - petId - rocketId - surveyResultId - taskId - type - updatedAt - } - } - } - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - domainName { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - employees - favorites { - edges { - node { - __typename - companyId - createdAt - deletedAt - favoriteFolderId - forWorkspaceMemberId - id - noteId - opportunityId - personId - petId - position - rocketId - surveyResultId - taskId - updatedAt - viewId - workflowId - workflowRunId - workflowVersionId - } - } - } - id - idealCustomerProfile - introVideo { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - name - noteTargets { - edges { - node { - __typename - companyId - createdAt - deletedAt - id - noteId - opportunityId - personId - petId - rocketId - surveyResultId - updatedAt - } - } - } - opportunities { - edges { - node { - __typename - amount { - amountMicros - currencyCode - } - closeDate - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - id - name - pointOfContactId - position - stage - updatedAt - } - } - } - people { - edges { - node { - __typename - avatarUrl - city - companyId - createdAt - createdBy { - source - workspaceMemberId - name - context - } - deletedAt - emails { - primaryEmail - additionalEmails - } - id - intro - jobTitle - linkedinLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - name { - firstName - lastName - } - performanceRating - phones { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - position - updatedAt - whatsapp { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - } - workPreference - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - } - } - } - position - tagline - taskTargets { - edges { - node { - __typename - companyId - createdAt - deletedAt - id - opportunityId - personId - petId - rocketId - surveyResultId - taskId - updatedAt - } - } - } - timelineActivities { - edges { - node { - __typename - companyId - createdAt - deletedAt - happensAt - id - linkedObjectMetadataId - linkedRecordCachedName - linkedRecordId - name - noteId - opportunityId - personId - petId - properties - rocketId - surveyResultId - taskId - updatedAt - workflowId - workflowRunId - workflowVersionId - workspaceMemberId - } - } - } - updatedAt - visaSponsorship - workPolicy - xLink { - primaryLinkUrl - primaryLinkLabel - secondaryLinks - } - } - } - `, + query: updateOneCompanyMutation, variables: { idToUpdate: 'recordId', input: { idealCustomerProfile: true }, diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts index 8a083ab5a1..a8be60f881 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts @@ -9,12 +9,13 @@ import { currentRecordFilterGroupsComponentState } from '@/object-record/record- import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; -import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields'; import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; +import { useRecordsFieldVisibleGqlFields } from '@/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields'; import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState'; +import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { @@ -79,10 +80,14 @@ export const useLoadRecordIndexBoardColumn = ({ }); const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts); - - const recordGqlFields = useRecordBoardRecordGqlFields({ - objectMetadataItem, + const recordGroupFieldMetadata = useRecoilComponentValue( + recordGroupFieldMetadataComponentState, recordBoardId, + ); + + const recordGqlFields = useRecordsFieldVisibleGqlFields({ + objectMetadataItem, + additionalFieldMetadataId: recordGroupFieldMetadata?.id, }); const recordIndexKanbanFieldMetadataFilterValue = isDefined( diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts deleted file mode 100644 index 54fd0cffa7..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; -import { generateDepthOneWithoutRelationsRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields'; -import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector'; -import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; -import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { isDefined } from 'twenty-shared/utils'; - -export const useRecordBoardRecordGqlFields = ({ - objectMetadataItem, - recordBoardId, -}: { - recordBoardId: string; - objectMetadataItem: ObjectMetadataItem; -}) => { - const visibleRecordFields = useRecoilComponentValue( - visibleRecordFieldsComponentSelector, - recordBoardId, - ); - - const { fieldMetadataItemByFieldMetadataItemId } = - useRecordIndexContextOrThrow(); - - const recordGroupFieldMetadata = useRecoilComponentValue( - recordGroupFieldMetadataComponentState, - recordBoardId, - ); - - const { objectMetadataItem: noteTargetObjectMetadataItem } = - useObjectMetadataItem({ - objectNameSingular: CoreObjectNameSingular.NoteTarget, - }); - - const { objectMetadataItem: taskTargetObjectMetadataItem } = - useObjectMetadataItem({ - objectNameSingular: CoreObjectNameSingular.TaskTarget, - }); - - const allDepthOneWithoutRelationsRecordGqlFields = - generateDepthOneWithoutRelationsRecordGqlFields({ - objectMetadataItem, - }); - - const visibleFieldMetadataItems = visibleRecordFields - .map( - (recordField) => - fieldMetadataItemByFieldMetadataItemId[ - recordField.fieldMetadataItemId - ] ?? null, - ) - .filter(isDefined); - - const recordGqlFields: Record = { - ...allDepthOneWithoutRelationsRecordGqlFields, - ...Object.fromEntries( - visibleFieldMetadataItems.map((visibleFieldMetadataItem) => [ - visibleFieldMetadataItem.name, - true, - ]), - ), - noteTargets: generateDepthOneRecordGqlFields({ - objectMetadataItem: noteTargetObjectMetadataItem, - }), - taskTargets: generateDepthOneRecordGqlFields({ - objectMetadataItem: taskTargetObjectMetadataItem, - }), - }; - - if (isDefined(recordGroupFieldMetadata?.name)) { - recordGqlFields[recordGroupFieldMetadata.name] = true; - } - - return recordGqlFields; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexTableFetchMore.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexTableFetchMore.ts index a7d3735f56..8e4adf6767 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexTableFetchMore.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexTableFetchMore.ts @@ -1,16 +1,17 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords'; -import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordFieldGqlFields'; +import { useRecordsFieldVisibleGqlFields } from '@/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields'; import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams'; - export const useRecordIndexTableFetchMore = (objectNameSingular: string) => { + const params = useFindManyRecordIndexTableParams(objectNameSingular); + const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); - const params = useFindManyRecordIndexTableParams(objectNameSingular); - - const recordGqlFields = useRecordFieldGqlFields({ objectMetadataItem }); + const recordGqlFields = useRecordsFieldVisibleGqlFields({ + objectMetadataItem, + }); const { fetchMoreRecordsLazy, queryIdentifier } = useLazyFindManyRecords({ ...params, diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexTableQuery.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexTableQuery.ts index 9f2dde5b68..5ce6ce269f 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexTableQuery.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexTableQuery.ts @@ -1,20 +1,21 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; -import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordFieldGqlFields'; +import { useRecordsFieldVisibleGqlFields } from '@/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields'; import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams'; import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies'; import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal'; - export const useRecordIndexTableQuery = (objectNameSingular: string) => { - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular, - }); - const showAuthModal = useShowAuthModal(); const params = useFindManyRecordIndexTableParams(objectNameSingular); - const recordGqlFields = useRecordFieldGqlFields({ objectMetadataItem }); + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular, + }); + + const recordGqlFields = useRecordsFieldVisibleGqlFields({ + objectMetadataItem, + }); const { records, hasNextPage, queryIdentifier, loading } = useFindManyRecords( { diff --git a/packages/twenty-front/src/modules/object-record/record-show/graphql/operations/factories/findOneRecordForShowPageOperationSignatureFactory.ts b/packages/twenty-front/src/modules/object-record/record-show/graphql/operations/factories/findOneRecordForShowPageOperationSignatureFactory.ts index 3d3ff6fbbd..5bb7ffbd27 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/graphql/operations/factories/findOneRecordForShowPageOperationSignatureFactory.ts +++ b/packages/twenty-front/src/modules/object-record/record-show/graphql/operations/factories/findOneRecordForShowPageOperationSignatureFactory.ts @@ -1,46 +1,56 @@ import { generateActivityTargetMorphFieldKeys } from '@/activities/utils/generateActivityTargetMorphFieldKeys'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; import { type RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; -export const buildFindOneRecordForShowPageOperationSignature: RecordGqlOperationSignatureFactory = - ({ - objectMetadataItem, - objectMetadataItems, - }: { - objectMetadataItem: ObjectMetadataItem; - objectMetadataItems: ObjectMetadataItem[]; - }) => ({ - objectNameSingular: objectMetadataItem.nameSingular, - variables: {}, - fields: { - ...generateDepthOneRecordGqlFields({ objectMetadataItem }), - ...(objectMetadataItem.nameSingular === CoreObjectNameSingular.Task - ? { - taskTargets: { - id: true, - __typename: true, - createdAt: true, - updatedAt: true, - note: true, - noteId: true, - ...generateActivityTargetMorphFieldKeys(objectMetadataItems), - }, - } - : {}), - ...(objectMetadataItem.nameSingular === CoreObjectNameSingular.Note - ? { - noteTargets: { - id: true, - __typename: true, - createdAt: true, - updatedAt: true, - task: true, - taskId: true, - ...generateActivityTargetMorphFieldKeys(objectMetadataItems), - }, - } - : {}), - }, - }); +type FindOneRecordForShowPageOperationSignatureFactory = { + objectMetadataItem: ObjectMetadataItem; + objectMetadataItems: ObjectMetadataItem[]; +}; + +export const buildFindOneRecordForShowPageOperationSignature: RecordGqlOperationSignatureFactory< + FindOneRecordForShowPageOperationSignatureFactory +> = ({ + objectMetadataItem, + objectMetadataItems, +}: { + objectMetadataItem: ObjectMetadataItem; + objectMetadataItems: ObjectMetadataItem[]; +}) => ({ + objectNameSingular: objectMetadataItem.nameSingular, + variables: {}, + fields: { + ...generateDepthRecordGqlFieldsFromObject({ + objectMetadataItem, + objectMetadataItems, + depth: 1, + }), + ...(objectMetadataItem.nameSingular === CoreObjectNameSingular.Task + ? { + taskTargets: { + id: true, + __typename: true, + createdAt: true, + updatedAt: true, + note: true, + noteId: true, + ...generateActivityTargetMorphFieldKeys(objectMetadataItems), + }, + } + : {}), + ...(objectMetadataItem.nameSingular === CoreObjectNameSingular.Note + ? { + noteTargets: { + id: true, + __typename: true, + createdAt: true, + updatedAt: true, + task: true, + taskId: true, + ...generateActivityTargetMorphFieldKeys(objectMetadataItems), + }, + } + : {}), + }, +}); diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useOpenObjectRecordsSpreadsheetImportDialog.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useOpenObjectRecordsSpreadsheetImportDialog.ts index 531377f5a6..7b6a9a279d 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useOpenObjectRecordsSpreadsheetImportDialog.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useOpenObjectRecordsSpreadsheetImportDialog.ts @@ -1,6 +1,6 @@ import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { generateDepthOneWithoutRelationsRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { useBatchCreateManyRecords } from '@/object-record/hooks/useBatchCreateManyRecords'; import { useBuildSpreadsheetImportFields } from '@/object-record/spreadsheet-import/hooks/useBuildSpreadSheetImportFields'; import { buildRecordFromImportedStructuredRow } from '@/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow'; @@ -32,11 +32,14 @@ export const useOpenObjectRecordsSpreadsheetImportDialog = ( const abortController = new AbortController(); + const { recordGqlFields } = useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular, + depth: 0, + }); + const { batchCreateManyRecords } = useBatchCreateManyRecords({ objectNameSingular, - recordGqlFields: generateDepthOneWithoutRelationsRecordGqlFields({ - objectMetadataItem, - }), + recordGqlFields, mutationBatchSize: SPREADSHEET_IMPORT_CREATE_RECORDS_BATCH_SIZE, setBatchedRecordsCount: setCreatedRecordsProgress, abortController, diff --git a/packages/twenty-front/src/modules/object-record/utils/__tests__/computeOptimisticRecordFromInput.test.ts b/packages/twenty-front/src/modules/object-record/utils/__tests__/computeOptimisticRecordFromInput.test.ts index f3186cde50..2819918d08 100644 --- a/packages/twenty-front/src/modules/object-record/utils/__tests__/computeOptimisticRecordFromInput.test.ts +++ b/packages/twenty-front/src/modules/object-record/utils/__tests__/computeOptimisticRecordFromInput.test.ts @@ -1,6 +1,6 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache'; -import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord'; +import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord'; import { type FieldActorForInputValue } from '@/object-record/record-field/ui/types/FieldMetadata'; import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput'; import { InMemoryCache } from '@apollo/client'; @@ -128,9 +128,11 @@ describe('computeOptimisticRecordFromInput', () => { (field) => field.name === 'id', ), }; - const recordGqlFields = computeDepthOneRecordGqlFieldsFromRecord({ + const recordGqlFields = generateDepthRecordGqlFieldsFromRecord({ objectMetadataItem, + objectMetadataItems: generatedMockObjectMetadataItems, record: companyRecord, + depth: 1, }); updateRecordFromCache({ objectMetadataItems: generatedMockObjectMetadataItems, @@ -178,9 +180,11 @@ describe('computeOptimisticRecordFromInput', () => { }), ], }; - const recordGqlFields = computeDepthOneRecordGqlFieldsFromRecord({ + const recordGqlFields = generateDepthRecordGqlFieldsFromRecord({ + depth: 1, objectMetadataItem, record: companyRecord, + objectMetadataItems: generatedMockObjectMetadataItems, }); updateRecordFromCache({ objectMetadataItems: generatedMockObjectMetadataItems, diff --git a/packages/twenty-front/src/modules/object-record/utils/generateAggregateQuery.ts b/packages/twenty-front/src/modules/object-record/utils/generateAggregateQuery.ts index 14397402f4..7dd4ef412e 100644 --- a/packages/twenty-front/src/modules/object-record/utils/generateAggregateQuery.ts +++ b/packages/twenty-front/src/modules/object-record/utils/generateAggregateQuery.ts @@ -1,7 +1,7 @@ import gql from 'graphql-tag'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields'; +import { type RecordGqlFields } from '@/object-record/graphql/record-gql-fields/types/RecordGqlFields'; import { getAggregateQueryName } from '@/object-record/utils/getAggregateQueryName'; import { capitalize } from 'twenty-shared/utils'; diff --git a/packages/twenty-front/src/modules/prefetch/components/PrefetchRunFavoriteQueriesEffect.tsx b/packages/twenty-front/src/modules/prefetch/components/PrefetchRunFavoriteQueriesEffect.tsx index ed1245da15..dca51992af 100644 --- a/packages/twenty-front/src/modules/prefetch/components/PrefetchRunFavoriteQueriesEffect.tsx +++ b/packages/twenty-front/src/modules/prefetch/components/PrefetchRunFavoriteQueriesEffect.tsx @@ -5,6 +5,7 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { type Favorite } from '@/favorites/types/Favorite'; import { type FavoriteFolder } from '@/favorites/types/FavoriteFolder'; import { useIsSettingsPage } from '@/navigation/hooks/useIsSettingsPage'; +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; @@ -36,19 +37,19 @@ export const PrefetchRunFavoriteQueriesEffect = () => { prefetchIsLoadedFamilyState(PrefetchKey.AllFavoritesFolders), ); + const { objectMetadataItem: favoriteObjectMetadataItem } = + useObjectMetadataItem({ + objectNameSingular: CoreObjectNameSingular.Favorite, + }); + const findAllFavoritesOperationSignature = findAllFavoritesOperationSignatureFactory({ - objectMetadataItem: objectMetadataItems.find( - (item) => item.nameSingular === CoreObjectNameSingular.Favorite, - ), + objectMetadataItem: favoriteObjectMetadataItem, + objectMetadataItems, }); const findAllFavoriteFoldersOperationSignature = - findAllFavoritesFolderOperationSignatureFactory({ - objectMetadataItem: objectMetadataItems.find( - (item) => item.nameSingular === CoreObjectNameSingular.FavoriteFolder, - ), - }); + findAllFavoritesFolderOperationSignatureFactory({}); const { records: favorites } = useFindManyRecords({ objectNameSingular: CoreObjectNameSingular.Favorite, diff --git a/packages/twenty-front/src/modules/prefetch/constants/PrefetchConfig.ts b/packages/twenty-front/src/modules/prefetch/constants/PrefetchConfig.ts deleted file mode 100644 index 57e2dcf05d..0000000000 --- a/packages/twenty-front/src/modules/prefetch/constants/PrefetchConfig.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { type RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory'; -import { findAllFavoritesFolderOperationSignatureFactory } from '@/prefetch/graphql/operation-signatures/factories/findAllFavoritesFolderOperationSignatureFactory'; -import { findAllFavoritesOperationSignatureFactory } from '@/prefetch/graphql/operation-signatures/factories/findAllFavoritesOperationSignatureFactory'; -import { type PrefetchKey } from '@/prefetch/types/PrefetchKey'; - -export const PREFETCH_CONFIG: Record< - PrefetchKey, - { - objectNameSingular: CoreObjectNameSingular; - operationSignatureFactory: RecordGqlOperationSignatureFactory; - } -> = { - ALL_FAVORITES: { - objectNameSingular: CoreObjectNameSingular.Favorite, - operationSignatureFactory: findAllFavoritesOperationSignatureFactory, - }, - ALL_FAVORITES_FOLDERS: { - objectNameSingular: CoreObjectNameSingular.FavoriteFolder, - operationSignatureFactory: findAllFavoritesFolderOperationSignatureFactory, - }, -}; diff --git a/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllFavoritesFolderOperationSignatureFactory.ts b/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllFavoritesFolderOperationSignatureFactory.ts index 1eb031dc8b..358741bad2 100644 --- a/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllFavoritesFolderOperationSignatureFactory.ts +++ b/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllFavoritesFolderOperationSignatureFactory.ts @@ -1,7 +1,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { type RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory'; -export const findAllFavoritesFolderOperationSignatureFactory: RecordGqlOperationSignatureFactory = +export const findAllFavoritesFolderOperationSignatureFactory: RecordGqlOperationSignatureFactory<{}> = () => ({ objectNameSingular: CoreObjectNameSingular.FavoriteFolder, variables: {}, diff --git a/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllFavoritesOperationSignatureFactory.ts b/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllFavoritesOperationSignatureFactory.ts index 5c39e2ab31..46f9905604 100644 --- a/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllFavoritesOperationSignatureFactory.ts +++ b/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllFavoritesOperationSignatureFactory.ts @@ -1,15 +1,26 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; import { type RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; -export const findAllFavoritesOperationSignatureFactory: RecordGqlOperationSignatureFactory = - ({ objectMetadataItem }: { objectMetadataItem: ObjectMetadataItem }) => ({ - objectNameSingular: CoreObjectNameSingular.Favorite, - variables: {}, - fields: { - ...generateDepthOneRecordGqlFields({ - objectMetadataItem, - }), - }, - }); +type FindAllFavoritesOperationSignatureFactory = { + objectMetadataItem: ObjectMetadataItem; + objectMetadataItems: ObjectMetadataItem[]; +}; + +export const findAllFavoritesOperationSignatureFactory: RecordGqlOperationSignatureFactory< + FindAllFavoritesOperationSignatureFactory +> = ({ + objectMetadataItem, + objectMetadataItems, +}: FindAllFavoritesOperationSignatureFactory) => ({ + objectNameSingular: CoreObjectNameSingular.Favorite, + variables: {}, + fields: { + ...generateDepthRecordGqlFieldsFromObject({ + objectMetadataItems, + objectMetadataItem, + depth: 1, + }), + }, +}); diff --git a/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllViewsOperationSignatureFactory.ts b/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllViewsOperationSignatureFactory.ts deleted file mode 100644 index e00159d823..0000000000 --- a/packages/twenty-front/src/modules/prefetch/graphql/operation-signatures/factories/findAllViewsOperationSignatureFactory.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { type RecordGqlOperationSignatureFactory } from '@/object-record/graphql/types/RecordGqlOperationSignatureFactory'; - -export const findAllViewsOperationSignatureFactory: RecordGqlOperationSignatureFactory = - () => ({ - objectNameSingular: CoreObjectNameSingular.View, - variables: {}, - fields: { - id: true, - createdAt: true, - updatedAt: true, - isCompact: true, - openRecordIn: true, - objectMetadataId: true, - position: true, - type: true, - kanbanFieldMetadataId: true, - kanbanAggregateOperation: true, - kanbanAggregateOperationFieldMetadataId: true, - name: true, - icon: true, - key: true, - viewFilters: true, - viewFilterGroups: true, - viewSorts: true, - viewFields: true, - viewGroups: true, - anyFieldFilterValue: true, - }, - }); diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx index 29aeeb53cd..7dba93c97e 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsMessageChannelsContainer.tsx @@ -4,9 +4,8 @@ import { useRecoilValue, useSetRecoilState } from 'recoil'; import { type ConnectedAccount } from '@/accounts/types/ConnectedAccount'; import { type MessageChannel } from '@/accounts/types/MessageChannel'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { SettingsAccountsMessageChannelDetails } from '@/settings/accounts/components/SettingsAccountsMessageChannelDetails'; import { SettingsNewAccountSection } from '@/settings/accounts/components/SettingsNewAccountSection'; @@ -32,10 +31,6 @@ export const SettingsAccountsMessageChannelsContainer = () => { settingsAccountsSelectedMessageChannelState, ); - const messageChannelObjectMetadataItem = useObjectMetadataItem({ - objectNameSingular: CoreObjectNameSingular.MessageChannel, - }); - const { records: accounts } = useFindManyRecords({ objectNameSingular: CoreObjectNameSingular.ConnectedAccount, filter: { @@ -45,6 +40,11 @@ export const SettingsAccountsMessageChannelsContainer = () => { }, }); + const { recordGqlFields } = useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular: CoreObjectNameSingular.MessageChannel, + depth: 1, + }); + const { records: messageChannels } = useFindManyRecords< MessageChannel & { connectedAccount: ConnectedAccount; @@ -59,9 +59,7 @@ export const SettingsAccountsMessageChannelsContainer = () => { eq: true, }, }, - recordGqlFields: generateDepthOneRecordGqlFields( - messageChannelObjectMetadataItem, - ), + recordGqlFields, onCompleted: (data) => { setSelectedMessageChannel(data[0]); }, diff --git a/packages/twenty-front/src/modules/subscription/components/ListenRecordUpdatesEffect.tsx b/packages/twenty-front/src/modules/subscription/components/ListenRecordUpdatesEffect.tsx index facc6ad76b..0a85f81ff6 100644 --- a/packages/twenty-front/src/modules/subscription/components/ListenRecordUpdatesEffect.tsx +++ b/packages/twenty-front/src/modules/subscription/components/ListenRecordUpdatesEffect.tsx @@ -6,7 +6,7 @@ import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordF import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename'; import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; @@ -37,8 +37,9 @@ export const ListenRecordUpdatesEffect = ({ const { objectMetadataItems } = useObjectMetadataItems(); const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); - const computedRecordGqlFields = generateDepthOneRecordGqlFields({ - objectMetadataItem, + const computedRecordGqlFields = useGenerateDepthRecordGqlFieldsFromObject({ + depth: 1, + objectNameSingular, }); const setRecordInStore = useRecoilCallback( diff --git a/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowVersion.tsx b/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowVersion.tsx index edc445d9fa..4aa343493f 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowVersion.tsx +++ b/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowVersion.tsx @@ -9,7 +9,7 @@ import { useCreateOneRecordInCache } from '@/object-record/cache/hooks/useCreate import { useUpsertFindOneRecordQueryInCache } from '@/object-record/cache/hooks/useUpsertFindOneRecordQueryInCache'; import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename'; import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { computeOptimisticCreateRecordBaseRecordInput } from '@/object-record/utils/computeOptimisticCreateRecordBaseRecordInput'; @@ -31,14 +31,14 @@ export const useRunWorkflowVersion = () => { const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular: CoreObjectNameSingular.WorkflowRun, }); + + const { objectMetadataItems } = useObjectMetadataItems(); const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); const createOneRecordInCache = useCreateOneRecordInCache({ objectMetadataItem, }); const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); - const { objectMetadataItems } = useObjectMetadataItems(); - const [mutate] = useMutation< RunWorkflowVersionMutation, RunWorkflowVersionMutationVariables @@ -46,8 +46,9 @@ export const useRunWorkflowVersion = () => { client: apolloCoreClient, }); - const computedRecordGqlFields = generateDepthOneRecordGqlFields({ - objectMetadataItem, + const computedRecordGqlFields = useGenerateDepthRecordGqlFieldsFromObject({ + objectNameSingular: CoreObjectNameSingular.WorkflowRun, + depth: 1, }); const { upsertFindOneRecordQueryInCache } = diff --git a/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx b/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx index a2364dbb8e..e357c77611 100644 --- a/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx +++ b/packages/twenty-front/src/pages/settings/accounts/SettingsAccounts.tsx @@ -1,8 +1,7 @@ import { type ConnectedAccount } from '@/accounts/types/ConnectedAccount'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { SettingsAccountLoader } from '@/settings/accounts/components/SettingsAccountLoader'; import { SettingsAccountsBlocklistSection } from '@/settings/accounts/components/SettingsAccountsBlocklistSection'; @@ -21,8 +20,10 @@ export const SettingsAccounts = () => { const { t } = useLingui(); const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); - const { objectMetadataItem } = useObjectMetadataItem({ + const { recordGqlFields } = useGenerateDepthRecordGqlFieldsFromObject({ objectNameSingular: CoreObjectNameSingular.ConnectedAccount, + depth: 1, + shouldOnlyLoadRelationIdentifiers: false, }); const { records: accounts, loading } = useFindManyRecords({ @@ -32,7 +33,7 @@ export const SettingsAccounts = () => { eq: currentWorkspaceMember?.id, }, }, - recordGqlFields: generateDepthOneRecordGqlFields({ objectMetadataItem }), + recordGqlFields, }); return ( diff --git a/packages/twenty-front/src/testing/cache/inMemoryTestingCacheInstance.ts b/packages/twenty-front/src/testing/cache/inMemoryTestingCacheInstance.ts index f309723df5..f2d8b52f8c 100644 --- a/packages/twenty-front/src/testing/cache/inMemoryTestingCacheInstance.ts +++ b/packages/twenty-front/src/testing/cache/inMemoryTestingCacheInstance.ts @@ -1,7 +1,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache'; import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache'; -import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord'; +import { generateDepthRecordGqlFieldsFromRecord } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromRecord'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { InMemoryCache, type NormalizedCacheObject } from '@apollo/client'; @@ -57,7 +57,9 @@ export class InMemoryTestingCacheInstance { objectMetadataItem, objectMetadataItems: this.objectMetadataItems, record, - recordGqlFields: computeDepthOneRecordGqlFieldsFromRecord({ + recordGqlFields: generateDepthRecordGqlFieldsFromRecord({ + objectMetadataItems: this.objectMetadataItems, + depth: 1, objectMetadataItem, record, }), diff --git a/packages/twenty-front/src/testing/jest/generateEmptyJestRecordNode.ts b/packages/twenty-front/src/testing/jest/generateEmptyJestRecordNode.ts index 057da63da6..d7d29fba70 100644 --- a/packages/twenty-front/src/testing/jest/generateEmptyJestRecordNode.ts +++ b/packages/twenty-front/src/testing/jest/generateEmptyJestRecordNode.ts @@ -1,5 +1,5 @@ import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord'; -import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { generateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/utils/generateDepthRecordGqlFieldsFromObject'; import { prefillRecord } from '@/object-record/utils/prefillRecord'; import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; import { getMockObjectMetadataItemOrThrow } from '~/testing/utils/getMockObjectMetadataItemOrThrow'; @@ -33,8 +33,10 @@ export const generateEmptyJestRecordNode = ({ objectMetadataItem, objectMetadataItems: generatedMockObjectMetadataItems, recordGqlFields: withDepthOneRelation - ? generateDepthOneRecordGqlFields({ + ? generateDepthRecordGqlFieldsFromObject({ objectMetadataItem, + objectMetadataItems: generatedMockObjectMetadataItems, + depth: 1, }) : undefined, });