fix(filters): unify combinedFilter for queries and bulk delete (#13952)

Issue: https://github.com/twentyhq/twenty/issues/13913

Motivation/Problem:
Bulk delete and query paths were composing filters differently, causing
mismatches. In some cases this led to invalid or empty GraphQL filters
(e.g. {"and":[{}]}), breaking delete operations.

Fix:
Unify filter composition (combinedFilter) across queries and bulk
delete, ensuring consistent handling of base filters + soft-deleted
clause. Also adjusted record filter grouping logic to avoid dropping
filters when no groups exist.

Result:
Filtered queries and bulk deletes now behave consistently and reliably
without producing broken filters.

---------

Co-authored-by: root <root@DESKTOP-E2VOJGE>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Puranjay Mishra
2025-08-18 06:01:47 -04:00
committed by GitHub
parent 1598e5590a
commit 483c1e2214
16 changed files with 185 additions and 53 deletions
@@ -20,13 +20,14 @@ describe('computeContextStoreFilters', () => {
selectedRecordIds: ['1', '2', '3'],
};
const filters = computeContextStoreFilters(
const filters = computeContextStoreFilters({
contextStoreTargetedRecordsRule,
[],
personObjectMetadataItem,
mockFilterValueDependencies,
'',
);
contextStoreFilters: [],
contextStoreFilterGroups: [],
objectMetadataItem: personObjectMetadataItem,
filterValueDependencies: mockFilterValueDependencies,
contextStoreAnyFieldFilterValue: '',
});
expect(filters).toEqual({
and: [
@@ -63,13 +64,14 @@ describe('computeContextStoreFilters', () => {
},
];
const filters = computeContextStoreFilters(
const filters = computeContextStoreFilters({
contextStoreTargetedRecordsRule,
contextStoreFilters,
personObjectMetadataItem,
mockFilterValueDependencies,
'',
);
contextStoreFilterGroups: [],
objectMetadataItem: personObjectMetadataItem,
filterValueDependencies: mockFilterValueDependencies,
contextStoreAnyFieldFilterValue: '',
});
expect(filters).toEqual({
and: [
@@ -1,24 +1,35 @@
import { type ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { type RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { type RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { type RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
import { turnAnyFieldFilterIntoRecordGqlFilter } from '@/object-record/record-filter/utils/turnAnyFieldFilterIntoRecordGqlFilter';
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
export const computeContextStoreFilters = (
contextStoreTargetedRecordsRule: ContextStoreTargetedRecordsRule,
contextStoreFilters: RecordFilter[],
objectMetadataItem: ObjectMetadataItem,
filterValueDependencies: RecordFilterValueDependencies,
anyFieldFilterValue: string,
) => {
type ComputeContextStoreFiltersProps = {
contextStoreTargetedRecordsRule: ContextStoreTargetedRecordsRule;
contextStoreFilters: RecordFilter[];
contextStoreFilterGroups: RecordFilterGroup[];
objectMetadataItem: ObjectMetadataItem;
filterValueDependencies: RecordFilterValueDependencies;
contextStoreAnyFieldFilterValue: string;
};
export const computeContextStoreFilters = ({
contextStoreTargetedRecordsRule,
contextStoreFilters,
contextStoreFilterGroups,
objectMetadataItem,
filterValueDependencies,
contextStoreAnyFieldFilterValue,
}: ComputeContextStoreFiltersProps) => {
let queryFilter: RecordGqlOperationFilter | undefined;
const { recordGqlOperationFilter: recordGqlFilterForAnyFieldFilter } =
turnAnyFieldFilterIntoRecordGqlFilter({
filterValue: anyFieldFilterValue,
filterValue: contextStoreAnyFieldFilterValue,
objectMetadataItem,
});
@@ -29,7 +40,7 @@ export const computeContextStoreFilters = (
filterValueDependencies,
fields: objectMetadataItem?.fields ?? [],
recordFilters: contextStoreFilters,
recordFilterGroups: [],
recordFilterGroups: contextStoreFilterGroups,
}),
contextStoreTargetedRecordsRule.excludedRecordIds.length > 0
? {
@@ -56,7 +67,7 @@ export const computeContextStoreFilters = (
filterValueDependencies,
fields: objectMetadataItem?.fields ?? [],
recordFilters: contextStoreFilters,
recordFilterGroups: [],
recordFilterGroups: contextStoreFilterGroups,
}),
]);
}