refactor(twenty-shared): split turnRecordFilterIntoGqlOperationFilter into dispatcher + direct builder (#20600)

Stacked on top of #20533 — merge that one first.

## Summary

Splits `turnRecordFilterIntoRecordGqlOperationFilter` into a thin
dispatcher and a private `buildDirectFieldGqlOperationFilter`. Same
observable behaviour, but:

- The two ordering-constraint comments that #20533 introduced go away
(\"must run before the emptiness shortcut\", \"drop rather than fall
through to legacy relation-by-record\"). They were both flagging a real
code smell: dispatch and direct-filter logic were interleaved in the
same function body and depended on a flag inside the input.
- Removes the self-recursion + \"inject target into fieldMetadataItems\"
hack.
- Removes three duplicated `fieldMetadataItems.find(...)` lookups that
only existed to compose error-message labels — the resolved
`fieldMetadataItem` was already in scope.
- Renames `correspondingFieldMetadataItem` → `fieldMetadataItem` inside
the extracted function (the variable IS the field; the longer prefix
predated the split).

## Why

Discussed in #20533: the conditional ordering between the
relation-traversal branch and the per-type switch was easy to get wrong,
and the legacy `case 'RELATION':` foot-gun (parsing the filter value as
a UUID list when it's actually a text value) was guarded by a defensive
`return` rather than by structure.

After this PR the dispatcher picks exactly one branch up front and
`buildDirectFieldGqlOperationFilter` only ever runs against the field
the filter operates on — no \"is this the source field or the target
field?\" ambiguity inside the switch.

## Test plan
- [x] `npx nx build twenty-shared` succeeds (no type regressions).
- [x] `jest packages/twenty-shared` — 1212 tests pass.
- [x] `jest view-query-params.service.spec.ts` — 7 tests pass, including
the relation-traversal round-trip case.
- [ ] CI green.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
Félix Malfait
2026-05-15 14:04:46 +02:00
committed by GitHub
parent e0e36a8f00
commit 4f707bb9f1
2 changed files with 225 additions and 136 deletions

View File

@@ -919,4 +919,92 @@ describe('turnRecordFilterIntoRecordGqlOperationFilter', () => {
expect(result).toHaveProperty('recordId.in');
});
});
describe('relation traversal', () => {
// The dispatcher should wrap the inner filter under the relation source
// field's GraphQL key and build it against the target field's type
// (not the relation FK's type).
it('should nest filter under source field name when target field is set', () => {
const result = turnRecordFilterIntoRecordGqlOperationFilter({
filterValueDependencies,
recordFilter: {
...makeFilter('f-relation', RecordFilterOperand.CONTAINS, 'Acme'),
relationTargetFieldMetadataId: 'f-text',
} as RecordFilter,
fieldMetadataItems: fields,
});
expect(result).toEqual({ company: { name: { ilike: '%Acme%' } } });
});
// If the target field is no longer in fieldMetadataItems (e.g. it was
// deleted from the workspace), dropping the filter is the safe path —
// the alternative would silently interpret the text value as a UUID
// list against the relation FK.
it('should return undefined when target field is not found', () => {
const result = turnRecordFilterIntoRecordGqlOperationFilter({
filterValueDependencies,
recordFilter: {
...makeFilter('f-relation', RecordFilterOperand.CONTAINS, 'Acme'),
relationTargetFieldMetadataId: 'nonexistent-target',
} as RecordFilter,
fieldMetadataItems: fields,
});
expect(result).toBeUndefined();
});
// Emptiness operands must check the target column on the related
// object rather than the FK column on the source record.
it('should apply IS_EMPTY against the target field, not the relation FK', () => {
const result = turnRecordFilterIntoRecordGqlOperationFilter({
filterValueDependencies,
recordFilter: {
...makeFilter('f-relation', RecordFilterOperand.IS_EMPTY, ''),
relationTargetFieldMetadataId: 'f-text',
} as RecordFilter,
fieldMetadataItems: fields,
});
// Without traversal, the RELATION case would have produced a filter
// on `companyId`; here we expect the emptiness check to fall on
// `company.name` instead.
expect(result).toEqual({
company: { or: [{ name: { ilike: '' } }, { name: { is: 'NULL' } }] },
});
});
// The target field's per-type switch must run, so number/select/etc.
// targets behave like a direct filter on that field — verified here
// with a SELECT target.
it('should dispatch to target type switch (SELECT target)', () => {
const result = turnRecordFilterIntoRecordGqlOperationFilter({
filterValueDependencies,
recordFilter: {
...makeFilter('f-relation', RecordFilterOperand.IS, '["ACTIVE"]'),
relationTargetFieldMetadataId: 'f-select',
} as RecordFilter,
fieldMetadataItems: fields,
});
expect(result).toEqual({ company: { status: { in: ['ACTIVE'] } } });
});
// Without a relationTargetFieldMetadataId the dispatcher must fall
// through to the direct builder — preserving the filter-by-record-id
// behaviour on the relation FK.
it('should keep relation filter-by-id behaviour when no target field is set', () => {
const result = turnRecordFilterIntoRecordGqlOperationFilter({
filterValueDependencies,
recordFilter: makeFilter(
'f-relation',
RecordFilterOperand.IS,
'["550e8400-e29b-41d4-a716-446655440000"]',
),
fieldMetadataItems: fields,
});
expect(result).toHaveProperty('companyId.in');
});
});
});

View File

@@ -75,11 +75,11 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
}: TurnRecordFilterIntoRecordGqlOperationFilterParams):
| RecordGqlOperationFilter
| undefined => {
const correspondingFieldMetadataItem = fieldMetadataItems.find(
const sourceFieldMetadataItem = fieldMetadataItems.find(
(field) => field.id === recordFilter.fieldMetadataId,
);
if (!isDefined(correspondingFieldMetadataItem)) {
if (!isDefined(sourceFieldMetadataItem)) {
return;
}
@@ -87,13 +87,8 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return;
}
// Must run before the emptiness shortcut so an "is empty" filter on
// `company.name` is evaluated against `Company.name`, not the FK column.
// If the target field can't be resolved the filter is dropped rather than
// fed to the per-type switch as a RELATION (which would parse `value` as
// a UUID list and silently mishandle target-field values like "Acme").
if (
correspondingFieldMetadataItem.type === FieldMetadataType.RELATION &&
sourceFieldMetadataItem.type === FieldMetadataType.RELATION &&
isDefined(recordFilter.relationTargetFieldMetadataId)
) {
const targetFieldMetadataItem = fieldMetadataItems.find(
@@ -104,13 +99,13 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return;
}
const innerFilter = turnRecordFilterIntoRecordGqlOperationFilter({
const innerFilter = buildDirectFieldGqlOperationFilter({
recordFilter: {
...recordFilter,
fieldMetadataId: targetFieldMetadataItem.id,
relationTargetFieldMetadataId: null,
},
fieldMetadataItems,
fieldMetadataItem: targetFieldMetadataItem,
filterValueDependencies,
});
@@ -119,19 +114,39 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
}
return {
[correspondingFieldMetadataItem.name]: innerFilter,
[sourceFieldMetadataItem.name]: innerFilter,
} as RecordGqlOperationFilter;
}
return buildDirectFieldGqlOperationFilter({
recordFilter,
fieldMetadataItem: sourceFieldMetadataItem,
filterValueDependencies,
});
};
type BuildDirectFieldGqlOperationFilterParams = {
filterValueDependencies: RecordFilterValueDependencies;
recordFilter: Omit<RecordFilter, 'id'>;
fieldMetadataItem: FieldShared;
};
const buildDirectFieldGqlOperationFilter = ({
recordFilter,
fieldMetadataItem,
filterValueDependencies,
}: BuildDirectFieldGqlOperationFilterParams):
| RecordGqlOperationFilter
| undefined => {
const shouldComputeEmptinessFilter = checkIfShouldComputeEmptinessFilter({
recordFilterOperand: recordFilter.operand,
correspondingFieldMetadataItem,
correspondingFieldMetadataItem: fieldMetadataItem,
});
if (shouldComputeEmptinessFilter) {
const emptinessFilter = getEmptyRecordGqlOperationFilter({
operand: recordFilter.operand,
correspondingField: correspondingFieldMetadataItem,
correspondingField: fieldMetadataItem,
recordFilter: recordFilter,
});
@@ -142,23 +157,21 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
const isSubFieldFilter = isNonEmptyString(subFieldName);
const filterType = getFilterTypeFromFieldType(
correspondingFieldMetadataItem.type,
);
const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type);
switch (filterType) {
case 'TEXT':
switch (recordFilter.operand) {
case RecordFilterOperand.CONTAINS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
ilike: `%${recordFilter.value}%`,
} as StringFilter,
};
case RecordFilterOperand.DOES_NOT_CONTAIN:
return {
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
ilike: `%${recordFilter.value}%`,
} as StringFilter,
},
@@ -173,7 +186,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.VECTOR_SEARCH:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
search: recordFilter.value,
} as TSVectorFilter,
};
@@ -186,14 +199,14 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.CONTAINS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
like: `%${recordFilter.value}%`,
} as RawJsonFilter,
};
case RecordFilterOperand.DOES_NOT_CONTAIN:
return {
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
like: `%${recordFilter.value}%`,
} as RawJsonFilter,
},
@@ -207,14 +220,14 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.CONTAINS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
like: `%${recordFilter.value}%`,
} as FilesFilter,
};
case RecordFilterOperand.DOES_NOT_CONTAIN:
return {
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
like: `%${recordFilter.value}%`,
} as FilesFilter,
},
@@ -251,12 +264,12 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return {
and: [
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
gte: start,
} as DateFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
lt: end,
} as DateFilter,
},
@@ -277,19 +290,19 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.IS_IN_PAST:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
lt: nowAsPlainDate,
} as DateFilter,
};
case RecordFilterOperand.IS_IN_FUTURE:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
gte: nowAsPlainDate,
} as DateFilter,
};
case RecordFilterOperand.IS_TODAY: {
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
eq: nowAsPlainDate,
} as DateFilter,
};
@@ -301,14 +314,14 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.IS_AFTER: {
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
gte: plainDateFilter,
} as DateFilter,
};
}
case RecordFilterOperand.IS_BEFORE: {
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
lt: plainDateFilter,
} as DateFilter,
};
@@ -316,7 +329,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
case RecordFilterOperand.IS: {
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
eq: plainDateFilter,
} as DateFilter,
};
@@ -367,12 +380,12 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return {
and: [
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
gte: start.toInstant().toString(),
} as DateTimeFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
lt: end.toInstant().toString(),
} as DateTimeFilter,
},
@@ -393,13 +406,13 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.IS_IN_PAST:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
lt: now.toInstant().round('minute').toString(),
} as DateTimeFilter,
};
case RecordFilterOperand.IS_IN_FUTURE:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
gt: now.toInstant().round('minute').toString(),
} as DateTimeFilter,
};
@@ -407,12 +420,12 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return {
and: [
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
gte: getPeriodStart(now, 'DAY').toInstant().toString(),
} as DateTimeFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
lt: getNextPeriodStart(now, 'DAY').toInstant().toString(),
} as DateTimeFilter,
},
@@ -449,12 +462,12 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return {
and: [
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
gte: start.toString(),
} as DateTimeFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
lt: end.toString(),
} as DateTimeFilter,
},
@@ -467,14 +480,14 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.IS_AFTER: {
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
gte: resolvedDateTime.toString(),
} as DateTimeFilter,
};
}
case RecordFilterOperand.IS_BEFORE: {
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
lt: resolvedDateTime.toString(),
} as DateTimeFilter,
};
@@ -490,13 +503,13 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.IS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
eq: convertRatingToRatingValue(parseFloat(recordFilter.value)),
} as RatingFilter,
};
case RecordFilterOperand.GREATER_THAN_OR_EQUAL:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
in: convertGreaterThanOrEqualRatingToArrayOfRatingValues(
parseFloat(recordFilter.value),
),
@@ -504,7 +517,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
};
case RecordFilterOperand.LESS_THAN_OR_EQUAL:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
in: convertLessThanOrEqualRatingToArrayOfRatingValues(
parseFloat(recordFilter.value),
),
@@ -519,26 +532,26 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.GREATER_THAN_OR_EQUAL:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
gte: parseFloat(recordFilter.value),
} as FloatFilter,
};
case RecordFilterOperand.LESS_THAN_OR_EQUAL:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
lte: parseFloat(recordFilter.value),
} as FloatFilter,
};
case RecordFilterOperand.IS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
eq: parseFloat(recordFilter.value),
} as FloatFilter,
};
case RecordFilterOperand.IS_NOT:
return {
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
eq: parseFloat(recordFilter.value),
} as FloatFilter,
},
@@ -571,7 +584,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.IS:
return {
[correspondingFieldMetadataItem.name + 'Id']: {
[fieldMetadataItem.name + 'Id']: {
in: recordIds,
} as RelationFilter,
};
@@ -581,13 +594,13 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name + 'Id']: {
[fieldMetadataItem.name + 'Id']: {
in: recordIds,
} as RelationFilter,
},
},
{
[correspondingFieldMetadataItem.name + 'Id']: {
[fieldMetadataItem.name + 'Id']: {
is: 'NULL',
} as RelationFilter,
},
@@ -615,7 +628,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
if (parsedCurrencyCodes.length === 0) return undefined;
const gqlFilter: RecordGqlOperationFilter = {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
currencyCode: { in: parsedCurrencyCodes },
} as CurrencyFilter,
};
@@ -643,26 +656,26 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.GREATER_THAN_OR_EQUAL:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
amountMicros: { gte: parseFloat(recordFilter.value) * 1000000 },
} as CurrencyFilter,
};
case RecordFilterOperand.LESS_THAN_OR_EQUAL:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
amountMicros: { lte: parseFloat(recordFilter.value) * 1000000 },
} as CurrencyFilter,
};
case RecordFilterOperand.IS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
amountMicros: { eq: parseFloat(recordFilter.value) * 1000000 },
} as CurrencyFilter,
};
case RecordFilterOperand.IS_NOT:
return {
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
amountMicros: {
eq: parseFloat(recordFilter.value) * 1000000,
},
@@ -682,7 +695,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
}
case 'LINKS': {
return computeGqlOperationFilterForLinks({
correspondingFieldMetadataItem,
correspondingFieldMetadataItem: fieldMetadataItem,
recordFilter,
subFieldName,
});
@@ -690,7 +703,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
case 'FULL_NAME': {
const fullNameFilters = generateILikeFiltersForCompositeFields(
recordFilter.value,
correspondingFieldMetadataItem.name,
fieldMetadataItem.name,
['firstName', 'lastName'],
);
switch (recordFilter.operand) {
@@ -701,7 +714,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
};
} else {
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
[subFieldName]: {
ilike: `%${recordFilter.value}%`,
},
@@ -720,7 +733,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
} else {
return {
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
[subFieldName]: {
ilike: `%${recordFilter.value}%`,
},
@@ -741,42 +754,42 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return {
or: [
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressStreet1: {
ilike: `%${recordFilter.value}%`,
},
} as AddressFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressStreet2: {
ilike: `%${recordFilter.value}%`,
},
} as AddressFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressCity: {
ilike: `%${recordFilter.value}%`,
},
} as AddressFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressState: {
ilike: `%${recordFilter.value}%`,
},
} as AddressFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressCountry: {
ilike: `%${recordFilter.value}%`,
},
} as AddressFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressPostcode: {
ilike: `%${recordFilter.value}%`,
},
@@ -795,7 +808,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
}
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
[subFieldName]: {
in: parsedCountryCodes,
} as AddressFilter,
@@ -804,7 +817,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
}
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
[subFieldName]: {
ilike: `%${recordFilter.value}%`,
} as AddressFilter,
@@ -819,7 +832,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressStreet1: {
ilike: `%${recordFilter.value}%`,
},
@@ -827,7 +840,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressStreet1: {
is: 'NULL',
},
@@ -839,7 +852,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressStreet2: {
ilike: `%${recordFilter.value}%`,
},
@@ -847,7 +860,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressStreet2: {
is: 'NULL',
},
@@ -859,7 +872,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressCity: {
ilike: `%${recordFilter.value}%`,
},
@@ -867,7 +880,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressCity: {
is: 'NULL',
},
@@ -879,7 +892,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressState: {
ilike: `%${recordFilter.value}%`,
},
@@ -887,7 +900,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressState: {
is: 'NULL',
},
@@ -899,7 +912,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressPostcode: {
ilike: `%${recordFilter.value}%`,
},
@@ -907,7 +920,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressPostcode: {
is: 'NULL',
},
@@ -919,7 +932,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressCountry: {
ilike: `%${recordFilter.value}%`,
},
@@ -927,7 +940,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressCountry: {
is: 'NULL',
},
@@ -954,7 +967,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressCountry: {
in: JSON.parse(recordFilter.value),
} as AddressFilter,
@@ -962,7 +975,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
addressCountry: {
is: 'NULL',
} as AddressFilter,
@@ -976,7 +989,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
[subFieldName]: {
ilike: `%${recordFilter.value}%`,
} as AddressFilter,
@@ -984,7 +997,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
[subFieldName]: {
is: 'NULL',
} as AddressFilter,
@@ -1012,7 +1025,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
if (nonEmptyOptions.length > 0) {
conditions.push({
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
containsAny: nonEmptyOptions,
} as MultiSelectFilter,
});
@@ -1020,7 +1033,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
if (emptyOptions.length > 0) {
conditions.push({
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
isEmptyArray: true,
} as MultiSelectFilter,
});
@@ -1033,18 +1046,18 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
containsAny: nonEmptyOptions,
} as MultiSelectFilter,
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
isEmptyArray: true,
} as MultiSelectFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
is: 'NULL',
} as MultiSelectFilter,
},
@@ -1070,7 +1083,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
if (nonEmptyOptions.length > 0) {
conditions.push({
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
in: nonEmptyOptions,
} as SelectFilter,
});
@@ -1078,7 +1091,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
if (emptyOptions.length > 0) {
conditions.push({
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
is: 'NULL',
} as SelectFilter,
});
@@ -1092,7 +1105,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
if (nonEmptyOptions.length > 0) {
conditions.push({
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
in: nonEmptyOptions,
} as SelectFilter,
},
@@ -1102,7 +1115,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
if (emptyOptions.length > 0) {
conditions.push({
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
is: 'NULL',
} as SelectFilter,
},
@@ -1121,14 +1134,14 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.CONTAINS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
containsIlike: `%${recordFilter.value}%`,
} as ArrayFilter,
};
case RecordFilterOperand.DOES_NOT_CONTAIN:
return {
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
containsIlike: `%${recordFilter.value}%`,
} as ArrayFilter,
},
@@ -1150,7 +1163,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
const parsedSources = JSON.parse(recordFilter.value) as string[];
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
source: {
in: parsedSources,
} satisfies RelationFilter,
@@ -1168,7 +1181,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return {
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
source: {
in: parsedSources,
} satisfies RelationFilter,
@@ -1177,12 +1190,8 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
};
}
default: {
const fieldForRecordFilter = fieldMetadataItems.find(
(field) => field.id === recordFilter.fieldMetadataId,
);
throw new Error(
`Unknown operand ${recordFilter.operand} for ${fieldForRecordFilter?.label ?? ''} filter`,
`Unknown operand ${recordFilter.operand} for ${fieldMetadataItem.label} filter`,
);
}
}
@@ -1213,7 +1222,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.IS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
workspaceMemberId: {
in: workspaceMemberIds,
} satisfies UUIDFilter,
@@ -1224,7 +1233,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
workspaceMemberId: {
in: workspaceMemberIds,
} satisfies UUIDFilter,
@@ -1232,7 +1241,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
workspaceMemberId: {
is: 'NULL',
} satisfies UUIDFilter,
@@ -1242,12 +1251,8 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
};
}
default: {
const fieldForRecordFilter = fieldMetadataItems.find(
(field) => field.id === recordFilter.fieldMetadataId,
);
throw new Error(
`Unknown operand ${recordFilter.operand} for ${fieldForRecordFilter?.label ?? ''} filter`,
`Unknown operand ${recordFilter.operand} for ${fieldMetadataItem.label} filter`,
);
}
}
@@ -1263,7 +1268,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return {
or: [
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
name: {
ilike: `%${recordFilter.value}%`,
},
@@ -1272,7 +1277,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
...(matchingSourceValues.length > 0
? [
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
source: {
in: matchingSourceValues,
},
@@ -1288,7 +1293,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
and: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
name: {
ilike: `%${recordFilter.value}%`,
},
@@ -1299,7 +1304,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
? [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
source: {
in: matchingSourceValues,
},
@@ -1312,19 +1317,15 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
};
}
default: {
const fieldForRecordFilter = fieldMetadataItems.find(
(field) => field.id === recordFilter.fieldMetadataId,
);
throw new Error(
`Unknown operand ${recordFilter.operand} for ${fieldForRecordFilter?.label ?? ''} filter`,
`Unknown operand ${recordFilter.operand} for ${fieldMetadataItem.label} filter`,
);
}
}
}
case 'EMAILS': {
return computeGqlOperationFilterForEmails({
correspondingFieldMetadataItem,
correspondingFieldMetadataItem: fieldMetadataItem,
recordFilter,
subFieldName,
});
@@ -1342,21 +1343,21 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return {
or: [
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
primaryPhoneNumber: {
ilike: `%${filterValue}%`,
},
} as PhonesFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
primaryPhoneCallingCode: {
ilike: `%${filterValue}%`,
},
} as PhonesFilter,
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
additionalPhones: {
like: `%${filterValue}%`,
},
@@ -1369,7 +1370,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
and: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
primaryPhoneNumber: {
ilike: `%${filterValue}%`,
},
@@ -1378,7 +1379,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
primaryPhoneCallingCode: {
ilike: `%${filterValue}%`,
},
@@ -1389,7 +1390,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
additionalPhones: {
like: `%${filterValue}%`,
},
@@ -1397,7 +1398,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
additionalPhones: {
is: 'NULL',
} as PhonesFilter,
@@ -1423,7 +1424,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
return {
or: [
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
additionalPhones: {
like: `%${filterValue}%`,
},
@@ -1436,7 +1437,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
or: [
{
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
additionalPhones: {
like: `%${filterValue}%`,
},
@@ -1444,7 +1445,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
},
},
{
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
additionalPhones: {
is: 'NULL',
} as PhonesFilter,
@@ -1462,7 +1463,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.CONTAINS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
primaryPhoneNumber: {
ilike: `%${filterValue}%`,
},
@@ -1471,7 +1472,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
case RecordFilterOperand.DOES_NOT_CONTAIN:
return {
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
primaryPhoneNumber: {
ilike: `%${filterValue}%`,
},
@@ -1488,7 +1489,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.CONTAINS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
primaryPhoneCallingCode: {
ilike: `%${filterValue}%`,
},
@@ -1497,7 +1498,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
case RecordFilterOperand.DOES_NOT_CONTAIN:
return {
not: {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
primaryPhoneCallingCode: {
ilike: `%${filterValue}%`,
},
@@ -1518,7 +1519,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
}
case 'BOOLEAN': {
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
eq: recordFilter.value === 'true',
} as BooleanFilter,
};
@@ -1531,7 +1532,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
switch (recordFilter.operand) {
case RecordFilterOperand.IS:
return {
[correspondingFieldMetadataItem.name]: {
[fieldMetadataItem.name]: {
in: recordIds,
} as UUIDFilter,
};