use a structured orderBy for ordering
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
@@ -751,9 +751,11 @@ describe('CatalogClient', () => {
|
||||
expect(mockedEndpoint).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should send orderFields with correct format (field,order)', async () => {
|
||||
it('should send orderFields with correct format', async () => {
|
||||
const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => {
|
||||
expect(req.body.orderField).toEqual(['metadata.name,asc']);
|
||||
expect(req.body.orderBy).toEqual([
|
||||
{ field: 'metadata.name', order: 'asc' },
|
||||
]);
|
||||
return res(ctx.json(defaultResponse));
|
||||
});
|
||||
|
||||
@@ -769,9 +771,9 @@ describe('CatalogClient', () => {
|
||||
|
||||
it('should send multiple orderFields with correct format', async () => {
|
||||
const mockedEndpoint = jest.fn().mockImplementation((req, res, ctx) => {
|
||||
expect(req.body.orderField).toEqual([
|
||||
'metadata.name,asc',
|
||||
'spec.type,desc',
|
||||
expect(req.body.orderBy).toEqual([
|
||||
{ field: 'metadata.name', order: 'asc' },
|
||||
{ field: 'spec.type', order: 'desc' },
|
||||
]);
|
||||
return res(ctx.json(defaultResponse));
|
||||
});
|
||||
|
||||
@@ -368,9 +368,7 @@ export class CatalogClient implements CatalogApi {
|
||||
body.limit = limit;
|
||||
}
|
||||
if (orderFields !== undefined) {
|
||||
body.orderField = (
|
||||
Array.isArray(orderFields) ? orderFields : [orderFields]
|
||||
).map(({ field, order }) => `${field},${order}`);
|
||||
body.orderBy = [orderFields].flat();
|
||||
}
|
||||
if (fullTextFilter) {
|
||||
body.fullTextFilter = fullTextFilter;
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@
|
||||
// ******************************************************************
|
||||
|
||||
import { QueryEntitiesByPredicateRequestFullTextFilter } from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model';
|
||||
import { QueryEntitiesByPredicateRequestOrderByInner } from '../models/QueryEntitiesByPredicateRequestOrderByInner.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -26,7 +27,7 @@ import { QueryEntitiesByPredicateRequestFullTextFilter } from '../models/QueryEn
|
||||
export interface QueryEntitiesByPredicateRequest {
|
||||
cursor?: string;
|
||||
limit?: number;
|
||||
orderField?: Array<string>;
|
||||
orderBy?: Array<QueryEntitiesByPredicateRequestOrderByInner>;
|
||||
fullTextFilter?: QueryEntitiesByPredicateRequestFullTextFilter;
|
||||
fields?: Array<string>;
|
||||
/**
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface QueryEntitiesByPredicateRequestOrderByInner {
|
||||
field: string;
|
||||
order: QueryEntitiesByPredicateRequestOrderByInnerOrderEnum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type QueryEntitiesByPredicateRequestOrderByInnerOrderEnum =
|
||||
| 'asc'
|
||||
| 'desc';
|
||||
@@ -47,6 +47,7 @@ export * from '../models/ModelError.model';
|
||||
export * from '../models/NullableEntity.model';
|
||||
export * from '../models/QueryEntitiesByPredicateRequest.model';
|
||||
export * from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model';
|
||||
export * from '../models/QueryEntitiesByPredicateRequestOrderByInner.model';
|
||||
export * from '../models/RecursivePartialEntity.model';
|
||||
export * from '../models/RecursivePartialEntityMeta.model';
|
||||
export * from '../models/RecursivePartialEntityMetaAllOf.model';
|
||||
|
||||
@@ -1125,10 +1125,21 @@ paths:
|
||||
type: string
|
||||
limit:
|
||||
type: number
|
||||
orderField:
|
||||
orderBy:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- field
|
||||
- order
|
||||
properties:
|
||||
field:
|
||||
type: string
|
||||
order:
|
||||
type: string
|
||||
enum:
|
||||
- asc
|
||||
- desc
|
||||
fullTextFilter:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@
|
||||
// ******************************************************************
|
||||
|
||||
import { QueryEntitiesByPredicateRequestFullTextFilter } from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model';
|
||||
import { QueryEntitiesByPredicateRequestOrderByInner } from '../models/QueryEntitiesByPredicateRequestOrderByInner.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -26,7 +27,7 @@ import { QueryEntitiesByPredicateRequestFullTextFilter } from '../models/QueryEn
|
||||
export interface QueryEntitiesByPredicateRequest {
|
||||
cursor?: string;
|
||||
limit?: number;
|
||||
orderField?: Array<string>;
|
||||
orderBy?: Array<QueryEntitiesByPredicateRequestOrderByInner>;
|
||||
fullTextFilter?: QueryEntitiesByPredicateRequestFullTextFilter;
|
||||
fields?: Array<string>;
|
||||
/**
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface QueryEntitiesByPredicateRequestOrderByInner {
|
||||
field: string;
|
||||
order: QueryEntitiesByPredicateRequestOrderByInnerOrderEnum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type QueryEntitiesByPredicateRequestOrderByInnerOrderEnum =
|
||||
| 'asc'
|
||||
| 'desc';
|
||||
@@ -47,6 +47,7 @@ export * from '../models/ModelError.model';
|
||||
export * from '../models/NullableEntity.model';
|
||||
export * from '../models/QueryEntitiesByPredicateRequest.model';
|
||||
export * from '../models/QueryEntitiesByPredicateRequestFullTextFilter.model';
|
||||
export * from '../models/QueryEntitiesByPredicateRequestOrderByInner.model';
|
||||
export * from '../models/RecursivePartialEntity.model';
|
||||
export * from '../models/RecursivePartialEntityMeta.model';
|
||||
export * from '../models/RecursivePartialEntityMetaAllOf.model';
|
||||
|
||||
@@ -1269,10 +1269,20 @@ export const spec = {
|
||||
limit: {
|
||||
type: 'number',
|
||||
},
|
||||
orderField: {
|
||||
orderBy: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
type: 'object',
|
||||
required: ['field', 'order'],
|
||||
properties: {
|
||||
field: {
|
||||
type: 'string',
|
||||
},
|
||||
order: {
|
||||
type: 'string',
|
||||
enum: ['asc', 'desc'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
fullTextFilter: {
|
||||
|
||||
@@ -77,9 +77,12 @@ describe('parseEntityQuery', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('parses orderField into orderFields', () => {
|
||||
it('parses orderBy into orderFields', () => {
|
||||
const result = parseEntityQuery({
|
||||
orderField: ['metadata.name,asc', 'metadata.namespace,desc'],
|
||||
orderBy: [
|
||||
{ field: 'metadata.name', order: 'asc' },
|
||||
{ field: 'metadata.namespace', order: 'desc' },
|
||||
],
|
||||
});
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -128,9 +131,12 @@ describe('parseEntityQuery', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('throws on invalid orderField order value', () => {
|
||||
it('throws on invalid orderBy order value', () => {
|
||||
expect(() =>
|
||||
parseEntityQuery({ orderField: ['metadata.name,sideways'] }),
|
||||
parseEntityQuery({
|
||||
// @ts-expect-error - invalid order value
|
||||
orderBy: [{ field: 'metadata.name', order: 'sideways' }],
|
||||
}),
|
||||
).toThrow(/Invalid order field order/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,17 +41,16 @@ function isSupportedFilterPredicateRoot(
|
||||
}
|
||||
|
||||
function parseOrderFields(
|
||||
orderField: string[] | undefined,
|
||||
orderField: Array<{ field: string; order: string }> | undefined,
|
||||
): EntityOrder[] | undefined {
|
||||
if (!orderField?.length) {
|
||||
return undefined;
|
||||
}
|
||||
return orderField.map(entry => {
|
||||
const [field, order] = entry.split(',');
|
||||
if (order !== undefined && order !== 'asc' && order !== 'desc') {
|
||||
return orderField.map(({ field, order }) => {
|
||||
if (order !== 'asc' && order !== 'desc') {
|
||||
throw new InputError('Invalid order field order, must be asc or desc');
|
||||
}
|
||||
return { field, order: order as 'asc' | 'desc' };
|
||||
return { field, order };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -98,7 +97,7 @@ export function parseEntityQuery(
|
||||
query = result.data;
|
||||
}
|
||||
|
||||
const orderFields = parseOrderFields(request.orderField);
|
||||
const orderFields = parseOrderFields(request.orderBy);
|
||||
|
||||
return {
|
||||
query,
|
||||
|
||||
Reference in New Issue
Block a user