Merge pull request #33818 from backstage/maratd/remove-humanize-entity-ref-org

fix(org): replace deprecated humanizeEntityRef with Catalog Presentation API
This commit is contained in:
Fredrik Adelöw
2026-04-09 11:39:37 +02:00
committed by GitHub
3 changed files with 150 additions and 4 deletions
@@ -0,0 +1,5 @@
---
'@backstage/plugin-org': patch
---
Replaced deprecated `humanizeEntityRef` usage with the Catalog Presentation API.
@@ -18,6 +18,7 @@ import { CompoundEntityRef, Entity } from '@backstage/catalog-model';
import { useGetEntities } from './useGetEntities';
import { renderHook, waitFor } from '@testing-library/react';
import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils';
import qs from 'qs';
const givenParentGroup = 'team.squad1';
const givenLeafGroup = 'team.squad2';
@@ -45,8 +46,10 @@ const getEntityRelationsMock: jest.Mock<
[Entity | undefined]
> = jest.fn();
jest.mock('@backstage/plugin-catalog-react', () => {
const actual = jest.requireActual('@backstage/plugin-catalog-react');
return {
catalogApiRef: {},
entityPresentationSnapshot: actual.entityPresentationSnapshot,
getEntityRelations: jest.fn(entity => {
return getEntityRelationsMock(entity);
}) as any,
@@ -283,6 +286,144 @@ describe('useGetEntities', () => {
});
});
describe('queryParams generation', () => {
afterEach(() => {
getEntityRelationsMock.mockRestore();
catalogApi.getEntities.mockRestore();
catalogApi.getEntitiesByRefs.mockRestore();
});
it('should produce query params with humanized entity refs as owners', async () => {
getEntityRelationsMock.mockReturnValue([]);
catalogApi.getEntities.mockResolvedValueOnce({
items: [
{
kind: 'Component',
metadata: { name: 'my-service', namespace: 'default' },
spec: { type: 'service' },
} as Partial<Entity> as Entity,
],
});
const { result } = renderHook(
({ entity }) => useGetEntities(entity, 'direct'),
{ initialProps: { entity: givenLeafGroupEntity } },
);
await waitFor(() => expect(result.current.loading).toBe(false));
expect(result.current.componentsWithCounters).toHaveLength(1);
const params = qs.parse(
result.current.componentsWithCounters![0].queryParams,
);
expect(params).toEqual({
filters: {
kind: 'component',
type: 'service',
owners: givenLeafGroup,
user: 'all',
},
});
});
it('should include multiple owners as an array in query params', async () => {
getEntityRelationsMock
.mockReturnValueOnce([createGroupRefFromName(givenLeafGroup)])
.mockReturnValue([]);
catalogApi.getEntitiesByRefs.mockResolvedValueOnce({
items: [givenLeafGroupEntity],
});
catalogApi.getEntities.mockResolvedValueOnce({
items: [
{
kind: 'API',
metadata: { name: 'my-api', namespace: 'default' },
spec: { type: 'openapi' },
} as Partial<Entity> as Entity,
],
});
const { result } = renderHook(
({ entity }) => useGetEntities(entity, 'aggregated'),
{ initialProps: { entity: givenParentGroupEntity } },
);
await waitFor(() => expect(result.current.loading).toBe(false));
expect(result.current.componentsWithCounters).toHaveLength(1);
const params = qs.parse(
result.current.componentsWithCounters![0].queryParams,
);
expect(params).toEqual({
filters: {
kind: 'api',
type: 'openapi',
owners: expect.arrayContaining([givenLeafGroup, givenParentGroup]),
user: 'all',
},
});
});
it('should group entities by kind and type in query params', async () => {
getEntityRelationsMock.mockReturnValue([]);
catalogApi.getEntities.mockResolvedValueOnce({
items: [
{
kind: 'Component',
metadata: { name: 'svc-1', namespace: 'default' },
spec: { type: 'service' },
} as Partial<Entity> as Entity,
{
kind: 'Component',
metadata: { name: 'svc-2', namespace: 'default' },
spec: { type: 'service' },
} as Partial<Entity> as Entity,
{
kind: 'API',
metadata: { name: 'api-1', namespace: 'default' },
spec: { type: 'openapi' },
} as Partial<Entity> as Entity,
],
});
const { result } = renderHook(
({ entity }) => useGetEntities(entity, 'direct'),
{ initialProps: { entity: givenLeafGroupEntity } },
);
await waitFor(() => expect(result.current.loading).toBe(false));
expect(result.current.componentsWithCounters).toHaveLength(2);
const componentEntry = result.current.componentsWithCounters!.find(
c => c.kind === 'Component',
)!;
expect(componentEntry.counter).toBe(2);
const componentParams = qs.parse(componentEntry.queryParams);
expect(componentParams).toEqual({
filters: expect.objectContaining({
kind: 'component',
type: 'service',
}),
});
const apiEntry = result.current.componentsWithCounters!.find(
c => c.kind === 'API',
)!;
expect(apiEntry.counter).toBe(1);
const apiParams = qs.parse(apiEntry.queryParams);
expect(apiParams).toEqual({
filters: expect.objectContaining({
kind: 'api',
type: 'openapi',
}),
});
});
});
describe('useGetEntities exceeding default 16384 bytes header size', () => {
const largeNumberOfGroups = Array.from({ length: 600 }, (_, i) =>
createGroupEntityFromName(`very-long-group-name-${i + 1}`),
@@ -16,7 +16,6 @@
import {
Entity,
parseEntityRef,
RELATION_MEMBER_OF,
RELATION_PARENT_OF,
stringifyEntityRef,
@@ -24,8 +23,8 @@ import {
import {
CatalogApi,
catalogApiRef,
entityPresentationSnapshot,
getEntityRelations,
humanizeEntityRef,
} from '@backstage/plugin-catalog-react';
import limiterFactory from 'p-limit';
import { useApi } from '@backstage/core-plugin-api';
@@ -47,8 +46,9 @@ const getQueryParams = (
selectedEntity: EntityTypeProps,
): string => {
const { kind, type } = selectedEntity;
const owners = ownersEntityRef.map(owner =>
humanizeEntityRef(parseEntityRef(owner), { defaultKind: 'group' }),
const owners = ownersEntityRef.map(
ref =>
entityPresentationSnapshot(ref, { defaultKind: 'group' }).primaryTitle,
);
const filters = {
kind: kind.toLocaleLowerCase('en-US'),