fix: detach relation records in cache on record deletion (#3707)
* fix: detach relation records in cache on record deletion * fix: fix useGetRelationMetadata tests
This commit is contained in:
@ -15,6 +15,7 @@ export const useGetRecordFromCache = ({
|
||||
|
||||
return <CachedObjectRecord extends ObjectRecord = ObjectRecord>(
|
||||
recordId: string,
|
||||
cache = apolloClient.cache,
|
||||
) => {
|
||||
if (!objectMetadataItem) {
|
||||
return null;
|
||||
@ -31,7 +32,6 @@ export const useGetRecordFromCache = ({
|
||||
}
|
||||
`;
|
||||
|
||||
const cache = apolloClient.cache;
|
||||
const cachedRecordId = cache.identify({
|
||||
__typename: capitalize(objectMetadataItem.nameSingular),
|
||||
id: recordId,
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { isObjectRecordConnection } from '@/apollo/optimistic-effect/utils/isObjectRecordConnection';
|
||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
||||
import { triggerDetachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDetachRelationOptimisticEffect';
|
||||
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { getDeleteManyRecordsMutationResponseField } from '@/object-record/hooks/useGenerateDeleteManyRecordMutation';
|
||||
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
type useDeleteOneRecordProps = {
|
||||
@ -14,9 +19,11 @@ type useDeleteOneRecordProps = {
|
||||
export const useDeleteManyRecords = ({
|
||||
objectNameSingular,
|
||||
}: useDeleteOneRecordProps) => {
|
||||
const { objectMetadataItem, deleteManyRecordsMutation } =
|
||||
const { objectMetadataItem, deleteManyRecordsMutation, getRecordFromCache } =
|
||||
useObjectMetadataItem({ objectNameSingular });
|
||||
|
||||
const getRelationMetadata = useGetRelationMetadata();
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const mutationResponseField = getDeleteManyRecordsMutationResponseField(
|
||||
@ -24,16 +31,10 @@ export const useDeleteManyRecords = ({
|
||||
);
|
||||
|
||||
const deleteManyRecords = async (idsToDelete: string[]) => {
|
||||
const deleteRecordFilter: ObjectRecordQueryFilter = {
|
||||
id: {
|
||||
in: idsToDelete,
|
||||
},
|
||||
};
|
||||
const deletedRecords = await apolloClient.mutate({
|
||||
mutation: deleteManyRecordsMutation,
|
||||
variables: {
|
||||
filter: deleteRecordFilter,
|
||||
// atMost: idsToDelete.length,
|
||||
filter: { id: { in: idsToDelete } },
|
||||
},
|
||||
optimisticResponse: {
|
||||
[mutationResponseField]: idsToDelete.map((idToDelete) => ({
|
||||
@ -46,10 +47,49 @@ export const useDeleteManyRecords = ({
|
||||
|
||||
if (!records?.length) return;
|
||||
|
||||
objectMetadataItem.fields.forEach((fieldMetadataItem) => {
|
||||
const relationMetadata = getRelationMetadata({ fieldMetadataItem });
|
||||
|
||||
if (!relationMetadata) return;
|
||||
|
||||
const { relationObjectMetadataItem, relationFieldMetadataItem } =
|
||||
relationMetadata;
|
||||
|
||||
records.forEach((record) => {
|
||||
const cachedRecord = getRecordFromCache(record.id, cache);
|
||||
|
||||
if (!cachedRecord) return;
|
||||
|
||||
const previousFieldValue:
|
||||
| ObjectRecordConnection
|
||||
| ObjectRecord
|
||||
| null = cachedRecord[fieldMetadataItem.name];
|
||||
|
||||
const relationRecordIds = isObjectRecordConnection(
|
||||
relationObjectMetadataItem.nameSingular,
|
||||
previousFieldValue,
|
||||
)
|
||||
? previousFieldValue.edges.map(({ node }) => node.id)
|
||||
: [previousFieldValue?.id].filter(isDefined);
|
||||
|
||||
relationRecordIds.forEach((relationRecordId) =>
|
||||
triggerDetachRelationOptimisticEffect({
|
||||
cache,
|
||||
objectNameSingular,
|
||||
recordId: record.id,
|
||||
relationObjectMetadataNameSingular:
|
||||
relationObjectMetadataItem.nameSingular,
|
||||
relationFieldName: relationFieldMetadataItem.name,
|
||||
relationRecordId,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
triggerDeleteRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
records,
|
||||
records: records,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { isObjectRecordConnection } from '@/apollo/optimistic-effect/utils/isObjectRecordConnection';
|
||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
||||
import { triggerDetachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDetachRelationOptimisticEffect';
|
||||
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||
import { getDeleteOneRecordMutationResponseField } from '@/object-record/utils/generateDeleteOneRecordMutation';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
type useDeleteOneRecordProps = {
|
||||
@ -14,9 +20,10 @@ type useDeleteOneRecordProps = {
|
||||
export const useDeleteOneRecord = ({
|
||||
objectNameSingular,
|
||||
}: useDeleteOneRecordProps) => {
|
||||
const { objectMetadataItem, deleteOneRecordMutation } = useObjectMetadataItem(
|
||||
{ objectNameSingular },
|
||||
);
|
||||
const { objectMetadataItem, deleteOneRecordMutation, getRecordFromCache } =
|
||||
useObjectMetadataItem({ objectNameSingular });
|
||||
|
||||
const getRelationMetadata = useGetRelationMetadata();
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
@ -39,6 +46,43 @@ export const useDeleteOneRecord = ({
|
||||
|
||||
if (!record) return;
|
||||
|
||||
objectMetadataItem.fields.forEach((fieldMetadataItem) => {
|
||||
const relationMetadata = getRelationMetadata({ fieldMetadataItem });
|
||||
|
||||
if (!relationMetadata) return;
|
||||
|
||||
const { relationObjectMetadataItem, relationFieldMetadataItem } =
|
||||
relationMetadata;
|
||||
|
||||
const cachedRecord = getRecordFromCache(record.id, cache);
|
||||
|
||||
if (!cachedRecord) return;
|
||||
|
||||
const previousFieldValue:
|
||||
| ObjectRecordConnection
|
||||
| ObjectRecord
|
||||
| null = cachedRecord[fieldMetadataItem.name];
|
||||
|
||||
const relationRecordIds = isObjectRecordConnection(
|
||||
relationObjectMetadataItem.nameSingular,
|
||||
previousFieldValue,
|
||||
)
|
||||
? previousFieldValue.edges.map(({ node }) => node.id)
|
||||
: [previousFieldValue?.id].filter(isDefined);
|
||||
|
||||
relationRecordIds.forEach((relationRecordId) =>
|
||||
triggerDetachRelationOptimisticEffect({
|
||||
cache,
|
||||
objectNameSingular,
|
||||
recordId: record.id,
|
||||
relationObjectMetadataNameSingular:
|
||||
relationObjectMetadataItem.nameSingular,
|
||||
relationFieldName: relationFieldMetadataItem.name,
|
||||
relationRecordId,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
triggerDeleteRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
@ -52,6 +96,8 @@ export const useDeleteOneRecord = ({
|
||||
[
|
||||
apolloClient,
|
||||
deleteOneRecordMutation,
|
||||
getRecordFromCache,
|
||||
getRelationMetadata,
|
||||
mutationResponseField,
|
||||
objectMetadataItem,
|
||||
objectNameSingular,
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { useGetRelationFieldsToOptimisticallyUpdate } from '@/apollo/optimistic-effect/hooks/useGetRelationFieldsToOptimisticallyUpdate';
|
||||
import { isObjectRecordConnection } from '@/apollo/optimistic-effect/utils/isObjectRecordConnection';
|
||||
import { triggerAttachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerAttachRelationOptimisticEffect';
|
||||
import { triggerDetachRelationOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDetachRelationOptimisticEffect';
|
||||
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
||||
import { triggerUpdateRelationFieldOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRelationFieldOptimisticEffect';
|
||||
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { getUpdateOneRecordMutationResponseField } from '@/object-record/hooks/useGenerateUpdateOneRecordMutation';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
type useUpdateOneRecordProps = {
|
||||
@ -21,8 +24,7 @@ export const useUpdateOneRecord = <
|
||||
const { objectMetadataItem, updateOneRecordMutation, getRecordFromCache } =
|
||||
useObjectMetadataItem({ objectNameSingular });
|
||||
|
||||
const getRelationFieldsToOptimisticallyUpdate =
|
||||
useGetRelationFieldsToOptimisticallyUpdate();
|
||||
const getRelationMetadata = useGetRelationMetadata();
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
@ -48,14 +50,6 @@ export const useUpdateOneRecord = <
|
||||
id: idToUpdate,
|
||||
};
|
||||
|
||||
const updatedRelationFields = cachedRecord
|
||||
? getRelationFieldsToOptimisticallyUpdate({
|
||||
cachedRecord,
|
||||
objectMetadataItem,
|
||||
updateRecordInput: updateOneRecordInput,
|
||||
})
|
||||
: [];
|
||||
|
||||
const mutationResponseField =
|
||||
getUpdateOneRecordMutationResponseField(objectNameSingular);
|
||||
|
||||
@ -73,29 +67,59 @@ export const useUpdateOneRecord = <
|
||||
|
||||
if (!record) return;
|
||||
|
||||
objectMetadataItem.fields.forEach((fieldMetadataItem) => {
|
||||
const relationMetadata = getRelationMetadata({ fieldMetadataItem });
|
||||
|
||||
if (!relationMetadata) return;
|
||||
|
||||
const { relationObjectMetadataItem, relationFieldMetadataItem } =
|
||||
relationMetadata;
|
||||
|
||||
const previousFieldValue = cachedRecord?.[fieldMetadataItem.name];
|
||||
const nextFieldValue =
|
||||
updateOneRecordInput[fieldMetadataItem.name] ?? null;
|
||||
|
||||
if (
|
||||
!(fieldMetadataItem.name in updateOneRecordInput) ||
|
||||
isObjectRecordConnection(
|
||||
relationObjectMetadataItem.nameSingular,
|
||||
previousFieldValue,
|
||||
) ||
|
||||
isDeeplyEqual(previousFieldValue, nextFieldValue)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousFieldValue) {
|
||||
triggerDetachRelationOptimisticEffect({
|
||||
cache,
|
||||
objectNameSingular,
|
||||
recordId: record.id,
|
||||
relationObjectMetadataNameSingular:
|
||||
relationObjectMetadataItem.nameSingular,
|
||||
relationFieldName: relationFieldMetadataItem.name,
|
||||
relationRecordId: previousFieldValue.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (nextFieldValue) {
|
||||
triggerAttachRelationOptimisticEffect({
|
||||
cache,
|
||||
objectNameSingular,
|
||||
recordId: record.id,
|
||||
relationObjectMetadataNameSingular:
|
||||
relationObjectMetadataItem.nameSingular,
|
||||
relationFieldName: relationFieldMetadataItem.name,
|
||||
relationRecordId: nextFieldValue.id,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
record,
|
||||
});
|
||||
|
||||
updatedRelationFields.forEach(
|
||||
({
|
||||
relationObjectMetadataNameSingular,
|
||||
relationFieldName,
|
||||
previousRelationRecord,
|
||||
nextRelationRecord,
|
||||
}) =>
|
||||
triggerUpdateRelationFieldOptimisticEffect({
|
||||
cache,
|
||||
objectNameSingular,
|
||||
record,
|
||||
relationObjectMetadataNameSingular,
|
||||
relationFieldName,
|
||||
previousRelationRecord,
|
||||
nextRelationRecord,
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user