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:
Michael Stergianis
2021-09-16 14:49:28 -04:00
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>
);
```