Fix optimistic effect deletedAt (#7606)

In this PR, I'm fixing part of the impact of soft deletion on optimistic
rendering.

## Backend Vision

1) Backend endpoints will not return soft deleted records (having
deletedAt set) by default. To get the softDeleted records, we will pass
a { withSoftDelete: true } additional param in the query.
2) Record relations will NEVER contain softDeleted relations

## Backend current state

Right now, we have the following behavior:
- if the query filters do not mention deletedAt, we don't return
softDeletedRecords
- if the query filters mention deletedAt, we take it into consideration.
Meaning that if we want to have the softDeleted records in any way we
need to do { or: [ deletedAt: NULL, deletedAt: NOT_NULL] }

## Optimistic rendering strategy

1) useDestroyOne/Many is triggering destroyOptimisticEffects (previously
deleteOptimisticEffects)
2) UseDeleteOne/Many and useRestoreOne/Many are actually triggering
updateOptimisticEffects (as they only update deletedAt field) AND we
need updateOptimisticEffects to take into account deletedAt (future
withSoftDelete: true) filter.
This commit is contained in:
Charles Bochet
2024-10-11 20:23:01 +02:00
committed by GitHub
parent d350143c92
commit 7b96be6f8c
21 changed files with 406 additions and 256 deletions

View File

@ -7,15 +7,15 @@ import { isObjectRecordConnectionWithRefs } from '@/object-record/cache/utils/is
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
import { isDefined } from '~/utils/isDefined';
export const triggerDeleteRecordsOptimisticEffect = ({
export const triggerDestroyRecordsOptimisticEffect = ({
cache,
objectMetadataItem,
recordsToDelete,
recordsToDestroy,
objectMetadataItems,
}: {
cache: ApolloCache<unknown>;
objectMetadataItem: ObjectMetadataItem;
recordsToDelete: RecordGqlNode[];
recordsToDestroy: RecordGqlNode[];
objectMetadataItems: ObjectMetadataItem[];
}) => {
cache.modify<StoreObject>({
@ -36,7 +36,7 @@ export const triggerDeleteRecordsOptimisticEffect = ({
const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;
const recordIdsToDelete = recordsToDelete.map(({ id }) => id);
const recordIdsToDelete = recordsToDestroy.map(({ id }) => id);
const cachedEdges = readField<RecordGqlRefEdge[]>(
'edges',
@ -69,20 +69,15 @@ export const triggerDeleteRecordsOptimisticEffect = ({
},
});
recordsToDelete.forEach((recordToDelete) => {
recordsToDestroy.forEach((recordToDestroy) => {
triggerUpdateRelationsOptimisticEffect({
cache,
sourceObjectMetadataItem: objectMetadataItem,
currentSourceRecord: recordToDelete,
currentSourceRecord: recordToDestroy,
updatedSourceRecord: null,
objectMetadataItems,
});
cache.modify({
id: cache.identify(recordToDelete),
fields: {
deletedAt: () => recordToDelete.deletedAt,
},
});
cache.evict({ id: cache.identify(recordToDestroy) });
});
};

View File

@ -65,48 +65,43 @@ export const triggerUpdateRecordOptimisticEffect = ({
const rootQueryFilter = rootQueryVariables?.filter;
const rootQueryOrderBy = rootQueryVariables?.orderBy;
const shouldTryToMatchFilter = isDefined(rootQueryFilter);
const updatedRecordMatchesThisRootQueryFilter = isRecordMatchingFilter({
record: updatedRecord,
filter: rootQueryFilter ?? {},
objectMetadataItem,
});
if (shouldTryToMatchFilter) {
const updatedRecordMatchesThisRootQueryFilter =
isRecordMatchingFilter({
record: updatedRecord,
filter: rootQueryFilter,
objectMetadataItem,
const updatedRecordIndexInRootQueryEdges =
rootQueryCurrentEdges.findIndex(
(cachedEdge) =>
readField('id', cachedEdge.node) === updatedRecord.id,
);
const updatedRecordFoundInRootQueryEdges =
updatedRecordIndexInRootQueryEdges > -1;
const updatedRecordShouldBeAddedToRootQueryEdges =
updatedRecordMatchesThisRootQueryFilter &&
!updatedRecordFoundInRootQueryEdges;
const updatedRecordShouldBeRemovedFromRootQueryEdges =
!updatedRecordMatchesThisRootQueryFilter &&
updatedRecordFoundInRootQueryEdges;
if (updatedRecordShouldBeAddedToRootQueryEdges) {
const updatedRecordNodeReference = toReference(updatedRecord);
if (isDefined(updatedRecordNodeReference)) {
rootQueryNextEdges.push({
__typename: getEdgeTypename(objectMetadataItem.nameSingular),
node: updatedRecordNodeReference,
cursor: '',
});
const updatedRecordIndexInRootQueryEdges =
rootQueryCurrentEdges.findIndex(
(cachedEdge) =>
readField('id', cachedEdge.node) === updatedRecord.id,
);
const updatedRecordFoundInRootQueryEdges =
updatedRecordIndexInRootQueryEdges > -1;
const updatedRecordShouldBeAddedToRootQueryEdges =
updatedRecordMatchesThisRootQueryFilter &&
!updatedRecordFoundInRootQueryEdges;
const updatedRecordShouldBeRemovedFromRootQueryEdges =
!updatedRecordMatchesThisRootQueryFilter &&
updatedRecordFoundInRootQueryEdges;
if (updatedRecordShouldBeAddedToRootQueryEdges) {
const updatedRecordNodeReference = toReference(updatedRecord);
if (isDefined(updatedRecordNodeReference)) {
rootQueryNextEdges.push({
__typename: getEdgeTypename(objectMetadataItem.nameSingular),
node: updatedRecordNodeReference,
cursor: '',
});
}
}
}
if (updatedRecordShouldBeRemovedFromRootQueryEdges) {
rootQueryNextEdges.splice(updatedRecordIndexInRootQueryEdges, 1);
}
if (updatedRecordShouldBeRemovedFromRootQueryEdges) {
rootQueryNextEdges.splice(updatedRecordIndexInRootQueryEdges, 1);
}
const rootQueryNextEdgesShouldBeSorted = isDefined(rootQueryOrderBy);

View File

@ -1,7 +1,7 @@
import { ApolloCache } from '@apollo/client';
import { triggerAttachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerAttachRelationOptimisticEffect';
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
import { triggerDestroyRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDestroyRecordsOptimisticEffect';
import { triggerDetachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDetachRelationOptimisticEffect';
import { CORE_OBJECT_NAMES_TO_DELETE_ON_TRIGGER_RELATION_DETACH } from '@/apollo/types/coreObjectNamesToDeleteOnRelationDetach';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
@ -122,10 +122,10 @@ export const triggerUpdateRelationsOptimisticEffect = ({
);
if (shouldCascadeDeleteTargetRecords) {
triggerDeleteRecordsOptimisticEffect({
triggerDestroyRecordsOptimisticEffect({
cache,
objectMetadataItem: fullTargetObjectMetadataItem,
recordsToDelete: targetRecordsToDetachFrom,
recordsToDestroy: targetRecordsToDetachFrom,
objectMetadataItems,
});
} else {