fix rest filter default conjunction detection (#20133)

Fixes #20128 

## Summary

Fix REST API filter parsing when bare filters are mixed with explicit
conjunctions.

## What changed

- Replaced the loose parentheses check in
`addDefaultConjunctionIfMissing` with proper root conjunction detection.
- Shared the root conjunction regex with `parseFilter`.
- Added regression tests for mixed filters like
`status[eq]:'TODO',and(title[ilike]:'%test%')`.

## Validation

- `npx nx test twenty-server
--testPathPatterns=add-default-conjunction.util.spec.ts --runInBand
--coverage=false`
- `npx prettier --check ...`
This commit is contained in:
Sri Hari Haran Sharma
2026-05-04 16:18:56 +00:00
committed by GitHub
parent c804f27846
commit 281eaa3721
3 changed files with 30 additions and 5 deletions
@@ -12,4 +12,24 @@ describe('addDefaultConjunctionIfMissing', () => {
'and(field[eq]:1)',
);
});
it('should add default conjunction when a bare filter is mixed with a nested conjunction', () => {
expect(
addDefaultConjunctionIfMissing(
"status[eq]:'TODO',and(title[ilike]:'%test%')",
),
).toEqual("and(status[eq]:'TODO',and(title[ilike]:'%test%'))");
});
it('should not add default conjunction for root or conjunction', () => {
expect(addDefaultConjunctionIfMissing('or(field[eq]:1)')).toEqual(
'or(field[eq]:1)',
);
});
it('should not add default conjunction for root not conjunction', () => {
expect(addDefaultConjunctionIfMissing('not(field[eq]:1)')).toEqual(
'not(field[eq]:1)',
);
});
});
@@ -1,9 +1,12 @@
import { Conjunctions } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter.util';
import {
Conjunctions,
ROOT_FILTER_CONJUNCTION_REGEX,
} from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter.util';
export const DEFAULT_CONJUNCTION = Conjunctions.and;
export const addDefaultConjunctionIfMissing = (filterQuery: string): string => {
if (!(filterQuery.includes('(') && filterQuery.includes(')'))) {
if (!ROOT_FILTER_CONJUNCTION_REGEX.test(filterQuery)) {
return `${DEFAULT_CONJUNCTION}(${filterQuery})`;
}
@@ -17,13 +17,15 @@ export enum Conjunctions {
not = 'not',
}
export const ROOT_FILTER_CONJUNCTION_REGEX = new RegExp(
`^(${Object.values(Conjunctions).join('|')})\\((.+)\\)$`,
);
export const parseFilter = (
filterQuery: string,
): Record<string, FieldValue> => {
const result = {};
const match = filterQuery.match(
`^(${Object.values(Conjunctions).join('|')})\\((.+)\\)$`,
);
const match = filterQuery.match(ROOT_FILTER_CONJUNCTION_REGEX);
if (match) {
const conjunction = match?.[1];