Support orderBy as array (#5681)
closes: #4301 --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
@ -14,7 +14,7 @@ export class FindManyQueryFactory {
|
||||
return `
|
||||
query FindMany${capitalize(objectNamePlural)}(
|
||||
$filter: ${objectNameSingular}FilterInput,
|
||||
$orderBy: ${objectNameSingular}OrderByInput,
|
||||
$orderBy: [${objectNameSingular}OrderByInput],
|
||||
$startingAfter: String,
|
||||
$endingBefore: String,
|
||||
$limit: Int = 60
|
||||
|
||||
@ -26,7 +26,7 @@ describe('OrderByInputFactory', () => {
|
||||
it('should return default if order by missing', () => {
|
||||
const request: any = { query: {} };
|
||||
|
||||
expect(service.create(request, objectMetadata)).toEqual({});
|
||||
expect(service.create(request, objectMetadata)).toEqual([{}]);
|
||||
});
|
||||
|
||||
it('should create order by parser properly', () => {
|
||||
@ -36,10 +36,10 @@ describe('OrderByInputFactory', () => {
|
||||
},
|
||||
};
|
||||
|
||||
expect(service.create(request, objectMetadata)).toEqual({
|
||||
fieldNumber: OrderByDirection.AscNullsFirst,
|
||||
fieldText: OrderByDirection.DescNullsLast,
|
||||
});
|
||||
expect(service.create(request, objectMetadata)).toEqual([
|
||||
{ fieldNumber: OrderByDirection.AscNullsFirst },
|
||||
{ fieldText: OrderByDirection.DescNullsLast },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should choose default direction if missing', () => {
|
||||
@ -49,9 +49,9 @@ describe('OrderByInputFactory', () => {
|
||||
},
|
||||
};
|
||||
|
||||
expect(service.create(request, objectMetadata)).toEqual({
|
||||
fieldNumber: OrderByDirection.AscNullsFirst,
|
||||
});
|
||||
expect(service.create(request, objectMetadata)).toEqual([
|
||||
{ fieldNumber: OrderByDirection.AscNullsFirst },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handler complex fields', () => {
|
||||
@ -61,9 +61,9 @@ describe('OrderByInputFactory', () => {
|
||||
},
|
||||
};
|
||||
|
||||
expect(service.create(request, objectMetadata)).toEqual({
|
||||
fieldCurrency: { amountMicros: OrderByDirection.AscNullsFirst },
|
||||
});
|
||||
expect(service.create(request, objectMetadata)).toEqual([
|
||||
{ fieldCurrency: { amountMicros: OrderByDirection.AscNullsFirst } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handler complex fields with direction', () => {
|
||||
@ -73,9 +73,9 @@ describe('OrderByInputFactory', () => {
|
||||
},
|
||||
};
|
||||
|
||||
expect(service.create(request, objectMetadata)).toEqual({
|
||||
fieldCurrency: { amountMicros: OrderByDirection.DescNullsLast },
|
||||
});
|
||||
expect(service.create(request, objectMetadata)).toEqual([
|
||||
{ fieldCurrency: { amountMicros: OrderByDirection.DescNullsLast } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handler multiple complex fields with direction', () => {
|
||||
@ -86,10 +86,10 @@ describe('OrderByInputFactory', () => {
|
||||
},
|
||||
};
|
||||
|
||||
expect(service.create(request, objectMetadata)).toEqual({
|
||||
fieldCurrency: { amountMicros: OrderByDirection.DescNullsLast },
|
||||
fieldLink: { label: OrderByDirection.AscNullsLast },
|
||||
});
|
||||
expect(service.create(request, objectMetadata)).toEqual([
|
||||
{ fieldCurrency: { amountMicros: OrderByDirection.DescNullsLast } },
|
||||
{ fieldLink: { label: OrderByDirection.AscNullsLast } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should throw if direction invalid', () => {
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
RecordOrderBy,
|
||||
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
|
||||
|
||||
import { checkFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-fields.utils';
|
||||
import { checkArrayFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-order-by.utils';
|
||||
|
||||
export const DEFAULT_ORDER_DIRECTION = OrderByDirection.AscNullsFirst;
|
||||
|
||||
@ -17,12 +17,12 @@ export class OrderByInputFactory {
|
||||
const orderByQuery = request.query.order_by;
|
||||
|
||||
if (typeof orderByQuery !== 'string') {
|
||||
return {};
|
||||
return [{}];
|
||||
}
|
||||
|
||||
//orderByQuery = field_1[AscNullsFirst],field_2[DescNullsLast],field_3
|
||||
const orderByItems = orderByQuery.split(',');
|
||||
let result = {};
|
||||
let result: Array<Record<string, OrderByDirection>> = [];
|
||||
let itemDirection = '';
|
||||
let itemFields = '';
|
||||
|
||||
@ -65,10 +65,14 @@ export class OrderByInputFactory {
|
||||
}
|
||||
}, itemDirection);
|
||||
|
||||
result = { ...result, ...fieldResult };
|
||||
const resultFields = Object.keys(fieldResult).map((key) => ({
|
||||
[key]: fieldResult[key],
|
||||
}));
|
||||
|
||||
result = [...result, ...resultFields];
|
||||
}
|
||||
|
||||
checkFields(objectMetadata.objectMetadataItem, Object.keys(result));
|
||||
checkArrayFields(objectMetadata.objectMetadataItem, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { objectMetadataItemMock } from 'src/engine/api/__mocks__/object-metadata-item.mock';
|
||||
import { checkFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-fields.utils';
|
||||
import { checkArrayFields } from 'src/engine/api/rest/rest-api-core-query-builder/utils/check-order-by.utils';
|
||||
|
||||
describe('checkFields', () => {
|
||||
it('should check field types', () => {
|
||||
@ -13,4 +14,21 @@ describe('checkFields', () => {
|
||||
checkFields(objectMetadataItemMock, ['fieldNumber', 'wrongField']),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it('should check field types from array of fields', () => {
|
||||
expect(() =>
|
||||
checkArrayFields(objectMetadataItemMock, [{ fieldNumber: undefined }]),
|
||||
).not.toThrow();
|
||||
|
||||
expect(() =>
|
||||
checkArrayFields(objectMetadataItemMock, [{ wrongField: undefined }]),
|
||||
).toThrow();
|
||||
|
||||
expect(() =>
|
||||
checkArrayFields(objectMetadataItemMock, [
|
||||
{ fieldNumber: undefined },
|
||||
{ wrongField: undefined },
|
||||
]),
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
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 { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
|
||||
export const checkArrayFields = (
|
||||
objectMetadata: ObjectMetadataInterface,
|
||||
fields: Array<Record<string, any>>,
|
||||
): void => {
|
||||
const fieldMetadataNames = objectMetadata.fields
|
||||
.map((field) => {
|
||||
if (isCompositeFieldMetadataType(field.type)) {
|
||||
const compositeType = compositeTypeDefintions.get(field.type);
|
||||
|
||||
if (!compositeType) {
|
||||
throw new BadRequestException(
|
||||
`Composite type '${field.type}' not found`,
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
field.name,
|
||||
compositeType.properties.map(
|
||||
(compositeProperty) => compositeProperty.name,
|
||||
),
|
||||
].flat();
|
||||
}
|
||||
|
||||
return field.name;
|
||||
})
|
||||
.flat();
|
||||
|
||||
for (const fieldObj of fields) {
|
||||
for (const fieldName in fieldObj) {
|
||||
if (!fieldMetadataNames.includes(fieldName)) {
|
||||
throw new BadRequestException(
|
||||
`field '${fieldName}' does not exist in '${computeObjectTargetTable(
|
||||
objectMetadata,
|
||||
)}' object`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user