From 6f830bb8465fce46ce41886441303cd47bd758c7 Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Fri, 9 Feb 2024 08:46:59 +0200 Subject: [PATCH] feat: allow passing optional filter to getEntityByRefs this closes #22839 Signed-off-by: Heikki Hellgren --- .changeset/quiet-stingrays-breathe.md | 6 ++++ packages/catalog-client/api-report.md | 1 + .../catalog-client/src/CatalogClient.test.ts | 9 +++++- packages/catalog-client/src/CatalogClient.ts | 30 +++++++++++-------- .../src/generated/apis/DefaultApi.client.ts | 7 +++-- packages/catalog-client/src/types/api.ts | 4 +++ .../src/schema/openapi.generated.ts | 5 ++++ .../catalog-backend/src/schema/openapi.yaml | 2 ++ .../src/service/createRouter.test.ts | 9 +++++- .../src/service/createRouter.ts | 1 + 10 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 .changeset/quiet-stingrays-breathe.md diff --git a/.changeset/quiet-stingrays-breathe.md b/.changeset/quiet-stingrays-breathe.md new file mode 100644 index 0000000000..b38c776021 --- /dev/null +++ b/.changeset/quiet-stingrays-breathe.md @@ -0,0 +1,6 @@ +--- +'@backstage/catalog-client': patch +'@backstage/plugin-catalog-backend': patch +--- + +Allow passing optional filter to `getEntitiesByRefs` diff --git a/packages/catalog-client/api-report.md b/packages/catalog-client/api-report.md index b1dd2b9188..ce25990fd0 100644 --- a/packages/catalog-client/api-report.md +++ b/packages/catalog-client/api-report.md @@ -192,6 +192,7 @@ export type EntityOrderQuery = export interface GetEntitiesByRefsRequest { entityRefs: string[]; fields?: EntityFieldsQuery | undefined; + filter?: EntityFilterQuery; } // @public diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index 81c0a6af5c..a5ff7c6e31 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -275,6 +275,7 @@ describe('CatalogClient', () => { }; server.use( rest.post(`${mockBaseUrl}/entities/by-refs`, async (req, res, ctx) => { + expect(req.url.search).toBe('?filter=kind%3DAPI%2Ckind%3DComponent'); await expect(req.json()).resolves.toEqual({ entityRefs: ['k:n/a', 'k:n/b'], fields: ['a', 'b'], @@ -284,7 +285,13 @@ describe('CatalogClient', () => { ); const response = await client.getEntitiesByRefs( - { entityRefs: ['k:n/a', 'k:n/b'], fields: ['a', 'b'] }, + { + entityRefs: ['k:n/a', 'k:n/b'], + fields: ['a', 'b'], + filter: { + kind: ['API', 'Component'], + }, + }, { token }, ); diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index 07fedd0e9c..947195d291 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -15,32 +15,32 @@ */ import { - Entity, CompoundEntityRef, + Entity, parseEntityRef, stringifyEntityRef, stringifyLocationRef, } from '@backstage/catalog-model'; import { ResponseError } from '@backstage/errors'; import { - CATALOG_FILTER_EXISTS, AddLocationRequest, AddLocationResponse, + CATALOG_FILTER_EXISTS, CatalogApi, - GetEntitiesRequest, - GetEntitiesResponse, CatalogRequestOptions, - GetEntityAncestorsRequest, - GetEntityAncestorsResponse, - Location, - GetEntityFacetsRequest, - GetEntityFacetsResponse, - ValidateEntityResponse, + EntityFilterQuery, GetEntitiesByRefsRequest, GetEntitiesByRefsResponse, + GetEntitiesRequest, + GetEntitiesResponse, + GetEntityAncestorsRequest, + GetEntityAncestorsResponse, + GetEntityFacetsRequest, + GetEntityFacetsResponse, + Location, QueryEntitiesRequest, - EntityFilterQuery, QueryEntitiesResponse, + ValidateEntityResponse, } from './types/api'; import { isQueryEntitiesInitialRequest } from './utils'; import { DefaultApiClient, TypedResponse } from './generated'; @@ -177,7 +177,13 @@ export class CatalogClient implements CatalogApi { ): Promise { const response = await this.apiClient.getEntitiesByRefs( { - body: request, + body: { + entityRefs: request.entityRefs, + fields: request.fields, + }, + query: { + filter: this.getFilterValue(request.filter), + }, }, options, ); diff --git a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts index 14fc73ba83..7619e69504 100644 --- a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts +++ b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts @@ -281,14 +281,17 @@ export class DefaultApiClient { // @ts-ignore request: { body: GetEntitiesByRefsRequest; + query?: { + filter?: Array; + }; }, options?: RequestOptions, ): Promise> { const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); - const uriTemplate = `/entities/by-refs`; + const uriTemplate = `/entities/by-refs/{?filter*}`; - const uri = parser.parse(uriTemplate).expand({}); + const uri = parser.parse(uriTemplate).expand({ ...request.query }); return await this.fetchApi.fetch(`${baseUrl}${uri}`, { headers: { diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index ecf2be7f44..91ae64fff3 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -203,6 +203,10 @@ export interface GetEntitiesByRefsRequest { * declarations. */ fields?: EntityFieldsQuery | undefined; + /** + * If given, return only entities that match the given filter. + */ + filter?: EntityFilterQuery; } /** diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index 9c9dc46ad7..fd865152fc 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -1107,6 +1107,11 @@ export const spec = { }, }, }, + parameters: [ + { + $ref: '#/components/parameters/filter', + }, + ], }, }, '/entities/by-query': { diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index 0b1ed5dc21..ab42afed1e 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -862,6 +862,8 @@ paths: - component:default/backstage fields: - metadata.annotations + parameters: + - $ref: '#/components/parameters/filter' /entities/by-query: get: operationId: GetEntitiesByQuery diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index 5177f3b42b..7bc7b8acfe 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -527,7 +527,7 @@ describe('createRouter readonly disabled', () => { const entityRef = stringifyEntityRef(entity); entitiesCatalog.entitiesBatch.mockResolvedValue({ items: [entity] }); const response = await request(app) - .post('/entities/by-refs') + .post('/entities/by-refs?filter=kind=Component') .set('Content-Type', 'application/json') .send( JSON.stringify({ @@ -540,6 +540,13 @@ describe('createRouter readonly disabled', () => { entityRefs: [entityRef], fields: expect.any(Function), credentials: mockCredentials.user(), + filter: { + anyOf: [ + { + allOf: [{ key: 'kind', values: ['Component'] }], + }, + ], + }, }); expect(response.status).toEqual(200); expect(response.body).toEqual({ items: [entity] }); diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 922782d75a..740ef63ea9 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -219,6 +219,7 @@ export async function createRouter( const request = entitiesBatchRequest(req); const response = await entitiesCatalog.entitiesBatch({ entityRefs: request.entityRefs, + filter: parseEntityFilterParams(req.query), fields: parseEntityTransformParams(req.query, request.fields), credentials: await httpAuth.credentials(req), });