change to asc and desc prefixes

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2022-12-21 11:47:45 +01:00
parent f75bf76330
commit 8a97d472a3
5 changed files with 53 additions and 42 deletions
+23
View File
@@ -154,6 +154,29 @@ Some more real world usable examples:
`/entities?fields=kind,metadata.namespace,metadata.name`
### Ordering
By default the entities are returned in an undefined, but stable, order. You can
pass in one or more `order` query parameters to affect that ordering.
Each parameter starts either with `asc:` for ascending lexicographical order or
`desc:` for descending (reverse) lexicographical order, followed by a
dot-separated path into an entity's keys. The ordering is case insensitive. If
more than one order directive is given, later directives have lower precedence
(they are applied only when directives of higher precedence have equal values).
Example:
```text
/entities?order=asc:kind&order=desc:metadata.name
```
This will order the output first by kind ascending, and then within each kind
(if there's more than one of a given kind) by their name descending. When given
a field that does NOT exist on all entities in the result set, those entities
that do not have the field will always be sorted last in that particular order
step, no matter what the desired order was.
#### Pagination
You may pass the `offset` and `limit` query parameters to do classical
@@ -199,7 +199,9 @@ describe('CatalogClient', () => {
server.use(
rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => {
expect(req.url.search).toBe('?order=kind&order=-metadata.name');
expect(req.url.search).toBe(
'?order=asc:kind&order=desc:metadata.name',
);
return res(ctx.json([]));
}),
);
+5 -7
View File
@@ -139,13 +139,11 @@ export class CatalogClient implements CatalogApi {
if (order) {
for (const directive of [order].flat()) {
if (directive) {
// We could choose to always put in the + prefix, but it's not
// required (ascending sort is the default) and it always gets URL
// encoded to %2B which looks a bit less pretty - so we don't
const str = `${directive.order === 'desc' ? '-' : ''}${
directive.field
}`;
params.push(`order=${encodeURIComponent(str)}`);
params.push(
`order=${encodeURIComponent(directive.order)}:${encodeURIComponent(
directive.field,
)}`,
);
}
}
}
@@ -22,22 +22,21 @@ describe('parseEntityOrderParams', () => {
});
it('handles parameters with various orders', () => {
expect(parseEntityOrderParams({ order: ['a', '+b', '-c'] })).toEqual([
expect(
parseEntityOrderParams({ order: ['asc:a', 'desc:b', 'asc:c:d'] }),
).toEqual([
{ field: 'a', order: 'asc' },
{ field: 'b', order: 'asc' },
{ field: 'c', order: 'desc' },
{ field: 'b', order: 'desc' },
{ field: 'c:d', order: 'asc' },
]);
});
it('rejects missing order or key', () => {
expect(() => parseEntityOrderParams({ order: [''] })).toThrow(
'Invalid order parameter "", no field given',
);
expect(() => parseEntityOrderParams({ order: ['+'] })).toThrow(
'Invalid order parameter "+", no field given',
);
expect(() => parseEntityOrderParams({ order: ['-'] })).toThrow(
'Invalid order parameter "-", no field given',
);
});
it.each(['', ':', 'ascii:', 'ascii:ebcdic', ':colon'])(
'rejects missing/bad order or key, %p',
order => {
expect(() => parseEntityOrderParams({ order: [order] })).toThrow(
`Invalid order parameter "${order}", expected "<asc or desc>:<field name>"`,
);
},
);
});
@@ -22,27 +22,16 @@ export function parseEntityOrderParams(
params: Record<string, unknown>,
): EntityOrder[] | undefined {
return parseStringsParam(params.order, 'order')?.map(item => {
let order: 'asc' | 'desc';
let field: string;
switch (item[0]) {
case '+':
order = 'asc';
field = item.slice(1).trim();
break;
case '-':
order = 'desc';
field = item.slice(1).trim();
break;
default:
order = 'asc';
field = item.trim();
break;
const match = item.match(/^(asc|desc):(.+)$/);
if (!match) {
throw new InputError(
`Invalid order parameter "${item}", expected "<asc or desc>:<field name>"`,
);
}
if (!field) {
throw new InputError(`Invalid order parameter "${item}", no field given`);
}
return { field, order };
return {
order: match[1] as 'asc' | 'desc',
field: match[2],
};
});
}