Reduce relation loading overload on FE graphql queries (#14991)
## Problem
With Twenty usage growing, we are facing challenges on server side to
respond to the demand. The current bottleneck we are facing is server
CPU.
While investigating CPU performances, we figured out that loading all
relation fields was the biggest issue.
### About graphql query response size
**Example:** on `People Index` Table page:
we query: `person.company` and we query **all non relation fields** on
company relation field.
`{ person { company { id, name, domainName, employees, address ... } }`
However we only need **company.id, company.name and company.domainName**
to be able to render the Company Chip in the company column (RELATION)
in the table.
`{ person { company { id, name, domainName } }`
We initially assumed that the querying all non relation fields was not
an issue because it was not adding additional load on database (which is
also partially true: if a field is containing a huge json this will add
load on postgres as data needs to be transfered)
This assumption is wrong on CPU side:
- when loading one to many relations (company.people), this quickly ads
up and we end up with response of 20kB quite quickly, even without
adding any custom field on person.
- even when loading a many to one relation, if the user is storing big
data in a given field (note.body for instance, or workflowRun.state),
this starts also being an issue.
- Worst case scenario are starting to happen: on a production workspace
with 20 active workflows. Loading workflow table while displaying
workflow runs commands (20 workflows x 60 workflowRuns x
workflowRun.state which is a big JSON) result in a response of... 125MB.
### Why is it an issue?
When yoga (graphql engine) parses a response to send it back in server
response, it's using `JSON.parse `(or stringify depending on the case).
Parsing data is CPU instensive (as you need to validate, transform) and
this is more or less `O(n)`.
This means returning 125MB is very intense on the CPU and will likely
use 100% of the CPU for ~1sec in our production servers.
NodeJs (nodeV8) is single threaded. It's able to process multiple
requests in parallel but to do that, it will cut them in "microTasks"
and process each microTasks (that can belong to different requests) one
after the other. Exactly like a single threaded CPU would allocate some
time to a process and then to the next one, etc.
This means that this big response request will actually block the other
request.
As a result, all requests are slow and our infrastructure is multi
tenant so some customers are impacting others.
This is even a bigger issue when our health checks start to fail and
containers are starting being considered as unhealthy and killed by the
orchestrator
## How to fix this
1. On Front end side, we should only query what we need. In this PR, I'm
forcing the relations to only query: `id`, `labelIdentifier`,
`imageIdentifier`
2. (later) as we are API first, we also need to do something to protect
the servers from this side too. To do that, Graphql APIs can associate a
cost to each graphql field (maybe 1 for a test fixed, 2 for a JSON
field, 10 for relations, etc...) and we can throttle based on that.
## Changes in this PR
It's mainly about refactor the tooling to generate `RecordGqlFields`
(`generateDepthOneGqlFields`). this tooling is used to generate the list
of fields we want to query. Most of the time we want to query a record
with one level of nesting.
1) Refactor to remove duplicate code => `generateDepthOne` become
`generateDepth` and can handle both `depth = 0 | 1`
2) Introduce `generateDepthRecordGqlFieldsFromFields.ts` which is the
base and can give you a list of gqlFields based on FieldMetadataItems
3) Introduce `generateDepthRecordGqlFieldsFromObject` which is a
shortcut for an object
4) Introduce `generateDepthRecordGqlFieldsFromRecords` which is the
intersection between `generateDepthRecordGqlFieldsFromObject` and a
given record (useful for cache tooling)
5) Replace all usages + introduce a hook
`useGenerateDepthRecordGqlFieldsFromObject` to ease usage
This commit is contained in:
+38
-33
@@ -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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
+39
-34
@@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
+70
-67
@@ -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),
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
+21
-18
@@ -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),
|
||||
},
|
||||
});
|
||||
|
||||
+11
-143
@@ -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 },
|
||||
|
||||
+7
-6
@@ -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',
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
+50
@@ -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;
|
||||
};
|
||||
+4
-2
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
+16
@@ -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,
|
||||
}),
|
||||
);
|
||||
@@ -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<FieldMetadataItem, 'id' | 'name'>;
|
||||
objectMetadataItem: Pick<
|
||||
ObjectMetadataItem,
|
||||
'imageIdentifierFieldMetadataId' | 'nameSingular'
|
||||
>;
|
||||
}) => {
|
||||
if (
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Company &&
|
||||
fieldMetadataItem.name === 'domainName'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
fieldMetadataItem.id === objectMetadataItem.imageIdentifierFieldMetadataId
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+6
-5
@@ -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 = <T extends ObjectRecord>({
|
||||
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 = <T extends ObjectRecord>({
|
||||
objectMetadataItem,
|
||||
input: record,
|
||||
});
|
||||
const recordGqlFields = computeDepthOneRecordGqlFieldsFromRecord({
|
||||
const recordGqlFields = generateDepthRecordGqlFieldsFromRecord({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
record: prefilledRecord,
|
||||
depth: 1,
|
||||
});
|
||||
const fragment = gql`
|
||||
fragment Create${capitalize(
|
||||
|
||||
+12
-8
@@ -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(
|
||||
|
||||
+8
-3
@@ -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 = <T extends ObjectRecord = ObjectRecord>({
|
||||
}
|
||||
|
||||
const appliedRecordGqlFields =
|
||||
recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem });
|
||||
recordGqlFields ??
|
||||
generateDepthRecordGqlFieldsFromObject({
|
||||
objectMetadataItem,
|
||||
depth: 1,
|
||||
objectMetadataItems,
|
||||
});
|
||||
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
||||
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+30
@@ -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,
|
||||
}),
|
||||
};
|
||||
};
|
||||
+81
@@ -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,
|
||||
}
|
||||
`;
|
||||
+25
@@ -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();
|
||||
});
|
||||
});
|
||||
+128
@@ -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;
|
||||
};
|
||||
+23
@@ -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,
|
||||
});
|
||||
};
|
||||
+34
@@ -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<ObjectRecord>;
|
||||
};
|
||||
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<RecordGqlFields>(
|
||||
(acc, key) => {
|
||||
return {
|
||||
...acc,
|
||||
[key]: recordKeys.includes(key),
|
||||
};
|
||||
},
|
||||
depthRecordGqlFields,
|
||||
);
|
||||
};
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { type RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
|
||||
|
||||
export type RecordGqlOperationSignatureFactory = (
|
||||
factoryParams: any,
|
||||
export type RecordGqlOperationSignatureFactory<FactoryParams extends object> = (
|
||||
factoryParams: FactoryParams,
|
||||
) => RecordGqlOperationSignature;
|
||||
|
||||
-49
@@ -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,
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
-44
@@ -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,
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
-34
@@ -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,
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
-30
@@ -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<ObjectRecord>;
|
||||
};
|
||||
export const computeDepthOneRecordGqlFieldsFromRecord = ({
|
||||
objectMetadataItem,
|
||||
record,
|
||||
}: ComputeDepthOneRecordGqlFieldsFromRecordArgs): RecordGqlFields => {
|
||||
const depthOneRecordGqlFields = generateDepthOneRecordGqlFields({
|
||||
objectMetadataItem,
|
||||
});
|
||||
const recordKeys = Object.keys(record);
|
||||
|
||||
return Object.keys(depthOneRecordGqlFields).reduce<RecordGqlFields>(
|
||||
(acc, key) => {
|
||||
return {
|
||||
...acc,
|
||||
[key]: recordKeys.includes(key),
|
||||
};
|
||||
},
|
||||
depthOneRecordGqlFields,
|
||||
);
|
||||
};
|
||||
-73
@@ -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<RecordGqlFields>((acc, field) => {
|
||||
return {
|
||||
...acc,
|
||||
[field]:
|
||||
// TODO: Remove once we have made the workflows lighter
|
||||
(objectMetadataItem.nameSingular === CoreObjectNameSingular.Workflow ||
|
||||
objectMetadataItem.nameSingular ===
|
||||
CoreObjectNameSingular.WorkflowVersion ||
|
||||
objectMetadataItem.nameSingular ===
|
||||
CoreObjectNameSingular.WorkflowRun) &&
|
||||
(field === 'versions' || field === 'runs')
|
||||
? { id: true, name: true }
|
||||
: true,
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
-23
@@ -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<Record<string, true>>((acc, field) => {
|
||||
return {
|
||||
...acc,
|
||||
[field.name]: true,
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
+1
-1
@@ -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 = (
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-303
@@ -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,
|
||||
|
||||
+7
-3
@@ -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>,
|
||||
): 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: {},
|
||||
}),
|
||||
|
||||
-439
@@ -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
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
+7
-27
@@ -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,
|
||||
|
||||
@@ -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 = <T extends ObjectRecord = ObjectRecord>({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { recordGqlFields: depthOneRecordGqlFields } =
|
||||
useGenerateDepthRecordGqlFieldsFromObject({
|
||||
objectNameSingular,
|
||||
depth: 1,
|
||||
});
|
||||
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
const computedRecordGqlFields =
|
||||
recordGqlFields ?? generateDepthOneRecordGqlFields({ objectMetadataItem });
|
||||
const computedRecordGqlFields = recordGqlFields ?? depthOneRecordGqlFields;
|
||||
|
||||
const { findOneRecordQuery } = useFindOneRecordQuery({
|
||||
objectNameSingular,
|
||||
|
||||
@@ -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 = <T extends ObjectRecord = ObjectRecord>({
|
||||
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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
+7
-6
@@ -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();
|
||||
|
||||
+4
-2
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+4
-2
@@ -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,
|
||||
|
||||
+6
-5
@@ -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,
|
||||
})}
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
|
||||
+11
-2
@@ -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,
|
||||
|
||||
-102
@@ -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,
|
||||
}),
|
||||
};
|
||||
};
|
||||
+86
@@ -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,
|
||||
}),
|
||||
};
|
||||
};
|
||||
+12
-279
@@ -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 },
|
||||
|
||||
+9
-4
@@ -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(
|
||||
|
||||
-77
@@ -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<string, any> = {
|
||||
...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;
|
||||
};
|
||||
+6
-5
@@ -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,
|
||||
|
||||
+8
-7
@@ -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(
|
||||
{
|
||||
|
||||
+51
-41
@@ -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),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
+7
-4
@@ -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,
|
||||
|
||||
+7
-3
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
+9
-8
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
+1
-1
@@ -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: {},
|
||||
|
||||
+22
-11
@@ -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,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
-30
@@ -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,
|
||||
},
|
||||
});
|
||||
+7
-9
@@ -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<ConnectedAccount>({
|
||||
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]);
|
||||
},
|
||||
|
||||
+4
-3
@@ -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(
|
||||
|
||||
@@ -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<WorkflowRun>({
|
||||
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 } =
|
||||
|
||||
@@ -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<ConnectedAccount>({
|
||||
@@ -32,7 +33,7 @@ export const SettingsAccounts = () => {
|
||||
eq: currentWorkspaceMember?.id,
|
||||
},
|
||||
},
|
||||
recordGqlFields: generateDepthOneRecordGqlFields({ objectMetadataItem }),
|
||||
recordGqlFields,
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user