diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/__tests__/graphql-query-filter-condition.parser.spec.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/__tests__/graphql-query-filter-condition.parser.spec.ts index 74836b32f5..f2216e36e6 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/__tests__/graphql-query-filter-condition.parser.spec.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/__tests__/graphql-query-filter-condition.parser.spec.ts @@ -1,4 +1,7 @@ -import { createWhereExpressionRecorder } from 'test/utils/create-where-expression-recorder.util'; +import { + createWhereExpressionRecorder, + type RecordedWhereCall, +} from 'test/utils/create-where-expression-recorder.util'; import { FieldMetadataType } from 'twenty-shared/types'; import { type ObjectLiteral } from 'typeorm'; @@ -74,6 +77,21 @@ const recordFilterEntries = (filter: Record) => { return recorder.calls; }; +const withNormalizedParameterKeys = (calls: RecordedWhereCall[]): unknown[] => + calls.map(({ method, node }) => + node.kind === 'sql' + ? { + method, + sql: node.sql.replace(/(? { describe('applyFilterEntriesToWhereBrackets', () => { it('emits the first entry with where and later entries with andWhere', () => { @@ -170,6 +188,38 @@ describe('GraphqlQueryFilterConditionParser', () => { ]); }); + it('applies an or group that is not wrapped in an array', () => { + const calls = recordFilterEntries({ or: { name: { ilike: '%acme%' } } }); + + expect(withNormalizedParameterKeys(calls)).toEqual( + withNormalizedParameterKeys( + recordFilterEntries({ or: [{ name: { ilike: '%acme%' } }] }), + ), + ); + expect(calls).toEqual([ + { + method: 'where', + node: { + kind: 'brackets', + children: [ + { + method: 'where', + node: { + kind: 'brackets', + children: [ + { + method: 'where', + node: expect.objectContaining({ kind: 'sql' }), + }, + ], + }, + }, + ], + }, + }, + ]); + }); + it('emits a not group as notBrackets', () => { const calls = recordFilterEntries({ not: { name: { ilike: '%a%' } } }); diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/utils/apply-filter-entries-to-where-expression.util.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/utils/apply-filter-entries-to-where-expression.util.ts index 15864b5817..024d58a3ab 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/utils/apply-filter-entries-to-where-expression.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/utils/apply-filter-entries-to-where-expression.util.ts @@ -94,13 +94,15 @@ const applyFilterEntry = ( const applyLogicalGroup = ( whereExpression: WhereExpressionBuilder, - filters: Record[], + filters: Record[] | Record, logicalOperator: 'and' | 'or', isFirst: boolean, context: FilterWalkContext, ): void => { + const filterList = Array.isArray(filters) ? filters : [filters]; + const groupCondition = new Brackets((groupWhereExpression) => { - filters.forEach((filter, index) => { + filterList.forEach((filter, index) => { const elementCondition = new Brackets((elementWhereExpression) => { applyFilterEntries(elementWhereExpression, filter, context); });