Add containsAny filter comparators in rest api (#9595)
- add missing `containsAny` filter comparator to filter multiSelect fields - fix app break if object with same name created - fix flash when clicking on choose plan ### Before https://github.com/user-attachments/assets/71709912-63e8-40f8-bc52-3ec0a62746bb ### After https://github.com/user-attachments/assets/93ee02c2-e3d9-4bab-8b6a-62813fd40b2b
This commit is contained in:
@ -156,7 +156,7 @@ export const ChooseYourPlan = () => {
|
|||||||
handleCheckoutSession();
|
handleCheckoutSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (billingCheckoutSession.skipPlanPage || isSubmitting) {
|
if (billingCheckoutSession.skipPlanPage && isSubmitting) {
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -53,5 +53,23 @@ describe('formatFieldValue', () => {
|
|||||||
).toThrow(
|
).toThrow(
|
||||||
"'filter' invalid for 'in' operator. Received '2024-12-01T14:23:23.914Z' but array value expected eg: 'field[in]:[value_1,value_2]'",
|
"'filter' invalid for 'in' operator. Received '2024-12-01T14:23:23.914Z' but array value expected eg: 'field[in]:[value_1,value_2]'",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
formatFieldValue(
|
||||||
|
'["2023-12-01T14:23:23.914Z","2024-12-01T14:23:23.914Z"]',
|
||||||
|
undefined,
|
||||||
|
'containsAny',
|
||||||
|
),
|
||||||
|
).toEqual(['2023-12-01T14:23:23.914Z', '2024-12-01T14:23:23.914Z']);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
formatFieldValue('[1,2]', FieldMetadataType.NUMBER, 'containsAny'),
|
||||||
|
).toEqual([1, 2]);
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
formatFieldValue('2024-12-01T14:23:23.914Z', undefined, 'containsAny'),
|
||||||
|
).toThrow(
|
||||||
|
"'filter' invalid for 'containsAny' operator. Received '2024-12-01T14:23:23.914Z' but array value expected eg: 'field[containsAny]:[value_1,value_2]'",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,16 +3,17 @@ import { BadRequestException } from '@nestjs/common';
|
|||||||
import { FieldMetadataType } from 'twenty-shared';
|
import { FieldMetadataType } from 'twenty-shared';
|
||||||
|
|
||||||
import { FieldValue } from 'src/engine/api/rest/core/types/field-value.type';
|
import { FieldValue } from 'src/engine/api/rest/core/types/field-value.type';
|
||||||
|
import { isDefined } from 'src/utils/is-defined';
|
||||||
|
|
||||||
export const formatFieldValue = (
|
export const formatFieldValue = (
|
||||||
value: string,
|
value: string,
|
||||||
fieldType?: FieldMetadataType,
|
fieldType?: FieldMetadataType,
|
||||||
comparator?: string,
|
comparator?: string,
|
||||||
): FieldValue => {
|
): FieldValue => {
|
||||||
if (comparator === 'in') {
|
if (isDefined(comparator) && ['in', 'containsAny'].includes(comparator)) {
|
||||||
if (value[0] !== '[' || value[value.length - 1] !== ']') {
|
if (value[0] !== '[' || value[value.length - 1] !== ']') {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
`'filter' invalid for 'in' operator. Received '${value}' but array value expected eg: 'field[in]:[value_1,value_2]'`,
|
`'filter' invalid for '${comparator}' operator. Received '${value}' but array value expected eg: 'field[${comparator}]:[value_1,value_2]'`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const stringValues = value.substring(1, value.length - 1);
|
const stringValues = value.substring(1, value.length - 1);
|
||||||
|
|||||||
@ -4,6 +4,7 @@ export enum FilterComparators {
|
|||||||
eq = 'eq',
|
eq = 'eq',
|
||||||
neq = 'neq',
|
neq = 'neq',
|
||||||
in = 'in',
|
in = 'in',
|
||||||
|
containsAny = 'containsAny',
|
||||||
is = 'is',
|
is = 'is',
|
||||||
gt = 'gt',
|
gt = 'gt',
|
||||||
gte = 'gte',
|
gte = 'gte',
|
||||||
@ -59,7 +60,7 @@ export const parseBaseFilter = (
|
|||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
`'filter' invalid for '${baseFilter}', comparator ${comparator} not in ${Object.keys(
|
`'filter' invalid for '${baseFilter}', comparator ${comparator} not in ${Object.keys(
|
||||||
FilterComparators,
|
FilterComparators,
|
||||||
).join(',')}`,
|
).join(', ')}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@ describe('FilterInputFactory', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
expect(() => service.create(request, objectMetadata)).toThrow(
|
expect(() => service.create(request, objectMetadata)).toThrow(
|
||||||
"'filter' invalid for 'fieldNumber[wrongComparator]:1', comparator wrongComparator not in eq,neq,in,is,gt,gte,lt,lte,startsWith,like,ilike",
|
"'filter' invalid for 'fieldNumber[wrongComparator]:1', comparator wrongComparator not in eq, neq, in, containsAny, is, gt, gte, lt, lte, startsWith, like, ilike",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -103,7 +103,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.validatesNoOtherObjectWithSameNameExistsOrThrows({
|
await this.validatesNoOtherObjectWithSameNameExistsOrThrows({
|
||||||
objectMetadataNamePlural: objectMetadataInput.namePlural,
|
objectMetadataNamePlural: objectMetadataInput.namePlural,
|
||||||
objectMetadataNameSingular: objectMetadataInput.nameSingular,
|
objectMetadataNameSingular: objectMetadataInput.nameSingular,
|
||||||
workspaceId: objectMetadataInput.workspaceId,
|
workspaceId: objectMetadataInput.workspaceId,
|
||||||
|
|||||||
Reference in New Issue
Block a user