api-docs: migrate entity filters to entity predicates

Migrated entity filters from deprecated string-based format to the new
entity predicate format in the alpha exports. Also updated documentation
across multiple plugins to use the new format and Blueprint APIs.

Changes:
- Migrated all entity filters in api-docs plugin from strings like
  'kind:api' to entity predicates like { kind: 'api' }
- Updated documentation examples in api-docs, org, kubernetes, and
  catalog-graph plugins
- Migrated all documentation examples from createEntityCardExtension and
  createEntityContentExtension to use EntityCardBlueprint and
  EntityContentBlueprint

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-02-04 12:02:52 +01:00
parent e81e6c3350
commit 41836147fc
8 changed files with 199 additions and 137 deletions
@@ -0,0 +1,8 @@
---
'@backstage/plugin-api-docs': patch
'@backstage/plugin-catalog-graph': patch
'@backstage/plugin-kubernetes': patch
'@backstage/plugin-org': patch
---
Updated usage of deprecated APIs in the new frontend system.
+103 -71
View File
@@ -77,12 +77,14 @@ To link that a component provides or consumes an API, see the [`providesApis`](h
- entity-card:api-docs/providing-components:
config:
# Presenting the card only for entities of kind api
filter: kind:api
filter:
kind: api
# Shows a table of components that consumes a particular api
- entity-card:api-docs/consuming-components:
config:
# Presenting the card only for entities of kind api
filter: kind:api
filter:
kind: api
# Enabling some contents
# The contents will be displayed in the same order it appears in this setting list
# Shows a "Definition" tab for entities of kind api
@@ -352,7 +354,8 @@ app:
# The default value is a function that verifies it is a components has api pat of relations
# For more information about entity cards filters, check out this pull request
# https://github.com/backstage/backstage/pull/21480
filter: 'kind:component'
filter:
kind: component
```
###### Override
@@ -361,17 +364,19 @@ Use extension overrides for completely re-implementing the has apis entity card
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'api-docs',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'has-apis' entity card extension provided by the 'api-docs' plugin
name: 'has-apis',
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomHasApisEntityCard />),
params: {
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomHasApisEntityCard />),
},
}),
],
});
@@ -422,29 +427,34 @@ app:
# <extension-kind>/<plugin-namespace>:<extension-name>
- entity-card:api-docs/definition:
config:
# Default to 'kind:api'
# Default to { kind: 'api' }
# For more information about entity cards filters, check out this pull request
# https://github.com/backstage/backstage/pull/21480
filter: 'kind:component'
filter:
kind: component
```
###### Override
Use extension overrides for completely re-implementing the has apis entity card extension:
Use extension overrides for completely re-implementing the definition entity card extension:
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'api-docs',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'definition' entity card extension provided by the 'api-docs' plugin
name: 'definition',
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomApiDefinitionEntityCard />),
params: {
// Returning a custom card component
loader: () =>
import('./components').then(m => (
<m.MyCustomApiDefinitionEntityCard />
)),
},
}),
],
});
@@ -495,29 +505,34 @@ app:
# <extension-kind>/<plugin-namespace>:<extension-name>
- entity-card:api-docs/provided-apis:
config:
# Default to 'kind:component'
# Default to { kind: 'component' }
# For more information about entity cards filters, check out this pull request
# https://github.com/backstage/backstage/pull/21480
filter: 'kind:component'
filter:
kind: component
```
###### Override
Use extension overrides for completely re-implementing the has apis entity card extension:
Use extension overrides for completely re-implementing the provided-apis entity card extension:
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'api-docs',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'provided-apis' entity card extension provided by the 'api-docs' plugin
name: 'provided-apis',
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomProvidedApisEntityCard />),
params: {
// Returning a custom card component
loader: () =>
import('./components').then(m => (
<m.MyCustomProvidedApisEntityCard />
)),
},
}),
],
});
@@ -568,29 +583,34 @@ app:
# <extension-kind>/<plugin-namespace>:<extension-name>
- entity-card:api-docs/consumed-apis:
config:
# Default to 'kind:component'
# Default to { kind: 'component' }
# For more information about entity cards filters, check out this pull request
# https://github.com/backstage/backstage/pull/21480
filter: 'kind:component'
filter:
kind: component
```
###### Override
Use extension overrides for completely re-implementing the has apis entity card extension:
Use extension overrides for completely re-implementing the consumed-apis entity card extension:
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'api-docs',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'consumed-apis' entity card extension provided by the 'api-docs' plugin
name: 'consumed-apis',
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomConsumedApisEntityCard />),
params: {
// Returning a custom card component
loader: () =>
import('./components').then(m => (
<m.MyCustomConsumedApisEntityCard />
)),
},
}),
],
});
@@ -641,31 +661,34 @@ app:
# <extension-kind>/<plugin-namespace>:<extension-name>
- entity-card:api-docs/providing-components:
config:
# Default to 'kind:api'
# Default to { kind: 'api' }
# For more information about entity cards filters, check out this pull request
# https://github.com/backstage/backstage/pull/21480
filter: 'kind:component'
filter:
kind: component
```
###### Override
Use extension overrides for completely re-implementing the has apis entity card extension:
Use extension overrides for completely re-implementing the providing-components entity card extension:
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'api-docs',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'providing-components' entity card extension provided by the 'api-docs' plugin
name: 'providing-components',
// Returning a custom card component
loader: () =>
import('./components').then(m => (
<m.MyCustomProvidingComponentsEntityCard />
)),
params: {
// Returning a custom card component
loader: () =>
import('./components').then(m => (
<m.MyCustomProvidingComponentsEntityCard />
)),
},
}),
],
});
@@ -716,31 +739,34 @@ app:
# <extension-kind>/<plugin-namespace>:<extension-name>
- entity-card:api-docs/consuming-components:
config:
# Default to 'kind:api'
# Default to { kind: 'api' }
# For more information about entity cards filters, check out this pull request
# https://github.com/backstage/backstage/pull/21480
filter: 'kind:component'
filter:
kind: component
```
###### Override
Use extension overrides for completely re-implementing the has apis entity card extension:
Use extension overrides for completely re-implementing the consuming-components entity card extension:
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'api-docs',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'consuming-components' entity card extension provided by the 'api-docs' plugin
name: 'consuming-components',
// Returning a custom card component
loader: () =>
import('./components').then(m => (
<m.MyCustomConsumingComponentsEntityCard />
)),
params: {
// Returning a custom card component
loader: () =>
import('./components').then(m => (
<m.MyCustomConsumingComponentsEntityCard />
)),
},
}),
],
});
@@ -798,11 +824,12 @@ app:
# <extension-kind>/<plugin-namespace>:<extension-name>
- entity-content:api-docs/definition:
config:
# A text-based query used to filter whether the entity contentextension should be rendered or not.
# An entity predicate used to filter whether the entity content extension should be rendered or not.
# For more information about entity cards filters, check out this pull request
# https://github.com/backstage/backstage/pull/21480
# defaults to 'kind:api'
filter: 'kind:api'
# defaults to { kind: 'api' }
filter:
kind: api
# The entity content table title
# defaults to 'Definition'
title: 'Definition'
@@ -813,23 +840,25 @@ app:
###### Override
Use extension overrides for completely re-implementing the has apis entity card extension:
Use extension overrides for completely re-implementing the definition entity content extension:
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityContentExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'api-docs',
extensions: [
createEntityContentExtension({
EntityContentBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'definition' entity content extension provided by the 'api-docs' plugin
name: 'definition',
// Returning a custom content component
loader: () =>
import('./components').then(m => (
<m.MyCustomApiDefinitionEntityContent />
)),
params: {
// Returning a custom content component
loader: () =>
import('./components').then(m => (
<m.MyCustomApiDefinitionEntityContent />
)),
},
}),
],
});
@@ -878,11 +907,12 @@ app:
# <extension-kind>/<plugin-namespace>:<extension-name>
- entity-content:api-docs/apis:
config:
# A text-based query used to filter whether the entity contentextension should be rendered or not.
# An entity predicate used to filter whether the entity content extension should be rendered or not.
# For more information about entity cards filters, check out this pull request
# https://github.com/backstage/backstage/pull/21480
# defaults to 'kind:component'
filter: 'kind:component'
# defaults to { kind: 'component' }
filter:
kind: component
# The entity content table title
# defaults to 'Definition'
title: 'Definition'
@@ -897,17 +927,19 @@ Use extension overrides for completely re-implementing the apis entity content e
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityContentExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'api-docs',
extensions: [
createEntityContentExtension({
EntityContentBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'apis' entity content extension provided by the 'api-docs' plugin
name: 'apis',
// Returning a custom content component
loader: () =>
import('./components').then(m => <m.MyCustomApisEntityContent />),
params: {
// Returning a custom content component
loader: () =>
import('./components').then(m => <m.MyCustomApisEntityContent />),
},
}),
],
});
+6 -5
View File
@@ -80,9 +80,9 @@ describe('api-docs plugin entity extensions', () => {
],
});
// Wait for page to render, then check definition card is not shown
expect(await screen.findByText('my-component')).toBeInTheDocument();
expect(screen.queryByText('openapi')).not.toBeInTheDocument();
expect(
await screen.findByTestId('empty-entity-page'),
).toBeInTheDocument();
});
it('should display the API definition content', async () => {
@@ -166,8 +166,9 @@ describe('api-docs plugin entity extensions', () => {
});
// Content should not render for Components
expect(await screen.findByText('my-service')).toBeInTheDocument();
expect(screen.queryByText('Definition')).not.toBeInTheDocument();
expect(
await screen.findByTestId('empty-entity-page'),
).toBeInTheDocument();
});
});
});
+7 -7
View File
@@ -109,7 +109,7 @@ const apiDocsHasApisEntityCard = EntityCardBlueprint.make({
const apiDocsDefinitionEntityCard = EntityCardBlueprint.make({
name: 'definition',
params: {
filter: 'kind:api',
filter: { kind: 'api' },
loader: () =>
import('./components/ApiDefinitionCard').then(m => (
<m.ApiDefinitionCard />
@@ -123,7 +123,7 @@ const apiDocsConsumedApisEntityCard = EntityCardBlueprint.make({
// Omitting configSchema for now
// We are skipping variants and columns are too complex to map to zod
// See: https://github.com/backstage/backstage/pull/22619#discussion_r1477333252
filter: 'kind:component',
filter: { kind: 'component' },
loader: () =>
import('./components/ApisCards').then(m => <m.ConsumedApisCard />),
},
@@ -135,7 +135,7 @@ const apiDocsProvidedApisEntityCard = EntityCardBlueprint.make({
// Omitting configSchema for now
// We are skipping variants and columns are too complex to map to zod
// See: https://github.com/backstage/backstage/pull/22619#discussion_r1477333252
filter: 'kind:component',
filter: { kind: 'component' },
loader: () =>
import('./components/ApisCards').then(m => <m.ProvidedApisCard />),
},
@@ -147,7 +147,7 @@ const apiDocsConsumingComponentsEntityCard = EntityCardBlueprint.make({
// Omitting configSchema for now
// We are skipping variants
// See: https://github.com/backstage/backstage/pull/22619#discussion_r1477333252
filter: 'kind:api',
filter: { kind: 'api' },
loader: () =>
import('./components/ComponentsCards').then(m => (
<m.ConsumingComponentsCard />
@@ -161,7 +161,7 @@ const apiDocsProvidingComponentsEntityCard = EntityCardBlueprint.make({
// Omitting configSchema for now
// We are skipping variants
// See: https://github.com/backstage/backstage/pull/22619#discussion_r1477333252
filter: 'kind:api',
filter: { kind: 'api' },
loader: () =>
import('./components/ComponentsCards').then(m => (
<m.ProvidingComponentsCard />
@@ -174,7 +174,7 @@ const apiDocsDefinitionEntityContent = EntityContentBlueprint.make({
params: {
path: '/definition',
title: 'Definition',
filter: 'kind:api',
filter: { kind: 'api' },
loader: async () =>
import('./components/ApiDefinitionCard').then(m => (
<Grid container spacing={3}>
@@ -191,7 +191,7 @@ const apiDocsApisEntityContent = EntityContentBlueprint.make({
params: {
path: '/apis',
title: 'APIs',
filter: 'kind:component',
filter: { kind: 'component' },
loader: async () =>
import('./components/ApisCards').then(m => (
<Grid container spacing={3} alignItems="stretch">
+14 -13
View File
@@ -196,25 +196,26 @@ Overriding the card extension allows you to modify how it is implemented.
Here is an example overriding the card extension with a custom component:
```tsx
import {
createFrontendModule,
createSchemaFromZod,
} from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'catalog-graph',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.makeWithOverrides({
name: 'entity-relations',
configSchema: createSchemaFromZod(z =>
z.object({
filter: z.string().optional(),
config: {
schema: {
filter: z => z.string().optional(),
// Omitting the rest of default configs for simplicity in this example
}),
),
loader: () =>
import('./components').then(m => <m.MyEntityRelationsCard />),
},
},
factory(originalFactory, { config }) {
return originalFactory({
loader: () =>
import('./components').then(m => <m.MyEntityRelationsCard />),
});
},
}),
],
});
@@ -155,6 +155,10 @@ export function createTestEntityPage(
title: content.get(EntityContentBlueprint.dataRefs.title),
}));
if (contents.length === 0 && cards.length === 0) {
return <div data-testid="empty-entity-page" />;
}
return (
<MockEntityApiProvider entity={entity}>
<EntityProvider entity={entity}>
+7 -1
View File
@@ -69,5 +69,11 @@ app:
extensions:
- entity-content:kubernetes/kubernetes:
config:
filter: kind:component,api,resource,system
filter:
kind:
$in:
- component
- api
- resource
- system
```
+50 -40
View File
@@ -99,9 +99,9 @@ This [entity card](https://github.com/backstage/backstage/blob/master/plugins/ca
Currently, this entity card extension has only one configuration:
| Config key | Default value | Description |
| ---------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | `kind:group` | An [entity filter](https://github.com/backstage/backstage/pull/21480) that determines when the card should be displayed on the entity page. |
| Config key | Default value | Description |
| ---------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | `{ kind: 'group' }` | An [entity filter](https://github.com/backstage/backstage/pull/21480) that determines when the card should be displayed on the entity page. |
This is how to configure the `group-profile` extension in the `app-config.yaml` file:
@@ -119,19 +119,23 @@ Use extension overrides for completely re-implementing the group-profile entity
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'org',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'group-profile' entity card extension provided by the 'org' plugin
name: 'group-profile',
// By default, this card will show up only for groups
filter: 'kind:group'
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomGroupProfileEntityCard />),
params: {
// By default, this card will show up only for groups
filter: { kind: 'group' },
// Returning a custom card component
loader: () =>
import('./components').then(m => (
<m.MyCustomGroupProfileEntityCard />
)),
},
}),
],
});
@@ -151,9 +155,9 @@ An [entity card](https://github.com/backstage/backstage/blob/master/plugins/cata
Currently, this entity card extension has only one configuration:
| Config key | Default value | Description |
| ---------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | `kind:group` | An [entity filter](https://github.com/backstage/backstage/pull/21480) that determines when the card should be displayed on the entity page. |
| Config key | Default value | Description |
| ---------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | `{ kind: 'group' }` | An [entity filter](https://github.com/backstage/backstage/pull/21480) that determines when the card should be displayed on the entity page. |
This is how to configure the `members-list` extension in the `app-config.yaml` file:
@@ -171,19 +175,21 @@ Use extension overrides for completely re-implementing the members-list entity c
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'org',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'members-list' entity card extension provided by the 'org' plugin
name: 'members-list',
// By default, this card will show up only for groups
filter: 'kind:group'
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomMembersListEntityCard />),
params: {
// By default, this card will show up only for groups
filter: { kind: 'group' },
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomMembersListEntityCard />),
},
}),
],
});
@@ -203,9 +209,9 @@ An [entity card](https://github.com/backstage/backstage/blob/master/plugins/cata
Currently, this entity card extension has only one configuration:
| Config key | Default value | Description |
| ---------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | `kind:group,user` | An [entity filter](https://github.com/backstage/backstage/pull/21480) that determines when the card should be displayed on the entity page. |
| Config key | Default value | Description |
| ---------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | `{ kind: { $in: ['group', 'user'] } }` | An [entity filter](https://github.com/backstage/backstage/pull/21480) that determines when the card should be displayed on the entity page. |
This is how to configure the `ownership` extension in the `app-config.yaml` file:
@@ -223,19 +229,21 @@ Use extension overrides for completely re-implementing the ownership entity card
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'org',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'ownership' entity card extension provided by the 'org' plugin
name: 'ownership',
// By default, this card will show up only for groups or users
filter: 'kind:group,user'
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomOwnershipEntityCard />),
params: {
// By default, this card will show up only for groups or users
filter: { kind: { $in: ['group', 'user'] } },
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomOwnershipEntityCard />),
},
}),
],
});
@@ -255,9 +263,9 @@ This [entity card](https://github.com/backstage/backstage/blob/master/plugins/ca
Currently, this entity card extension has only one configuration:
| Config key | Default value | Description |
| ---------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | `kind:user` | An [entity filter](https://github.com/backstage/backstage/pull/21480) that determines when the card should be displayed on the entity page. |
| Config key | Default value | Description |
| ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | `{ kind: 'user' }` | An [entity filter](https://github.com/backstage/backstage/pull/21480) that determines when the card should be displayed on the entity page. |
This is how to configure the `user-profile` extension in the `app-config.yaml` file:
@@ -275,19 +283,21 @@ Use extension overrides for completely re-implementing the user-profile entity c
```tsx
import { createFrontendModule } from '@backstage/backstage-plugin-api';
import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha';
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
export default createFrontendModule({
pluginId: 'org',
extensions: [
createEntityCardExtension({
EntityCardBlueprint.make({
// Name is necessary so the system knows that this extension will override the default 'user-profile' entity card extension provided by the 'org' plugin
name: 'user-profile',
// By default, this card will show up only for groups or users
filter: 'kind:user'
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomOwnershipEntityCard />),
params: {
// By default, this card will show up only for groups or users
filter: { kind: 'user' },
// Returning a custom card component
loader: () =>
import('./components').then(m => <m.MyCustomOwnershipEntityCard />),
},
}),
],
});