Skip to content

Commit

Permalink
4902 bug fix fix api filter for enum (#4909)
Browse files Browse the repository at this point in the history
- Handle NUMERIC, SELECT, PROBABILITY, RATING FieldMetadataTypes

Those filters now works:
- http://localhost:3000/rest/opportunities?filter=stage[eq]:MEETING
-
http://localhost:3000/rest/opportunities?filter=stage[in]:[MEETING,NEW]

When providing wrong enum values, the following error messages are
returned:
- http://localhost:3000/rest/opportunities?filter=stage[eq]:MEETINGG
> BadRequestException: 'filter' enum value 'MEETINGG' not available in
'stage' enum. Available enum values are ['NEW', 'SCREENING', 'MEETING',
'PROPOSAL', 'CUSTOMER']
-
http://localhost:3000/rest/opportunities?filter=stage[in]:[MEETING,NEWW]
> BadRequestException: 'filter' enum value 'NEWW' not available in
'stage' enum. Available enum values are ['NEW', 'SCREENING', 'MEETING',
'PROPOSAL', 'CUSTOMER']
  • Loading branch information
martmull authored Apr 10, 2024
1 parent cfcc93d commit 01991fe
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,38 @@ export const fieldCurrencyMock = {
defaultValue: { amountMicros: null, currencyCode: "''" },
};

export const fieldSelectMock = {
name: 'fieldSelect',
type: FieldMetadataType.SELECT,
isNullable: true,
defaultValue: 'OPTION_1',
options: [
{
id: '9a519a86-422b-4598-88ae-78751353f683',
color: 'red',
label: 'Opt 1',
value: 'OPTION_1',
position: 0,
},
{
id: '33f28d51-bc82-4e1d-ae4b-d9e4c0ed0ab4',
color: 'purple',
label: 'Opt 2',
value: 'OPTION_2',
position: 1,
},
],
};

export const objectMetadataItemMock = {
targetTableName: 'testingObject',
nameSingular: 'objectName',
namePlural: 'objectsName',
fields: [fieldNumberMock, fieldStringMock, fieldLinkMock, fieldCurrencyMock],
fields: [
fieldNumberMock,
fieldStringMock,
fieldLinkMock,
fieldCurrencyMock,
fieldSelectMock,
],
} as ObjectMetadataEntity;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { checkFilterEnumValues } from 'src/engine/api/rest/api-rest-query-builder/factories/input-factories/filter-utils/check-filter-enum-values';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import {
fieldSelectMock,
objectMetadataItemMock,
} from 'src/engine/api/__mocks__/object-metadata-item.mock';

describe('checkFilterEnumValues', () => {
it('should check properly', () => {
expect(() =>
checkFilterEnumValues(
FieldMetadataType.SELECT,
fieldSelectMock.name,
'OPTION_1',
objectMetadataItemMock,
),
).not.toThrow();

expect(() =>
checkFilterEnumValues(
FieldMetadataType.SELECT,
fieldSelectMock.name,
'MISSING_OPTION',
objectMetadataItemMock,
),
).toThrow(
`'filter' enum value 'MISSING_OPTION' not available in '${fieldSelectMock.name}' enum. Available enum values are ['OPTION_1', 'OPTION_2']`,
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { BadRequestException } from '@nestjs/common';

import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';

import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';

export const checkFilterEnumValues = (
fieldType: FieldMetadataType | undefined,
fieldName: string,
value: string,
objectMetadataItem: ObjectMetadataInterface,
): void => {
if (
!fieldType ||
![FieldMetadataType.MULTI_SELECT, FieldMetadataType.SELECT].includes(
fieldType,
)
) {
return;
}
const field = objectMetadataItem.fields.find(
(field) => field.name === fieldName,
);

const values = /^\[.*\]$/.test(value)
? value.slice(1, -1).split(',')
: [value];
const enumValues = field?.options?.map((option) => option.value);

if (!enumValues) {
return;
}
values.forEach((val) => {
if (!enumValues.includes(val)) {
throw new BadRequestException(
`'filter' enum value '${val}' not available in '${fieldName}' enum. Available enum values are ['${enumValues.join(
"', '",
)}']`,
);
}
});
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { BadRequestException } from '@nestjs/common';

import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';

import { parseFilterContent } from 'src/engine/api/rest/api-rest-query-builder/factories/input-factories/filter-utils/parse-filter-content.utils';
import { parseBaseFilter } from 'src/engine/api/rest/api-rest-query-builder/factories/input-factories/filter-utils/parse-base-filter.utils';
import {
Expand All @@ -8,6 +10,7 @@ import {
} from 'src/engine/api/rest/api-rest-query-builder/utils/fields.utils';
import { formatFieldValue } from 'src/engine/api/rest/api-rest-query-builder/factories/input-factories/filter-utils/format-field-values.utils';
import { FieldValue } from 'src/engine/api/rest/types/api-rest-field-value.type';
import { checkFilterEnumValues } from 'src/engine/api/rest/api-rest-query-builder/factories/input-factories/filter-utils/check-filter-enum-values';

export enum Conjunctions {
or = 'or',
Expand All @@ -17,7 +20,7 @@ export enum Conjunctions {

export const parseFilter = (
filterQuery: string,
objectMetadataItem,
objectMetadataItem: ObjectMetadataInterface,
): Record<string, FieldValue> => {
const result = {};
const match = filterQuery.match(
Expand Down Expand Up @@ -51,8 +54,13 @@ export const parseFilter = (
}
const { fields, comparator, value } = parseBaseFilter(filterQuery);

const fieldName = fields[0];

checkFields(objectMetadataItem, fields);
const fieldType = getFieldType(objectMetadataItem, fields[0]);
const fieldType = getFieldType(objectMetadataItem, fieldName);

checkFilterEnumValues(fieldType, fieldName, value, objectMetadataItem);

const formattedValue = formatFieldValue(value, fieldType, comparator);

return fields.reverse().reduce(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { BadRequestException } from '@nestjs/common';

import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';

import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';

export const getFieldType = (
objectMetadata: ObjectMetadataEntity,
objectMetadata: ObjectMetadataInterface,
fieldName: string,
): FieldMetadataType | undefined => {
for (const fieldMetdata of objectMetadata.fields) {
Expand All @@ -18,7 +19,7 @@ export const getFieldType = (
};

export const checkFields = (
objectMetadata: ObjectMetadataEntity,
objectMetadata: ObjectMetadataInterface,
fieldNames: string[],
): void => {
const fieldMetadataNames = objectMetadata.fields
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export const mapFieldMetadataToGraphqlQuery = (
FieldMetadataType.DATE_TIME,
FieldMetadataType.EMAIL,
FieldMetadataType.NUMBER,
FieldMetadataType.SELECT,
FieldMetadataType.RATING,
FieldMetadataType.BOOLEAN,
FieldMetadataType.POSITION,
].includes(fieldType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('computeSchemaComponents', () => {
).toEqual({
ObjectName: {
type: 'object',
description: undefined,
required: ['fieldNumber'],
example: { fieldNumber: '' },
properties: {
Expand All @@ -31,6 +32,9 @@ describe('computeSchemaComponents', () => {
fieldNumber: {
type: 'number',
},
fieldSelect: {
type: 'string',
},
fieldString: {
type: 'string',
},
Expand Down

0 comments on commit 01991fe

Please sign in to comment.