fix: allow multiple kinds on the InMemoryCatalogClient

Signed-off-by: JPeer264 <jan.oster94@gmail.com>
This commit is contained in:
JPeer264
2025-06-30 16:49:51 +02:00
parent ab539cc72b
commit 6fb414360e
4 changed files with 43 additions and 36 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/catalog-client': patch
---
allow arrays in the InMemoryCatalogClient to filter entities
@@ -26,12 +26,12 @@ const entity1: Entity = {
name: 'e1',
uid: 'u1',
},
relations: [{ type: 'relatedTo', targetRef: 'customkind:default/e2' }],
relations: [{ type: 'relatedTo', targetRef: 'secondcustomkind:default/e2' }],
};
const entity2: Entity = {
apiVersion: 'v1',
kind: 'CustomKind',
kind: 'SecondCustomKind',
metadata: {
namespace: 'default',
name: 'e2',
@@ -67,9 +67,21 @@ describe('InMemoryCatalogClient', () => {
await expect(
client.getEntities({
filter: { 'relations.relatedTo': 'customkind:default/e2' },
filter: { 'relations.relatedTo': 'secondcustomkind:default/e2' },
}),
).resolves.toEqual({ items: [entity1] });
await expect(
client.getEntities({
filter: { kind: ['not-existing', 'CustomKind'] },
}),
).resolves.toEqual({ items: [entity1] });
await expect(
client.getEntities({
filter: { kind: ['SecondCustomKind', 'also-not-existing'] },
}),
).resolves.toEqual({ items: [entity2] });
});
it('getEntitiesByRefs', async () => {
@@ -77,7 +89,7 @@ describe('InMemoryCatalogClient', () => {
await expect(
client.getEntitiesByRefs({
entityRefs: [
'customkind:default/e2',
'secondcustomkind:default/e2',
'customkind:missing/missing',
'customkind:default/e1',
],
@@ -86,7 +98,7 @@ describe('InMemoryCatalogClient', () => {
await expect(
client.getEntitiesByRefs({
entityRefs: [
'customkind:default/e2',
'secondcustomkind:default/e2',
'customkind:missing/missing',
'customkind:default/e1',
],
@@ -114,9 +126,9 @@ describe('InMemoryCatalogClient', () => {
it('getEntityAncestors', async () => {
const client = new InMemoryCatalogClient({ entities });
await expect(
client.getEntityAncestors({ entityRef: 'customkind:default/e2' }),
client.getEntityAncestors({ entityRef: 'secondcustomkind:default/e2' }),
).resolves.toEqual({
rootEntityRef: 'customkind:default/e2',
rootEntityRef: 'secondcustomkind:default/e2',
items: [{ entity: entity2, parentEntityRefs: [] }],
});
});
@@ -124,7 +136,7 @@ describe('InMemoryCatalogClient', () => {
it('getEntityByRef', async () => {
const client = new InMemoryCatalogClient({ entities });
await expect(
client.getEntityByRef('customkind:default/e2'),
client.getEntityByRef('secondcustomkind:default/e2'),
).resolves.toEqual(entity2);
await expect(
client.getEntityByRef('customkind:missing/missing'),
@@ -147,7 +159,7 @@ describe('InMemoryCatalogClient', () => {
it('refreshEntity', async () => {
const client = new InMemoryCatalogClient({ entities });
await expect(
client.refreshEntity('customkind:default/e2'),
client.refreshEntity('secondcustomkind:default/e2'),
).resolves.toBeUndefined();
});
});
@@ -106,6 +106,11 @@ function createFilter(
if (expectedValue === CATALOG_FILTER_EXISTS) {
continue;
}
if (Array.isArray(expectedValue)) {
return expectedValue.some(value =>
searchValues?.includes(String(value).toLocaleLowerCase('en-US')),
);
}
if (
!searchValues?.includes(
String(expectedValue).toLocaleLowerCase('en-US'),
@@ -14,10 +14,6 @@
* limitations under the License.
*/
import {
GetEntitiesRequest,
GetEntitiesResponse,
} from '@backstage/catalog-client';
import { Entity, GroupEntity, UserEntity } from '@backstage/catalog-model';
import { catalogApiRef, EntityProvider } from '@backstage/plugin-catalog-react';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
@@ -104,18 +100,6 @@ const items = [
},
] as Entity[];
const getEntitiesMock = (
request?: GetEntitiesRequest,
): Promise<GetEntitiesResponse> => {
const filterKinds =
Array.isArray(request?.filter) && Array.isArray(request?.filter[0].kind)
? request?.filter[0].kind ?? []
: []; // we expect the request to be like { filter: [{ kind: ['API','System'], 'relations.ownedBy': [group:default/my-team], .... }]. If changed in OwnerShipCard, let's change in also here
return Promise.resolve({
items: items.filter(item => filterKinds.find(k => k === item.kind)),
} as GetEntitiesResponse);
};
describe('OwnershipCard', () => {
const groupEntity: GroupEntity = {
apiVersion: 'backstage.io/v1alpha1',
@@ -157,7 +141,8 @@ describe('OwnershipCard', () => {
};
it('displays entity counts', async () => {
const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock });
const catalogApi = catalogApiMock({ entities: items });
const mockedGetEntities = jest.spyOn(catalogApi, 'getEntities');
const { getByText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
@@ -172,7 +157,7 @@ describe('OwnershipCard', () => {
},
);
expect(catalogApi.getEntities).toHaveBeenCalledWith({
expect(mockedGetEntities).toHaveBeenCalledWith({
filter: [
{
kind: ['Component', 'API', 'System'],
@@ -207,7 +192,7 @@ describe('OwnershipCard', () => {
});
it('applies CustomFilterDefinition', async () => {
const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock });
const catalogApi = catalogApiMock({ entities: items });
const { getByText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
@@ -240,7 +225,7 @@ describe('OwnershipCard', () => {
});
it('links to the catalog with the group filter', async () => {
const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock });
const catalogApi = catalogApiMock({ entities: items });
const { getByText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
@@ -263,7 +248,7 @@ describe('OwnershipCard', () => {
});
it('links to the catalog with the user and groups filters from an user profile', async () => {
const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock });
const catalogApi = catalogApiMock({ entities: items });
const { getByText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
@@ -288,7 +273,7 @@ describe('OwnershipCard', () => {
describe('OwnershipCard relations', () => {
it('shows relations toggle', async () => {
const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock });
const catalogApi = catalogApiMock({ entities: items });
const { getByTitle } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
@@ -307,7 +292,7 @@ describe('OwnershipCard', () => {
});
it('hides relations toggle', async () => {
const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock });
const catalogApi = catalogApiMock({ entities: items });
const rendered = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
@@ -326,7 +311,7 @@ describe('OwnershipCard', () => {
});
it('overrides relation type', async () => {
const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock });
const catalogApi = catalogApiMock({ entities: items });
const { getByTitle } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
@@ -345,7 +330,7 @@ describe('OwnershipCard', () => {
});
it('defaults to aggregated for User entity kind', async () => {
const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock });
const catalogApi = catalogApiMock({ entities: items });
const { getByLabelText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
@@ -364,7 +349,7 @@ describe('OwnershipCard', () => {
});
it('defaults to direct for all entity kinds except User', async () => {
const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock });
const catalogApi = catalogApiMock({ entities: items });
const { getByLabelText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
@@ -383,7 +368,7 @@ describe('OwnershipCard', () => {
});
it('defaults to provided relationsType', async () => {
const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock });
const catalogApi = catalogApiMock({ entities: items });
const { getByLabelText } = await renderInTestApp(
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>