From f715898006cd677cd481fa2c77b8ed1ae2f66a9a Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Fri, 26 Feb 2021 12:34:47 +0100 Subject: [PATCH] Use an optional external route ref to link to the create component page from the api explorer --- .changeset/long-bikes-check.md | 23 +++++++++++ .changeset/red-taxis-confess.md | 23 +++++++++++ packages/app/src/App.tsx | 27 +++++++------ .../default-app/packages/app/src/App.tsx | 40 ++++++++++--------- .../ApiExplorerPage/ApiExplorerPage.tsx | 28 ++++++++----- plugins/api-docs/src/plugin.ts | 9 +++-- plugins/api-docs/src/routes.ts | 7 +++- 7 files changed, 113 insertions(+), 44 deletions(-) create mode 100644 .changeset/long-bikes-check.md create mode 100644 .changeset/red-taxis-confess.md diff --git a/.changeset/long-bikes-check.md b/.changeset/long-bikes-check.md new file mode 100644 index 0000000000..4fbc28d5aa --- /dev/null +++ b/.changeset/long-bikes-check.md @@ -0,0 +1,23 @@ +--- +'@backstage/create-app': patch +--- + +The api-docs plugin has been migrated to use an [external route reference](https://backstage.io/docs/plugins/composability#binding-external-routes-in-the-app) to dynamically link to the create component page. + +If you want to have a button that links to the scaffolder plugin from the API explorer, apply the following changes to `packages/app/src/App.tsx`: + +```diff ++ import { apiDocsPlugin } from '@backstage/plugin-api-docs'; + import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; + + const app = createApp({ + // ... + bindRoutes({ bind }) { ++ bind(apiDocsPlugin.externalRoutes, { ++ createComponent: scaffolderPlugin.routes.root, ++ }); + }, + }); +``` + +If you choose to not bind the routes, the button to create new APIs is not displayed. diff --git a/.changeset/red-taxis-confess.md b/.changeset/red-taxis-confess.md new file mode 100644 index 0000000000..4fa40d8f17 --- /dev/null +++ b/.changeset/red-taxis-confess.md @@ -0,0 +1,23 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +The api-docs plugin has been migrated to use an [external route reference](https://backstage.io/docs/plugins/composability#binding-external-routes-in-the-app) to dynamically link to the create component page. This means you need to migrate the api docs plugin to use the new extension components, as well as bind the external route. + +To bind the external route from the api docs plugin to the scaffolder template index page, make sure you have the appropriate imports and add the following to the `createApp` call: + +```ts +import { apiDocsPlugin } from '@backstage/plugin-api-docs'; +import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; + +const app = createApp({ + // ... + bindRoutes({ bind }) { + bind(apiDocsPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + }); + }, +}); +``` + +If you choose to not bind the routes, the button to create new APIs is not displayed. diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 9a7c857167..5b6f7b95fe 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -21,19 +21,29 @@ import { OAuthRequestDialog, SignInPage, } from '@backstage/core'; +import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs'; import { - catalogPlugin, - CatalogIndexPage, CatalogEntityPage, + CatalogIndexPage, + catalogPlugin, } from '@backstage/plugin-catalog'; import { CatalogImportPage } from '@backstage/plugin-catalog-import'; +import { + CostInsightsLabelDataflowInstructionsPage, + CostInsightsPage, + CostInsightsProjectGrowthInstructionsPage, +} from '@backstage/plugin-cost-insights'; import { ExplorePage } from '@backstage/plugin-explore'; +import { GcpProjectsPage } from '@backstage/plugin-gcp-projects'; import { GraphiQLPage } from '@backstage/plugin-graphiql'; import { LighthousePage } from '@backstage/plugin-lighthouse'; +import { NewRelicPage } from '@backstage/plugin-newrelic'; import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder'; +import { SearchPage } from '@backstage/plugin-search'; import { TechRadarPage } from '@backstage/plugin-tech-radar'; import { TechdocsPage } from '@backstage/plugin-techdocs'; import { UserSettingsPage } from '@backstage/plugin-user-settings'; +import AlarmIcon from '@material-ui/icons/Alarm'; import React from 'react'; import { hot } from 'react-hot-loader/root'; import { Navigate, Route } from 'react-router'; @@ -42,16 +52,6 @@ import { EntityPage } from './components/catalog/EntityPage'; import Root from './components/Root'; import { providers } from './identityProviders'; import * as plugins from './plugins'; -import AlarmIcon from '@material-ui/icons/Alarm'; -import { ApiExplorerPage } from '@backstage/plugin-api-docs'; -import { GcpProjectsPage } from '@backstage/plugin-gcp-projects'; -import { NewRelicPage } from '@backstage/plugin-newrelic'; -import { SearchPage } from '@backstage/plugin-search'; -import { - CostInsightsLabelDataflowInstructionsPage, - CostInsightsPage, - CostInsightsProjectGrowthInstructionsPage, -} from '@backstage/plugin-cost-insights'; const app = createApp({ apis, @@ -76,6 +76,9 @@ const app = createApp({ bind(catalogPlugin.externalRoutes, { createComponent: scaffolderPlugin.routes.root, }); + bind(apiDocsPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + }); }, }); diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index d4f1178899..b2c9d10eb0 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -1,29 +1,28 @@ -import React from 'react'; import { - createApp, AlertDisplay, + createApp, + FlatRoutes, OAuthRequestDialog, SidebarPage, - FlatRoutes, } from '@backstage/core'; +import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs'; +import { + CatalogEntityPage, + CatalogIndexPage, + catalogPlugin, +} from '@backstage/plugin-catalog'; +import { CatalogImportPage } from '@backstage/plugin-catalog-import'; +import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder'; +import { SearchPage } from '@backstage/plugin-search'; +import { TechRadarPage } from '@backstage/plugin-tech-radar'; +import { TechdocsPage } from '@backstage/plugin-techdocs'; +import { UserSettingsPage } from '@backstage/plugin-user-settings'; +import React from 'react'; +import { Navigate, Route } from 'react-router'; import { apis } from './apis'; +import { EntityPage } from './components/catalog/EntityPage'; import * as plugins from './plugins'; import { AppSidebar } from './sidebar'; -import { Route, Navigate } from 'react-router'; -import { - catalogPlugin, - CatalogIndexPage, - CatalogEntityPage, -} from '@backstage/plugin-catalog'; -import { TechdocsPage } from '@backstage/plugin-techdocs'; -import { CatalogImportPage } from '@backstage/plugin-catalog-import'; -import { TechRadarPage } from '@backstage/plugin-tech-radar'; -import { SearchPage } from '@backstage/plugin-search'; -import { UserSettingsPage } from '@backstage/plugin-user-settings'; -import { ApiExplorerPage } from '@backstage/plugin-api-docs'; - -import { EntityPage } from './components/catalog/EntityPage'; -import { scaffolderPlugin, ScaffolderPage } from '@backstage/plugin-scaffolder'; const app = createApp({ apis, @@ -32,7 +31,10 @@ const app = createApp({ bind(catalogPlugin.externalRoutes, { createComponent: scaffolderPlugin.routes.root, }); - } + bind(apiDocsPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + }); + }, }); const AppProvider = app.getProvider(); diff --git a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx b/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx index df404ce7cd..83d0bfc2ba 100644 --- a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx +++ b/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx @@ -14,16 +14,24 @@ * limitations under the License. */ -import { Content, ContentHeader, SupportButton, useApi } from '@backstage/core'; +import { + Content, + ContentHeader, + SupportButton, + useApi, + useRouteRef, +} from '@backstage/core'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { Button } from '@material-ui/core'; import React from 'react'; import { Link as RouterLink } from 'react-router-dom'; import { useAsync } from 'react-use'; +import { createComponentRouteRef } from '../../routes'; import { ApiExplorerTable } from '../ApiExplorerTable'; import { ApiExplorerLayout } from './ApiExplorerLayout'; export const ApiExplorerPage = () => { + const createComponentLink = useRouteRef(createComponentRouteRef); const catalogApi = useApi(catalogApiRef); const { loading, error, value: catalogResponse } = useAsync(() => { return catalogApi.getEntities({ filter: { kind: 'API' } }); @@ -33,14 +41,16 @@ export const ApiExplorerPage = () => { - + {createComponentLink && ( + + )} All your APIs null; @@ -23,3 +23,8 @@ export const rootRoute = createRouteRef({ path: '/api-docs', title: 'APIs', }); + +export const createComponentRouteRef = createExternalRouteRef({ + id: 'create-component', + optional: true, +});