[REFACTOR][BUG] Dynamically compute field to write in cache UPDATE & DELETE (#10079)
# Introduction At the moment when updating any record cache occurence, we will build a fragment that will expect all of the object metadata item fields to be provided. Which result in the following traces: ( in the video companies aren't fetch with companyId and other missing fields ) https://github.com/user-attachments/assets/56eab7c1-8f01-45ff-8f5d-78737b788b92 By definition as we're using graphql we might not request every record's fields each time we wanna consume them. In this way we will now dynamically compute or expect depending on the CRUD operation specific fields to be written in the cache, and not all of them Tested all optimistic and failure management use cases ## Covering cache Added coverage only for the `deleteOne` and `deleteMany` hooks, it cover only the record record cache and not its relations hydratation ( for the moment ) ## Why not closing #9927 Unless I'm mistaken everything done here have fixed the same logs/traces issue for updates and deletion but not creation. Which means we still need to investigate the mass upload from import and prefillRecord behavior In a nutshell: went over each `updateRecordFromCache` calls, still need to do all `createRecordInCache` calls related to #9927 ## Conlusion Sorry for the big PR should have ejected into a specific one for the `MinimalRecord` refactor Will also continue covering others hooks later in my week as for the `deleteOne` As always any suggestions are welcomed !
This commit is contained in:
@ -1,36 +1,16 @@
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { computeOptimisticRecordFromInput } from '@/object-record/utils/computeOptimisticRecordFromInput';
|
||||
import { InMemoryCache } from '@apollo/client';
|
||||
import { getCompanyObjectMetadataItem } from '~/testing/mock-data/companies';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
|
||||
const getPersonObjectMetadaItem = () => {
|
||||
const personObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'person',
|
||||
);
|
||||
|
||||
if (!personObjectMetadataItem) {
|
||||
throw new Error('Person object metadata item not found');
|
||||
}
|
||||
|
||||
return personObjectMetadataItem;
|
||||
};
|
||||
|
||||
const getCompanyObjectMetadataItem = () => {
|
||||
const companyObjectMetadataItem = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
);
|
||||
|
||||
if (!companyObjectMetadataItem) {
|
||||
throw new Error('Company object metadata item not found');
|
||||
}
|
||||
|
||||
return companyObjectMetadataItem;
|
||||
};
|
||||
import { getPersonObjectMetadataItem } from '~/testing/mock-data/people';
|
||||
|
||||
describe('computeOptimisticRecordFromInput', () => {
|
||||
it('should generate correct optimistic record if no relation field is present', () => {
|
||||
const cache = new InMemoryCache();
|
||||
const personObjectMetadataItem = getPersonObjectMetadaItem();
|
||||
const personObjectMetadataItem = getPersonObjectMetadataItem();
|
||||
|
||||
const result = computeOptimisticRecordFromInput({
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
@ -48,7 +28,7 @@ describe('computeOptimisticRecordFromInput', () => {
|
||||
|
||||
it('should generate correct optimistic record if relation field is present but cache is empty', () => {
|
||||
const cache = new InMemoryCache();
|
||||
const personObjectMetadataItem = getPersonObjectMetadaItem();
|
||||
const personObjectMetadataItem = getPersonObjectMetadataItem();
|
||||
|
||||
const result = computeOptimisticRecordFromInput({
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
@ -66,7 +46,7 @@ describe('computeOptimisticRecordFromInput', () => {
|
||||
|
||||
it('should generate correct optimistic record even if recordInput contains field __typename', () => {
|
||||
const cache = new InMemoryCache();
|
||||
const personObjectMetadataItem = getPersonObjectMetadaItem();
|
||||
const personObjectMetadataItem = getPersonObjectMetadataItem();
|
||||
const companyObjectMetadataItem = getCompanyObjectMetadataItem();
|
||||
|
||||
const companyRecord = {
|
||||
@ -74,16 +54,22 @@ describe('computeOptimisticRecordFromInput', () => {
|
||||
__typename: 'Company',
|
||||
};
|
||||
|
||||
const objectMetadataItem: ObjectMetadataItem = {
|
||||
...companyObjectMetadataItem,
|
||||
fields: companyObjectMetadataItem.fields.filter(
|
||||
(field) => field.name === 'id',
|
||||
),
|
||||
};
|
||||
const recordGqlFields = generateDepthOneRecordGqlFields({
|
||||
objectMetadataItem,
|
||||
record: companyRecord,
|
||||
});
|
||||
updateRecordFromCache({
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
objectMetadataItem: {
|
||||
...companyObjectMetadataItem,
|
||||
fields: companyObjectMetadataItem.fields.filter(
|
||||
(field) => field.name === 'id',
|
||||
),
|
||||
},
|
||||
objectMetadataItem,
|
||||
cache,
|
||||
record: companyRecord,
|
||||
recordGqlFields,
|
||||
});
|
||||
|
||||
const result = computeOptimisticRecordFromInput({
|
||||
@ -104,7 +90,7 @@ describe('computeOptimisticRecordFromInput', () => {
|
||||
|
||||
it('should generate correct optimistic record if relation field is present and cache is not empty', () => {
|
||||
const cache = new InMemoryCache();
|
||||
const personObjectMetadataItem = getPersonObjectMetadaItem();
|
||||
const personObjectMetadataItem = getPersonObjectMetadataItem();
|
||||
const companyObjectMetadataItem = getCompanyObjectMetadataItem();
|
||||
|
||||
const companyRecord = {
|
||||
@ -112,16 +98,22 @@ describe('computeOptimisticRecordFromInput', () => {
|
||||
__typename: 'Company',
|
||||
};
|
||||
|
||||
const objectMetadataItem: ObjectMetadataItem = {
|
||||
...companyObjectMetadataItem,
|
||||
fields: companyObjectMetadataItem.fields.filter(
|
||||
(field) => field.name === 'id',
|
||||
),
|
||||
};
|
||||
const recordGqlFields = generateDepthOneRecordGqlFields({
|
||||
objectMetadataItem,
|
||||
record: companyRecord,
|
||||
});
|
||||
updateRecordFromCache({
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
objectMetadataItem: {
|
||||
...companyObjectMetadataItem,
|
||||
fields: companyObjectMetadataItem.fields.filter(
|
||||
(field) => field.name === 'id',
|
||||
),
|
||||
},
|
||||
objectMetadataItem,
|
||||
cache,
|
||||
record: companyRecord,
|
||||
recordGqlFields,
|
||||
});
|
||||
|
||||
const result = computeOptimisticRecordFromInput({
|
||||
@ -141,7 +133,7 @@ describe('computeOptimisticRecordFromInput', () => {
|
||||
|
||||
it('should generate correct optimistic record if relation field is null and cache is empty', () => {
|
||||
const cache = new InMemoryCache();
|
||||
const personObjectMetadataItem = getPersonObjectMetadaItem();
|
||||
const personObjectMetadataItem = getPersonObjectMetadataItem();
|
||||
|
||||
const result = computeOptimisticRecordFromInput({
|
||||
objectMetadataItems: generatedMockObjectMetadataItems,
|
||||
@ -160,7 +152,7 @@ describe('computeOptimisticRecordFromInput', () => {
|
||||
|
||||
it('should throw an error if recordInput contains fields unrelated to the current objectMetadata', () => {
|
||||
const cache = new InMemoryCache();
|
||||
const personObjectMetadataItem = getPersonObjectMetadaItem();
|
||||
const personObjectMetadataItem = getPersonObjectMetadataItem();
|
||||
|
||||
expect(() =>
|
||||
computeOptimisticRecordFromInput({
|
||||
@ -181,7 +173,7 @@ describe('computeOptimisticRecordFromInput', () => {
|
||||
|
||||
it('should throw an error if recordInput contains both the relationFieldId and relationField', () => {
|
||||
const cache = new InMemoryCache();
|
||||
const personObjectMetadataItem = getPersonObjectMetadaItem();
|
||||
const personObjectMetadataItem = getPersonObjectMetadataItem();
|
||||
|
||||
expect(() =>
|
||||
computeOptimisticRecordFromInput({
|
||||
@ -200,7 +192,7 @@ describe('computeOptimisticRecordFromInput', () => {
|
||||
|
||||
it('should throw an error if recordInput contains both the relationFieldId and relationField even if null', () => {
|
||||
const cache = new InMemoryCache();
|
||||
const personObjectMetadataItem = getPersonObjectMetadaItem();
|
||||
const personObjectMetadataItem = getPersonObjectMetadataItem();
|
||||
|
||||
expect(() =>
|
||||
computeOptimisticRecordFromInput({
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
|
||||
import {
|
||||
FieldMetadataType,
|
||||
RelationDefinitionType,
|
||||
@ -106,8 +107,14 @@ export const generateEmptyFieldValue = (
|
||||
additionalPhones: null,
|
||||
};
|
||||
}
|
||||
case FieldMetadataType.TS_VECTOR: {
|
||||
throw new Error('TS_VECTOR not implemented yet');
|
||||
}
|
||||
default: {
|
||||
throw new Error('Unhandled FieldMetadataType');
|
||||
return assertUnreachable(
|
||||
fieldMetadataItem.type,
|
||||
'Unhandled FieldMetadataType',
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user