From 34ebdb5f199dc0d304d953ef3a90666da108130b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 12 Dec 2023 16:30:24 +0100 Subject: [PATCH] flesh out all but the configuration section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/frontend-system/utility-apis/01-index.md | 10 +- .../utility-apis/02-creating.md | 10 +- .../utility-apis/03-consuming.md | 2 +- .../utility-apis/04-configuring.md | 2 + .../utility-apis/05-migrating.md | 131 +++++++++++++++++- 5 files changed, 143 insertions(+), 12 deletions(-) diff --git a/docs/frontend-system/utility-apis/01-index.md b/docs/frontend-system/utility-apis/01-index.md index b6eb22706d..a6c9f6a784 100644 --- a/docs/frontend-system/utility-apis/01-index.md +++ b/docs/frontend-system/utility-apis/01-index.md @@ -8,7 +8,7 @@ description: Working with Utility APIs in the New Frontend System As described [in the architecture section](../architecture/06-utility-apis.md), utility APIs are pieces of shared functionality - interfaces that can be requested by plugins to use. They are defined by a TypeScript interface as well as a reference (an "API ref") used to access its implementation. They can be provided both by plugins and the core framework, and are themselves [extensions](../architecture/03-extensions.md) that can accept inputs, be declaratively configured in your app-config, or transparently be replaced entirely with custom implementations that fulfill the same contract. -## Creating Utility APIs +## Creating utility APIs > For details, [see the main article](./02-creating.md). @@ -18,7 +18,7 @@ Some are available out of the box, such as the API for reading app configuration [The main article](./02-creating.md) describes the process of creating and exposing utility APIs of your own, for sharing functionality or configurability across plugins and apps. -## Consuming Utility APIs +## Consuming utility APIs > For details, [see the main article](./03-consuming.md). @@ -28,10 +28,14 @@ Some utility APIs in turn depend on other utility APIs. This powerful composabil These are described in detail in [the main article](./03-consuming.md) -## Configuring Utility APIs +## Configuring utility APIs > For details, [see the main article](./04-configuring.md). Most utility APIs are usable directly without any configuration. But they are proper extensions, and can therefore have their implementations entirely swapped out by your app for advanced use cases. They can also be built with the ability to configured in your app-config, or to have inputs that extend their functionality. These cases are all described in [the main article](./04-configuring.md). + +## Migrating from the old frontend system + +If you want to learn how to migrate your own utility APIs from the old frontend system to the new one, that's described in [a dedicated migration guide](./05-migrating.md). diff --git a/docs/frontend-system/utility-apis/02-creating.md b/docs/frontend-system/utility-apis/02-creating.md index 58655a24f5..92bd623744 100644 --- a/docs/frontend-system/utility-apis/02-creating.md +++ b/docs/frontend-system/utility-apis/02-creating.md @@ -62,12 +62,8 @@ const workApi = createApiExtension({ factory: () => createApiFactory({ api: workApiRef, - deps: { - configApi: configApiRef, - }, - factory: ({ configApi }) => { - return new WorkImpl({ configApi }); - }, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => new WorkImpl({ configApi }), }), }); @@ -81,7 +77,7 @@ export default createPlugin({ }); ``` -For illustration we make a skeleton implementation class and the API extension and factory for it, in the same file. These are not exported to the public surface of the plugin package; only the plugin is, as the default export. +For illustration we make a skeleton implementation class and the API extension and factory for it, in the same file. These are not exported to the public surface of the plugin package; only the plugin is, as the default export. Users who install the plugin will now get the utility API automatically as well. The code also illustrates how the API factory declares a dependency on another utility API - the core config API in this case. An instance of that utility API is then provided to the factory function. diff --git a/docs/frontend-system/utility-apis/03-consuming.md b/docs/frontend-system/utility-apis/03-consuming.md index 7c339f1bf9..b24489a21d 100644 --- a/docs/frontend-system/utility-apis/03-consuming.md +++ b/docs/frontend-system/utility-apis/03-consuming.md @@ -43,9 +43,9 @@ Your utility APIs can depend on other utility APIs in their factories. You do th ```tsx import { + configApiRef, createApiExtension, createApiFactory, - configApiRef, discoveryApiRef, } from '@backstage/frontend-plugin-api'; import { MyApiImpl } from './MyApiImpl'; diff --git a/docs/frontend-system/utility-apis/04-configuring.md b/docs/frontend-system/utility-apis/04-configuring.md index d1de1aa070..b56cd10c5c 100644 --- a/docs/frontend-system/utility-apis/04-configuring.md +++ b/docs/frontend-system/utility-apis/04-configuring.md @@ -23,3 +23,5 @@ description: Configuring, extending, and overriding utility APIs > TODO ## Replacing a utility API implementation + +> TODO diff --git a/docs/frontend-system/utility-apis/05-migrating.md b/docs/frontend-system/utility-apis/05-migrating.md index 0334e80f0b..f3e85ceaf4 100644 --- a/docs/frontend-system/utility-apis/05-migrating.md +++ b/docs/frontend-system/utility-apis/05-migrating.md @@ -6,4 +6,133 @@ sidebar_label: Migrating description: Migrating Utility APIs from the old frontend system --- -> TODO +If you are migrating your plugins or app over from the old frontend system, there are a few things to keep in mind in regards to utility APIs. + +## Overview + +- Migrate your repo overall to the latest release of Backstage +- Follow the plugin migration guide +- Change your package dependencies from `core-*-api` to `frontend-*-api` +- Change the imports in your code from `core-*-api` to `frontend-*-api` +- Keep the TypeScript interface and API ref exported as they were, except possibly reconsidering the choice of ID of the latter +- Wrap the old API factory call in an extension using `createApiExtension` +- Make sure that this extension is referenced by your migrated plugin + +## Prerequisites + +This guide assumes that you first [upgrade your repo](../../getting-started/keeping-backstage-updated.md) to the latest release of Backstage. This ensures that you do not have to fight several types of incompatibilities and updates at the same time. + +## Dependency changes + +In this article we will discuss some interfaces that you used to import from the `@backstage/core-plugin-api` package. Those are now generally moved over to `@backstage/frontend-plugin-api`, so you will want to update both your `package.json` and your source code. This applies both in your `-react` package and your main plugin package. + +First `package.json`. The following commands are examples - note that they refer to `plugins/example`, which you'll have to update to the actual folder name that your package to migrate is in. + +```bash title="from your repo root" +yarn --cwd plugins/example remove @backstage/core-plugin-api ; +yarn --cwd plugins/example add @backstage/frontend-plugin-api +``` + +Now in all of the code files in that package: + +```tsx title="in your source code" +/* highlight-remove-next-line */ +import { createApiRef } from '@backstage/core-plugin-api'; +/* highlight-add-next-line */ +import { createApiRef } from '@backstage/frontend-plugin-api'; +``` + +These can typically be search-and-replaced wholesale - the interfaces in the new package are mostly identical to the old one. The `createApiRef` is just an example, and the same replacement makes sense for all of the other symbols from the core package as well. + +## React package interface and ref changes + +Let's begin with [your `-react` package](../../architecture-decisions/adr011-plugin-package-structure.md). The act of exporting TypeScript interfaces and API refs have not changed from the old system. You can typically keep those as-is. For illustrative purposes, this is an example of an interface and its API ref: + +```tsx title="in @internal/plugin-example-react" +import { createApiRef } from '@backstage/frontend-plugin-api'; + +/** + * Performs some work. + * @oublic + */ +export interface WorkApi { + doWork(): Promise; +} + +/** + * The work interface for the Example plugin. + * @public + */ +export const workApiRef = createApiRef({ + id: 'plugin.example.work', +}); +``` + +In this example, the plugin ID already follows the common naming convention. If it doesn't, you may want to consider renaming that ID at this point. Don't worry, this won't hurt consumers in the old frontend system since the ID is mostly used for debugging purposes there. In the new system, it's much more important and appears in app-config files and similar. + +Note at the top of the file that it uses the updated import from `@backstage/frontend-plugin-api` that we migrated in the previous section, instead of the old `@backstage/core-plugin-api`. + +## Plugin package changes + +Now let's turn to the main plugin package where the plugin itself is exported. You will probably already have a `createPlugin` call in here. Before we changed the `core-plugin-api` imports it'll have looked somewhat similar to the following: + +```tsx title="in @internal/plugin-example, NOTE THIS IS LEGACY CODE" +import { + configApiRef, + createPlugin, + createApiFactory, +} from '@backstage/core-plugin-api'; +import { workApiRef } from '@internal/plugin-example-react'; +import { WorkImpl } from './WorkImpl'; + +const workApi = createApiFactory({ + api: workApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => new WorkImpl({ configApi }), +}); + +/** @public */ +export const catalogPlugin = createPlugin({ + id: 'example', + apis: [workApi], +}); +``` + +The major changes we'll make are + +- Change the import to the new package as per the top section of this guide +- Wrap the existing API factory in a `createApiExtension` +- Change to the new version of `createPlugin` which exports this extension +- Change the plugin export to be the default instead + +```tsx title="in @internal/plugin-example" +import { + configApiRef, + createPlugin, + createApiFactory, + createApiExtension, +} from '@backstage/frontend-plugin-api'; +import { workApiRef } from '@internal/plugin-example-react'; +import { WorkImpl } from './WorkImpl'; + +const workApi = createApiExtension({ + api: workApiRef, + factory: () => + // The factory itself is unchanged + createApiFactory({ + api: workApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => new WorkImpl({ configApi }), + }), +}); + +/** @public */ +export default createPlugin({ + id: 'example', + extensions: [workApi], +}); +``` + +## Further work + +Since utility APIs are now complete extensions, you may want to take a bigger look at how they used to be used, and what the new frontend system offers. You may for example consider [adding configurability or inputs](./04-configuring.md) to your API, if that makes sense for your current application.