From 4727161be97d4e7899207f819e0d844e2033e292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 19 Jan 2023 13:19:06 +0100 Subject: [PATCH] more updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../architecture/03-services.md | 9 ++ .../building-backends/08-migrating.md | 151 ++++++++++++------ 2 files changed, 115 insertions(+), 45 deletions(-) diff --git a/docs/backend-system/architecture/03-services.md b/docs/backend-system/architecture/03-services.md index 27fea27db9..71bd95dd1e 100644 --- a/docs/backend-system/architecture/03-services.md +++ b/docs/backend-system/architecture/03-services.md @@ -101,3 +101,12 @@ const backend = createBackend({ ], }); ``` + +### Migrating Your Own Utilities Into Services + +As your backends grow in complexity, you may have started writing utility +classes with shared backend functionality, that you pass down into your plugins. +Maybe you have placed them into your `PluginEnvironment` in +`packages/backend/src/types.ts`. If this applies to you, you'll probably be +interested in migrating those to become proper backend services according to the +new backend system, so that they can be dependency injected into your backends. diff --git a/docs/backend-system/building-backends/08-migrating.md b/docs/backend-system/building-backends/08-migrating.md index 86f8515d87..3a59599518 100644 --- a/docs/backend-system/building-backends/08-migrating.md +++ b/docs/backend-system/building-backends/08-migrating.md @@ -51,7 +51,7 @@ async function main() { const todoEnv = useHotMemoize(module, () => createEnv('todo')); // repeated for N plugins const apiRouter = Router(); apiRouter.use('/todo', await todo(todoEnv)); // repeated for N plugins - // ... return composite router + // ... wire up and start http server } module.hot?.accept(); @@ -166,31 +166,17 @@ custom API, so we use a helper function to transform that particular one. To make additions as mentioned above to the environment, you will start to get into the weeds of how the backend system wiring works. You'll need to have a service reference, and a service factory that performs the actual creation of -your service. For now, let's add them directly in the same file, and then you -can move them out into a separate file at a later time. +your service. Please see [the services +article](../architecture/03-services.md#defining-a-service) to learn how to +create a service ref and its default factory. You can place that code directly +in the index file for now if you want, or near the actual implementation class +in question. -In this example, we'll assume that your added environment item is named "example". +In this example, we'll assume that your added environment field is named +`example`, and the created ref is named `exampleServiceRef`. ```diff --import { coreServices } from '@backstage/backend-plugin-api'; -+import { coreServices, createServiceFactory } from '@backstage/backend-plugin-api'; -+import { ExampleApi, ExampleImpl } from ''; - -+const exampleServiceRef = createServiceRef({ -+ id: 'example', -+ scope: 'plugin', // can be 'root' or 'plugin' -+ defaultFactory: async service => createServiceFactory({ -+ service, -+ // optional; just an example of how dependencies work -+ deps: { -+ logger: coreServices.logger, -+ }, -+ // here instances of the declared dependencies are injected -+ async factory({ logger }) { -+ // return your ExampleImpl -+ }, -+ }), -+}); ++import { exampleServiceRef } from ''; // if the definition is elsewhere const legacyPlugin = makeLegacyPlugin( { @@ -203,7 +189,13 @@ In this example, we'll assume that your added environment item is named "example ); ``` -After this, your backend will know how to instantiate your thing on demand. +After this, your backend will know how to instantiate your thing on demand and +place it in the legacy plugin environment. + +> NOTE: If you happen to be dealing with a service ref that does NOT have a +> default implementation, but rather has a separate service factory, then you +> will also need to import that factory and pass it to the `services` array +> argument of `createBackend`. # Cleaning Up the Plugins Folder @@ -213,32 +205,101 @@ later time. For third party backend plugins, in particular the larger core plugins that are maintained by the Backstage maintainers, you may find that they have already -been migrated to the new backend system. Let's try to clean up the `plugins` -folder a bit. +been migrated to the new backend system. This section describes some specific +such migrations you can make. -Each plugin file is under your control, and it was added as part of following -their installation instructions. You were then free to start tweaking those -files and adding customizations. The simplest case is when you did NOT perform -any customizations at all. For those plugins, you can in general just delete the -corresponding file in the `plugins` folder, and instead importing the backend -plugin from the package directly. +> NOTE: For each of these, note that your backend still needs to have a +> dependency (e.g. in `packages/backend/package.json`) to those plugin packages, +> and they still need to be configured properly in your app-config. Those +> mechanisms still work just the same as they used to in the old backend system. + +## The App Plugin + +The app backend plugin that serves the frontend from the backend can trivially +be used in its new form. ```diff - import { createBackend } from '@backstage/backend-defaults'; - import { legacyPlugin } from '@backstage/backend-common'; -+import { catalogPlugin } from '@backstage/plugin-catalog-backend'; + // packages/backend/src/index.ts ++import { appPlugin } from '@backstage/plugin-app-backend'; const backend = createBackend(); - backend.add(legacyPlugin('todo', import('./plugins/todo'))); -+backend.add(catalogPlugin()); - backend.start(); ++backend.add(appPlugin({ appPackageName: 'app' })); ``` -This will install a fully functioning catalog backend plugin, in its default -state (not amended with any custom processors or providers, for example). You -still need to have the `@backstage/plugin-catalog-backend` dependency in your -`package.json`. You also need to have your app-config set up properly like -before; that subsystem works the same as it used to even though its -instantiation now happens behind the scenes. +This is an example of how options can be passed into some backend plugins. The +app plugin specifically needs to know the name of the package that holds the +frontend code. This is the `"name"` field in that package's `package.json`, +typically found in your `packages/app` folder. By default it's just plain "app". -You can now delete `plugins/catalog.ts`. +You should be able to delete the `plugins/app.ts` file at this point. + +## The Catalog Plugin + +A basic installation of the catalog plugin looks as follows. + +```diff + // packages/backend/src/index.ts ++import { catalogPlugin } from '@backstage/plugin-catalog-backend'; ++import { scaffolderCatalogModule } from '@backstage/plugin-scaffolder-backend'; + + const backend = createBackend(); ++backend.add(catalogPlugin()); ++backend.add(scaffolderCatalogModule()); +``` + +Note that this also installs a module from the scaffolder, namely the one which +enables the use of the `Template` kind. In the unlikely event that you do not +use templates at all, you can remove those lines. + +If you have other customizations made to `plugins/catalog.ts`, such as adding +custom processors or entity providers, read on. Otherwise, you should be able to +just delete the `plugins/catalog.ts` file at this point. + +You will use the [extension points](../architecture/05-extension-points.md) +mechanism to extend or tweak the functionality of the catalog. To do that, +you'll make your own bespoke [module](../architecture/06-modules.md) which +depends on the appropriate extension point and interacts with it. + +```diff + // packages/backend/src/index.ts ++import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node'; ++import { createBackendModule } from '@backstage/backend-plugin-api'; + ++const catalogExtensionsModule = createBackendModule({ ++ pluginId: 'catalog', // name of the plugin that the module is targeting ++ moduleId: 'extensions', // you can choose this one freely ++ register(env) { ++ env.registerInit({ ++ deps: { ++ catalog: catalogProcessingExtensionPoint, ++ // ... and other dependencies as needed ++ }, ++ init({ catalog /* ..., other dependencies */ }) { ++ // Here you have the opportunity to interact with the catalog extension ++ // point before the catalog plugin itself gets instantiated ++ catalog.addEntityProvider(new MyEntityProvider()); // just an example ++ catalog.addProcessor(new MyProcessor()); // just an example ++ }, ++ }); ++ }, ++}); + + const backend = createBackend(); + backend.add(catalogPlugin()); + backend.add(scaffolderCatalogModule()); ++backend.add(catalogExtensionsModule()); +``` + +This also requires that you have a dependency on the catalog node package, if +you didn't already have one. + +```bash +# from the repository root +yarn add --cwd packages/backend @backstage/plugin-catalog-node +``` + +Here we've placed the module directly in the backend index file just to get +going easily, but feel free to move it out to where it fits best. As you migrate +your entire plugin flora to the new backend system, you will probably make more +and more of these modules as "first class" things, living right next to the +implementations that they represent, and being exported from there.