[REFACTOR][BUG] Dynamically compute field to write in cache CREATE (#10130)

# Introduction
While importing records encountering missing expected fields when
writting a fragment from apollo cache

## Updates

### 1/ `createdBy` Default value
When inserting in cache in create single or many we will now make
optimistic behavior on the createdBy value

### 2/ `createRecordInCache` dynamically create `recordGrqlFields`
When creating an entry in cache, we will now dynamically generate fields
to be written in the fragment instead of expecting all of them. As by
nature record could be partial

### 3/ Strictly typed `RecordGqlFields`

# Conclusion
closes #9927
This commit is contained in:
Paul Rastoin
2025-02-13 17:43:54 +01:00
committed by GitHub
parent 58a62ec6f0
commit 5963c0f384
35 changed files with 495 additions and 107 deletions

View File

@ -95,7 +95,7 @@ idealCustomerProfile
it('should return only return relation subFields that are in recordGqlFields', async () => {
const res = mapFieldMetadataToGraphQLQuery({
objectMetadataItems: generatedMockObjectMetadataItems,
relationrecordFields: {
relationRecordGqlFields: {
accountOwner: { id: true, name: true },
people: true,
xLink: true,

View File

@ -0,0 +1,10 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { FieldMetadataType } from 'twenty-shared';
export const checkObjectMetadataItemHasFieldCreatedBy = (
objectMetadataItem: ObjectMetadataItem,
) =>
objectMetadataItem.fields.some(
(field) =>
field.type === FieldMetadataType.ACTOR && field.name === 'createdBy',
);

View File

@ -1,26 +1,27 @@
import { isUndefined } from '@sniptt/guards';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
import { isUndefined } from '@sniptt/guards';
import {
FieldMetadataType,
RelationDefinitionType,
} from '~/generated-metadata/graphql';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
import { FieldMetadataItem } from '../types/FieldMetadataItem';
type MapFieldMetadataToGraphQLQueryArgs = {
objectMetadataItems: ObjectMetadataItem[];
field: Pick<FieldMetadataItem, 'name' | 'type' | 'relationDefinition'>;
relationRecordGqlFields?: RecordGqlFields;
computeReferences?: boolean;
};
// TODO: change ObjectMetadataItems mock before refactoring with relationDefinition computed field
export const mapFieldMetadataToGraphQLQuery = ({
objectMetadataItems,
field,
relationrecordFields,
relationRecordGqlFields,
computeReferences = false,
}: {
objectMetadataItems: ObjectMetadataItem[];
field: Pick<FieldMetadataItem, 'name' | 'type' | 'relationDefinition'>;
relationrecordFields?: Record<string, any>;
computeReferences?: boolean;
}): any => {
}: MapFieldMetadataToGraphQLQueryArgs): string => {
const fieldType = field.type;
const fieldIsSimpleValue = [
@ -61,7 +62,7 @@ export const mapFieldMetadataToGraphQLQuery = ({
${mapObjectMetadataToGraphQLQuery({
objectMetadataItems,
objectMetadataItem: relationMetadataItem,
recordGqlFields: relationrecordFields,
recordGqlFields: relationRecordGqlFields,
computeReferences: computeReferences,
isRootLevel: false,
})}`;
@ -87,7 +88,7 @@ ${mapObjectMetadataToGraphQLQuery({
node ${mapObjectMetadataToGraphQLQuery({
objectMetadataItems,
objectMetadataItem: relationMetadataItem,
recordGqlFields: relationrecordFields,
recordGqlFields: relationRecordGqlFields,
computeReferences,
isRootLevel: false,
})}

View File

@ -1,20 +1,23 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
import { shouldFieldBeQueried } from '@/object-metadata/utils/shouldFieldBeQueried';
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
import { isRecordGqlFieldsNode } from '@/object-record/graphql/utils/isRecordGraphlFieldsNode';
type MapObjectMetadataToGraphQLQueryArgs = {
objectMetadataItems: ObjectMetadataItem[];
objectMetadataItem: Pick<ObjectMetadataItem, 'nameSingular' | 'fields'>;
recordGqlFields?: RecordGqlFields;
computeReferences?: boolean;
isRootLevel?: boolean;
};
export const mapObjectMetadataToGraphQLQuery = ({
objectMetadataItems,
objectMetadataItem,
recordGqlFields,
computeReferences = false,
isRootLevel = true,
}: {
objectMetadataItems: ObjectMetadataItem[];
objectMetadataItem: Pick<ObjectMetadataItem, 'nameSingular' | 'fields'>;
recordGqlFields?: Record<string, any>;
computeReferences?: boolean;
isRootLevel?: boolean;
}): any => {
}: MapObjectMetadataToGraphQLQueryArgs): string => {
const fieldsThatShouldBeQueried =
objectMetadataItem?.fields
.filter((field) => field.isActive)
@ -36,13 +39,16 @@ export const mapObjectMetadataToGraphQLQuery = ({
__typename
${fieldsThatShouldBeQueried
.map((field) => {
const currentRecordGqlFields = recordGqlFields?.[field.name];
const relationRecordGqlFields = isRecordGqlFieldsNode(
currentRecordGqlFields,
)
? currentRecordGqlFields
: undefined;
return mapFieldMetadataToGraphQLQuery({
objectMetadataItems,
field,
relationrecordFields:
typeof recordGqlFields?.[field.name] === 'boolean'
? undefined
: recordGqlFields?.[field.name],
relationRecordGqlFields,
computeReferences,
});
})