Will
2021-04-27 15:36:01 +01:00
committed by Will Sewell
parent f72aa8116a
commit d1b1306d98
4 changed files with 58 additions and 9 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/catalog-client': minor
---
Allow `filter` param to be specified multiple times
@@ -79,6 +79,37 @@ describe('CatalogClient', () => {
it('builds entity search filters properly', async () => {
expect.assertions(2);
server.use(
rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => {
expect(req.url.search).toBe(
'?filter=a=1,b=2,b=3,%C3%B6=%3D&filter=a=2',
);
return res(ctx.json([]));
}),
);
const response = await client.getEntities(
{
filter: [
{
a: '1',
b: ['2', '3'],
ö: '=',
},
{
a: '2',
},
],
},
{ token },
);
expect(response.items).toEqual([]);
});
it('builds entity legacy search filters properly', async () => {
expect.assertions(2);
server.use(
rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => {
expect(req.url.search).toBe('?filter=a=1,b=2,b=3,%C3%B6=%3D');
@@ -88,6 +119,7 @@ describe('CatalogClient', () => {
const response = await client.getEntities(
{
// The legacy value of filter is not an array
filter: {
a: '1',
b: ['2', '3'],
+17 -8
View File
@@ -56,18 +56,27 @@ export class CatalogClient implements CatalogApi {
request?: CatalogEntitiesRequest,
options?: CatalogRequestOptions,
): Promise<CatalogListResponse<Entity>> {
const { filter = {}, fields = [] } = request ?? {};
const { filter = [], fields = [] } = request ?? {};
const filterItems = [filter].flat();
const params: string[] = [];
const filterParts: string[] = [];
for (const [key, value] of Object.entries(filter)) {
for (const v of [value].flat()) {
filterParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
// filter param can occur multiple times, for example
// /api/catalog/entities?filter=metadata.name=wayback-search,kind=component&filter=metadata.name=www-artist,kind=component'
// the "outer array" defined by `filter` occurrences corresponds to "anyOf" filters
// the "inner array" defined within a `filter` param corresponds to "allOf" filters
for (const filterItem of filterItems) {
const filterParts: string[] = [];
for (const [key, value] of Object.entries(filterItem)) {
for (const v of [value].flat()) {
filterParts.push(
`${encodeURIComponent(key)}=${encodeURIComponent(v)}`,
);
}
}
}
if (filterParts.length) {
params.push(`filter=${filterParts.join(',')}`);
if (filterParts.length) {
params.push(`filter=${filterParts.join(',')}`);
}
}
if (fields.length) {
+4 -1
View File
@@ -17,7 +17,10 @@
import { Entity, EntityName, Location } from '@backstage/catalog-model';
export type CatalogEntitiesRequest = {
filter?: Record<string, string | string[]> | undefined;
filter?:
| Record<string, string | string[]>[]
| Record<string, string | string[]> // Legacy type preserved for backwards compatibility
| undefined;
fields?: string[] | undefined;
};