Update plugin software catalog integration docs
Updates ./docs/plugins/integrating-plugin-into-software-catalog.md as the docs had fallen out of date. Prefers educating plugin authors about useEntity as that is the defacto method for reading entities. Takes the author through an example of adding their plugin content to an existing entity element. Signed-off-by: Michael Stergianis <mstergianis@vmware.com> Signed-off-by: Daniel Bravo <dbravo@vmware.com>
This commit is contained in:
committed by
Daniel Bravo
parent
ded1477a27
commit
d646407c76
@@ -10,8 +10,8 @@ description: How to integrate a plugin into software catalog
|
||||
## Steps
|
||||
|
||||
1. [Create a plugin](#create-a-plugin)
|
||||
1. [Export a router with relative routes](#export-a-router)
|
||||
1. [Import and use router in the APP](#import-and-use-router-in-the-app)
|
||||
1. [Reading entities from within your plugin](#reading-entities-from-within-your-plugin)
|
||||
1. [Import your plugin and embed in the entities page](#import-your-plugin-and-embed-in-the-entities-page)
|
||||
|
||||
### Create a plugin
|
||||
|
||||
@@ -28,98 +28,92 @@ $ yarn create-plugin
|
||||
Creating the plugin...
|
||||
```
|
||||
|
||||
### Export a router
|
||||
### Reading entities from within your plugin
|
||||
|
||||
Now in the plugin you have a `Router.tsx` file in the `src` folder. By default
|
||||
it contains only one example route. Create a routing structure needed for your
|
||||
plugin, keeping in mind that the whole set of routes defined here are going to
|
||||
be mounted under some different route in the App.
|
||||
|
||||
Example:
|
||||
|
||||
`my-plugin` consists of 2 different views - `/me` and `/about`. I envision
|
||||
people integrating it into plugin catalog as a tab named "MyPlugin". Then, my
|
||||
`Routes.tsx` for the plugin is going to look like:
|
||||
You can access the currently selected entity using the backstage api
|
||||
`useEntity`. For example,
|
||||
|
||||
```tsx
|
||||
<Routes>
|
||||
<Route path="/me" element={<MePage />} />
|
||||
<Route path="/about" element={<AboutPage />} />
|
||||
</Routes>
|
||||
```
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
|
||||
(where MePage and AboutPage are 2 components defined in your plugin and imported
|
||||
accordingly inside `Router.tsx`)
|
||||
export const MyPluginEntityContent = () => {
|
||||
const { entity, loading, error, refresh } = useEntity();
|
||||
|
||||
> Pay attention, if your `MePage` references the `AboutPage` it needs to do it
|
||||
> through link to `about`, not `/about`. This allows react-router v6 to enable
|
||||
> its relative routing mechanism. Read more -
|
||||
> https://reacttraining.com/blog/react-router-v6-pre/#relative-route-path-and-link-to
|
||||
|
||||
### Import and use router in the APP
|
||||
|
||||
In the `app/src/components/catalog/EntityPage.tsx` (app === your folder,
|
||||
containing Backstage app) import your created Router:
|
||||
|
||||
```tsx
|
||||
import { Router as MyPluginRouter } from '@backstage/plugin-my-plugin;
|
||||
```
|
||||
|
||||
Now, you need to mount `MyPluginRouter` onto some route, for example if you had:
|
||||
|
||||
```tsx
|
||||
const DefaultEntityPage = ({ entity }: { entity: Entity }) => (
|
||||
<EntityPageLayout>
|
||||
<EntityPageLayout.Content
|
||||
path="/"
|
||||
title="Overview"
|
||||
element={<OverviewPage entity={entity} />}
|
||||
/>
|
||||
</EntityPageLayout>
|
||||
);
|
||||
```
|
||||
|
||||
after you add your code it becomes:
|
||||
|
||||
```tsx
|
||||
const DefaultEntityPage = ({ entity }: { entity: Entity }) => (
|
||||
<EntityPageLayout>
|
||||
<EntityPageLayout.Content
|
||||
path="/"
|
||||
title="Overview"
|
||||
element={<OverviewPage entity={entity} />}
|
||||
/>
|
||||
<EntityPageLayout.Content
|
||||
path="/my-plugin"
|
||||
title="My Plugin"
|
||||
element={<MyPluginRouter entity={entity} />}
|
||||
/>
|
||||
</EntityPageLayout>
|
||||
);
|
||||
```
|
||||
|
||||
All of magic happens thanks to the `EntityPageLayout` component, which comes as
|
||||
an export from `@backstage/plugin-catalog` package.
|
||||
|
||||
```tsx
|
||||
type EntityPageLayoutContentProps = {
|
||||
/**
|
||||
* Going to be transformed into react-router v6
|
||||
* path under the hood. Read more at https://reacttraining.com/blog/react-router-v6-pre
|
||||
*/
|
||||
path: string;
|
||||
/**
|
||||
* Gets transformed into the title for the tab
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Element that is rendered when the location
|
||||
* matches the path provided
|
||||
*/
|
||||
element: JSX.Element;
|
||||
// Do something with the entity data...
|
||||
};
|
||||
```
|
||||
|
||||
> You can either pass the entity from App to the plugin's router as a prop or
|
||||
> use `useEntity` hook from `@backstage/plugin-catalog` directly inside your
|
||||
> plugin.
|
||||
Internally `useEntity` makes use of
|
||||
[react `Context`s](https://reactjs.org/docs/context.html). The entity context is
|
||||
provided by the entity page into which your plugin will be embedded.
|
||||
|
||||
### Import your plugin and embed in the entities page
|
||||
|
||||
To begin, you will need to import your plugin in the entities page. Located at
|
||||
`packages/app/src/components/Catalog/EntityPage.tsx` from the root package of
|
||||
your backstage app.
|
||||
|
||||
```tsx
|
||||
import { MyPluginEntityContent } from '@backstage/plugin-my-plugin;
|
||||
```
|
||||
|
||||
To add your component to the Entity view, you will need to modify the
|
||||
`packages/app/src/components/Catalog/EntityPage.tsx`. Depending on the needs of
|
||||
your plugin, you may only care about certain kinds of
|
||||
[entities](https://backstage.io/docs/features/software-catalog/descriptor-format),
|
||||
each of which has its own
|
||||
[element](https://reactjs.org/docs/rendering-elements.html) for rendering. This
|
||||
functionality is handled by the `EntitySwitch` component:
|
||||
|
||||
```tsx
|
||||
export const entityPage = (
|
||||
<EntitySwitch>
|
||||
<EntitySwitch.Case if={isKind('component')} children={componentPage} />
|
||||
<EntitySwitch.Case if={isKind('api')} children={apiPage} />
|
||||
<EntitySwitch.Case if={isKind('group')} children={groupPage} />
|
||||
<EntitySwitch.Case if={isKind('user')} children={userPage} />
|
||||
<EntitySwitch.Case if={isKind('system')} children={systemPage} />
|
||||
<EntitySwitch.Case if={isKind('domain')} children={domainPage} />
|
||||
|
||||
<EntitySwitch.Case>{defaultEntityPage}</EntitySwitch.Case>
|
||||
</EntitySwitch>
|
||||
);
|
||||
```
|
||||
|
||||
At this point, you will need to modify the specific page where you want your
|
||||
component to appear. If you are extending the Software Catalog model you will
|
||||
need to add a new case to the `EntitySwitch`. For adding a plugin to an existing
|
||||
component type, you modify the existing page. For example, if you want to add
|
||||
your plugin to the `systemPage`, you can add a new tab by adding an
|
||||
`EntityLayout.Route` such as below:
|
||||
|
||||
```tsx
|
||||
const systemPage = (
|
||||
<EntityLayout>
|
||||
<EntityLayout.Route path="/" title="Overview">
|
||||
<Grid container spacing={3} alignItems="stretch">
|
||||
<Grid item md={6}>
|
||||
<EntityAboutCard variant="gridItem" />
|
||||
</Grid>
|
||||
<Grid item md={6}>
|
||||
<EntityHasComponentsCard variant="gridItem" />
|
||||
</Grid>
|
||||
<Grid item md={6}>
|
||||
<EntityHasApisCard variant="gridItem" />
|
||||
</Grid>
|
||||
<Grid item md={6}>
|
||||
<EntityHasResourcesCard variant="gridItem" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</EntityLayout.Route>
|
||||
<EntityLayout.Route path="/diagram" title="Diagram">
|
||||
<EntitySystemDiagramCard />
|
||||
</EntityLayout.Route>
|
||||
|
||||
{/* Adding a new tab to the system view */}
|
||||
<EntityLayout.Route path="/your-custom-route" title="CustomTitle">
|
||||
<MyPluginEntityContent />
|
||||
</EntityLayout.Route>
|
||||
</EntityLayout>
|
||||
);
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user