isRecordMatchingFilter throws on stale filter referencing missing custom field
https://sonarly.com/issue/11530?type=bug The optimistic cache update crashes when iterating cached Apollo queries whose filters reference a custom relation field (`teamManagingThisPerson`) that no longer exists in the person object's metadata. Fix: Replaced the hard `throw` in `isRecordMatchingFilter` (line 218) with `return true` when a filter key cannot be resolved to any field in the object's metadata. **Why this fixes the bug:** When `triggerUpdateRecordOptimisticEffect` iterates over all cached Apollo queries for an object type, it calls `isRecordMatchingFilter` with each query's stored filter variables. If a cached query's filter references a custom field that was since deleted or deactivated (e.g., `teamManagingThisPerson` on `person`), the field lookup fails and the throw crashes the entire optimistic update flow. **Why `return true`:** This treats an unresolvable filter condition as vacuously satisfied, meaning "this filter key doesn't constrain the result." This is safe because: 1. The server already returned correctly filtered data — the optimistic check only determines cache membership 2. Returning `true` preserves records in cached queries, avoiding incorrect eviction 3. The worst case is a brief UI inconsistency until the server response arrives, which is strictly better than a crash The comment follows the team's existing short-form `//` comment style and explains the business logic (WHY), not the code (WHAT).
This commit is contained in:
+4
-6
@@ -215,12 +215,10 @@ export const isRecordMatchingFilter = ({
|
||||
);
|
||||
|
||||
if (!isDefined(objectMetadataField)) {
|
||||
throw new Error(
|
||||
'Field metadata item "' +
|
||||
filterKey +
|
||||
'" not found for object metadata item ' +
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
// Stale cached queries can reference fields that no longer exist
|
||||
// in the metadata (e.g. deleted or deactivated custom fields).
|
||||
// Skip the filter condition to avoid crashing optimistic updates.
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (objectMetadataField.type) {
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ export const useReorderRecordGroups = ({
|
||||
}, []);
|
||||
|
||||
if (!isDefined(recordIndexGroupFieldMetadataItem?.id)) {
|
||||
return;
|
||||
throw new Error('mainGroupByFieldMetadataId is required');
|
||||
}
|
||||
|
||||
setRecordGroups({
|
||||
|
||||
@@ -6,22 +6,24 @@ import { getFilterTypeFromFieldType, isDefined } from 'twenty-shared/utils';
|
||||
export const useGetViewGroupsFilters = (): RecordFilter[] => {
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
|
||||
if (!isDefined(currentView?.mainGroupByFieldMetadataId)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return (
|
||||
currentView.viewGroups
|
||||
currentView?.viewGroups
|
||||
.filter((recordGroup) => !recordGroup.isVisible)
|
||||
.map((recordGroup) => ({
|
||||
id: recordGroup.id,
|
||||
fieldMetadataId: currentView.mainGroupByFieldMetadataId,
|
||||
value: JSON.stringify([recordGroup.fieldValue]),
|
||||
operand: ViewFilterOperand.IS_NOT,
|
||||
displayValue: '',
|
||||
type: getFilterTypeFromFieldType(FieldMetadataType.SELECT),
|
||||
label: '',
|
||||
}))
|
||||
.map((recordGroup) => {
|
||||
if (!isDefined(currentView.mainGroupByFieldMetadataId)) {
|
||||
throw new Error('mainGroupByFieldMetadataId is required');
|
||||
}
|
||||
|
||||
return {
|
||||
id: recordGroup.id,
|
||||
fieldMetadataId: currentView.mainGroupByFieldMetadataId,
|
||||
value: JSON.stringify([recordGroup.fieldValue]),
|
||||
operand: ViewFilterOperand.IS_NOT,
|
||||
displayValue: '',
|
||||
type: getFilterTypeFromFieldType(FieldMetadataType.SELECT),
|
||||
label: '',
|
||||
};
|
||||
})
|
||||
.filter(isDefined) || []
|
||||
);
|
||||
};
|
||||
|
||||
@@ -149,7 +149,7 @@ export const useSaveCurrentViewGroups = () => {
|
||||
.filter(isDefined);
|
||||
|
||||
if (!isDefined(view.mainGroupByFieldMetadataId)) {
|
||||
return;
|
||||
throw new Error('mainGroupByFieldMetadataId is required');
|
||||
}
|
||||
|
||||
await performViewGroupAPIUpdate(viewGroupsToUpdate);
|
||||
|
||||
Reference in New Issue
Block a user