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, });