diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index 09158295fde07c..36883440e0b5dc 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -1,35 +1,56 @@ # Migrating legacy plugins to the new platform -* [Overview](#overview) - * [Architecture](#architecture) - * [Services](#services) - * [Integrating with other plugins](#integrating-with-other-plugins) - * [Challenges to overcome with legacy plugins](#challenges-to-overcome-with-legacy-plugins) - * [Plan of action](#plan-of-action) -* [Server-side plan of action](#server-side-plan-of-action) - * [De-couple from hapi.js server and request objects](#de-couple-from-hapijs-server-and-request-objects) - * [Introduce new plugin definition shim](#introduce-new-plugin-definition-shim) - * [Switch to new platform services](#switch-to-new-platform-services) - * [Migrate to the new plugin system](#migrate-to-the-new-plugin-system) -* [Browser-side plan of action](#browser-side-plan-of-action) - * [Move UI modules into plugins](#move-ui-modules-into-plugins) - * [Provide plugin extension points decoupled from angular.js](#provide-plugin-extension-points-decoupled-from-angularjs) - * [Move all webpack alias imports into uiExport entry files](#move-all-webpack-alias-imports-into-uiexport-entry-files) - * [Switch to new platform services](#switch-to-new-platform-services-1) - * [Migrate to the new plugin system](#migrate-to-the-new-plugin-system-1) -* [Frequently asked questions](#frequently-asked-questions) - * [Is migrating a plugin an all-or-nothing thing?](#is-migrating-a-plugin-an-all-or-nothing-thing) - * [Do plugins need to be converted to TypeScript?](#do-plugins-need-to-be-converted-to-typescript) - * [Can static code be shared between plugins?](#can-static-code-be-shared-between-plugins) - * [How can I avoid passing Core services deeply within my UI component tree?](#how-can-i-avoid-passing-core-services-deeply-within-my-ui-component-tree) - * [How is "common" code shared on both the client and server?](#how-is-common-code-shared-on-both-the-client-and-server) - * [When does code go into a plugin, core, or packages?](#when-does-code-go-into-a-plugin-core-or-packages) - * [How do I build my shim for New Platform services?](#how-do-i-build-my-shim-for-new-platform-services) - * aka, where did everything move to? -* [How to](#how-to) - * [Configure plugin](#configure-plugin) - * [Mock new platform services in tests](#mock-new-platform-services-in-tests) - * [Provide Legacy Platform API to the New platform plugin](#provide-legacy-platform-api-to-the-new-platform-plugin) +- [Migrating legacy plugins to the new platform](#migrating-legacy-plugins-to-the-new-platform) + - [Overview](#overview) + - [Architecture](#architecture) + - [Services](#services) + - [Integrating with other plugins](#integrating-with-other-plugins) + - [Challenges to overcome with legacy plugins](#challenges-to-overcome-with-legacy-plugins) + - [Challenges on the server](#challenges-on-the-server) + - [Challenges in the browser](#challenges-in-the-browser) + - [Plan of action](#plan-of-action) + - [Server-side plan of action](#server-side-plan-of-action) + - [De-couple from hapi.js server and request objects](#de-couple-from-hapijs-server-and-request-objects) + - [Introduce new plugin definition shim](#introduce-new-plugin-definition-shim) + - [Switch to new platform services](#switch-to-new-platform-services) + - [Migrate to the new plugin system](#migrate-to-the-new-plugin-system) + - [Browser-side plan of action](#browser-side-plan-of-action) + - [1. Create a plugin definition file](#1-create-a-plugin-definition-file) + - [2. Export all static code and types from `public/index.ts`](#2-export-all-static-code-and-types-from-publicindexts) + - [3. Export your runtime contract](#3-export-your-runtime-contract) + - [4. Move "owned" UI modules into your plugin and expose them from your public contract](#4-move-owned-ui-modules-into-your-plugin-and-expose-them-from-your-public-contract) + - [5. Provide plugin extension points decoupled from angular.js](#5-provide-plugin-extension-points-decoupled-from-angularjs) + - [6. Move all webpack alias imports into uiExport entry files](#6-move-all-webpack-alias-imports-into-uiexport-entry-files) + - [7. Switch to new platform services](#7-switch-to-new-platform-services) + - [8. Migrate to the new plugin system](#8-migrate-to-the-new-plugin-system) + - [Bonus: Tips for complex migration scenarios](#bonus-tips-for-complex-migration-scenarios) + - [Frequently asked questions](#frequently-asked-questions) + - [Is migrating a plugin an all-or-nothing thing?](#is-migrating-a-plugin-an-all-or-nothing-thing) + - [Do plugins need to be converted to TypeScript?](#do-plugins-need-to-be-converted-to-typescript) + - [Can static code be shared between plugins?](#can-static-code-be-shared-between-plugins) + - [Background](#background) + - [What goes wrong if I do share modules with state?](#what-goes-wrong-if-i-do-share-modules-with-state) + - [How to decide what code can be statically imported](#how-to-decide-what-code-can-be-statically-imported) + - [Concrete Example](#concrete-example) + - [How can I avoid passing Core services deeply within my UI component tree?](#how-can-i-avoid-passing-core-services-deeply-within-my-ui-component-tree) + - [How is "common" code shared on both the client and server?](#how-is-common-code-shared-on-both-the-client-and-server) + - [When does code go into a plugin, core, or packages?](#when-does-code-go-into-a-plugin-core-or-packages) + - [How do I build my shim for New Platform services?](#how-do-i-build-my-shim-for-new-platform-services) + - [Client-side](#client-side) + - [Core services](#core-services) + - [Plugins for shared application services](#plugins-for-shared-application-services) + - [Server-side](#server-side) + - [Core services](#core-services-1) + - [UI Exports](#ui-exports) + - [How to](#how-to) + - [Configure plugin](#configure-plugin) + - [Mock new platform services in tests](#mock-new-platform-services-in-tests) + - [Writing mocks for your plugin](#writing-mocks-for-your-plugin) + - [Using mocks in your tests](#using-mocks-in-your-tests) + - [What about karma tests?](#what-about-karma-tests) + - [Provide Legacy Platform API to the New platform plugin](#provide-legacy-platform-api-to-the-new-platform-plugin) + - [On the server side](#on-the-server-side) + - [On the client side](#on-the-client-side) Make no mistake, it is going to take a lot of work to move certain plugins to the new platform. Our target is to migrate the entire repo over to the new platform throughout 7.x and to remove the legacy plugin system no later than 8.0, and this is only possible if teams start on the effort now. @@ -229,7 +250,7 @@ With that specified in the plugin manifest, the appropriate interfaces are then **demo plugin.ts:** ```ts -import { CoreSetup, CoreStart } from '../../../core/server'; +import { CoreSetup, CoreStart } from 'src/core/server'; import { FoobarPluginSetup, FoobarPluginStop } from '../../foobar/server'; interface DemoSetupPlugins { @@ -423,7 +444,7 @@ export default (kibana) => { } } - // HTTP functionality from core + // HTTP functionality from legacy server.route({ path: '/api/demo_plugin/search', method: 'POST', @@ -448,35 +469,47 @@ We now move this logic into a new plugin definition, which is based off of the c ```ts // server/plugin.ts +import { CoreSetup, Plugin } from 'src/core/server'; import { ElasticsearchPlugin } from '../elasticsearch'; -interface CoreSetup { - elasticsearch: ElasticsearchPlugin // note: Elasticsearch is in Core in NP, rather than a plugin -} - interface FooSetup { getBar(): string } -interface PluginsSetup { - foo: FooSetup +// We inject the miminal legacy dependencies into our plugin including dependencies on other legacy +// plugins. Take care to only expose the legacy functionality you need e.g. don't inject the whole +// `Legacy.Server` if you only depend on `Legacy.Server['route']`. +interface LegacySetup { + route: Legacy.Server['route'] + plugins: { + elasticsearch: ElasticsearchPlugin, // note: Elasticsearch is in CoreSetup in NP, rather than a plugin + foo: FooSetup + } } -export type DemoPluginSetup = ReturnType; +// Define the public API's for our plugins setup and start lifecycle +export interface DemoSetup { + getDemoBar: () => string; +} +export interface DemoStart {} -export class Plugin { - public setup(core: CoreSetup, plugins: PluginsSetup) { +// Once we start dependending on NP plugins' setup or start API's we'll add their types here +export interface DemoSetupDeps {} +export interface DemoStartDeps {} + +export class DemoPlugin implements Plugin { + public setup(core: CoreSetup, plugins: PluginsSetup, __LEGACY: LegacySetup): DemoSetup { + // We're still using the legacy Elasticsearch and http router here, but we're now accessing + // these services in the same way a NP plugin would: injected into the setup function. It's + // also obvious that these dependencies needs to be removed by migrating over to the New + // Platform services exposed through core. const serverFacade: ServerFacade = { plugins: { - // We're still using the legacy Elasticsearch here, but we're now accessing it - // the same way a NP plugin would, via core. Later, we'll swap this out for the - // actual New Platform service. - elasticsearch: core.elasticsearch + elasticsearch: __LEGACY.plugins.elasticsearch } } - // HTTP functionality from legacy platform, accessed in the NP convention, just like Elasticsearch above. - core.http.route({ // note: we know routes will be created on core.http + __LEGACY.route({ path: '/api/demo_plugin/search', method: 'POST', async handler(request) { @@ -490,7 +523,7 @@ export class Plugin { // Exposing functionality for other plugins return { getDemoBar() { - return `Demo ${plugins.foo.getBar()}`; // Accessing functionality from another plugin + return `Demo ${__LEGACY.plugins.foo.getBar()}`; // Accessing functionality from another legacy plugin } }; } @@ -502,26 +535,29 @@ The legacy plugin definition is still the one that is being executed, so we now ```ts // index.ts -import { Plugin } from './server/plugin'; +import { Plugin, PluginDependencies, LegacySetup } from './server/plugin'; export default (kibana) => { return new kibana.Plugin({ id: 'demo_plugin', init(server) { - // core shim - const coreSetup = { - elasticsearch: server.plugins.elasticsearch, - http: { - route: server.route + // core setup API's + const coreSetup = server.newPlatform.setup.core; + + // For now we don't have any dependencies on NP plugins + const pluginsSetup: PluginsSetup = {}; + + // legacy dependencies + const __LEGACY: LegacySetup = { + route: server.route, + plugins: { + elasticsearch: server.plugins.elasticsearch, + foo: server.plugins.foo } }; - // plugins shim - const pluginsSetup = { - foo: server.plugins.foo - }; - const demoSetup = new Plugin().setup(coreSetup, pluginsSetup); + const demoSetup = new Plugin().setup(coreSetup, pluginsSetup, __LEGACY); // continue to expose functionality to legacy plugins server.expose('getDemoBar', demoSetup.getDemoBar); @@ -529,6 +565,11 @@ export default (kibana) => { }); } ``` +> Note: An equally valid approach is to extend `CoreSetup` with a `__legacy` +> property instead of introducing a third parameter to your plugins lifecycle +> function. The important thing is that you reduce the legacy API surface that +> you depend on to a minimum by only picking and injecting the methods you +> require and that you clearly differentiate legacy dependencies in a namespace. This introduces a layer between the legacy plugin system with hapi.js and the logic you want to move to the new plugin system. The functionality exposed through that layer is still provided from the legacy world and in some cases is still technically powered directly by hapi, but building this layer forced you to identify the remaining touch points into the legacy world and it provides you with control when you start migrating to new platform-backed services. @@ -536,73 +577,81 @@ This introduces a layer between the legacy plugin system with hapi.js and the lo ### Switch to new platform services -At this point, your legacy server-side plugin is described in the shape and conventions of the new plugin system, and all of the touch points with the legacy world and hapi.js have been isolated to the shims in the legacy plugin definition. +At this point, your legacy server-side plugin is described in the shape and +conventions of the new plugin system, and all of the touch points with the +legacy world and hapi.js have been isolated inside the `__LEGACY` parameter. -Now the goal is to replace the legacy services backing your shims with services provided by the new platform instead. +Now the goal is to replace all legacy services with services provided by the new platform instead. For the first time in this guide, your progress here is limited by the migration efforts within core and other plugins. As core capabilities are migrated to services in the new platform, they are made available as lifecycle contracts to the legacy `init` function through `server.newPlatform`. This allows you to adopt the new platform service APIs directly in your legacy plugin as they get rolled out. -For the most part, care has been taken when migrating services to the new platform to preserve the existing APIs as much as possible, but there will be times when new APIs differ from the legacy equivalents. Start things off by having your core shim extend the equivalent new platform contract. - -```ts -// index.ts - -init(server) { - // core shim - const coreSetup = { - ...server.newPlatform.setup.core, - - elasticsearch: server.plugins.elasticsearch, - http: { - route: server.route - } - }; -} -``` +For the most part, care has been taken when migrating services to the new platform to preserve the existing APIs as much as possible, but there will be times when new APIs differ from the legacy equivalents. If a legacy API differs from its new platform equivalent, some refactoring will be required. The best outcome comes from updating the plugin code to use the new API, but if that's not practical now, you can also create a facade inside your new plugin definition that is shaped like the legacy API but powered by the new API. Once either of these things is done, that override can be removed from the shim. -Eventually, all overrides will be removed and your `coreSetup` shim is entirely powered by `server.newPlatform.setup.core`. +Eventually, all `__LEGACY` dependencies will be removed and your Plugin will +be powered entirely by Core API's from `server.newPlatform.setup.core`. ```ts init(server) { - // core shim - const coreSetup = { - ...server.newPlatform.setup.core + // core setup API's + const coreSetup = server.newPlatform.setup.core; + + // For now we don't have any dependencies on NP plugins + const pluginsSetup: PluginsSetup = {}; + + // legacy dependencies, we've removed our dependency on elasticsearch and server.route + const __LEGACY: LegacySetup = { + plugins: { + foo: server.plugins.foo + } }; + + const demoSetup = new Plugin().setup(coreSetup, pluginsSetup, __LEGACY); } ``` -At this point, your legacy server-side plugin logic is no longer coupled to the legacy core. +At this point, your legacy server-side plugin logic is no longer coupled to +the legacy core. -A similar approach can be taken for your plugins shim. First, update your plugin shim in `init` to extend `server.newPlatform.setup.plugins`. +A similar approach can be taken for your plugin dependencies. To start +consuming an API from a New Platform plugin access these from +`server.newPlatform.setup.plugins` and inject it into your plugin's setup +function. ```ts init(server) { - // plugins shim - const pluginsSetup = { - ...server.newPlatform.setup.plugins, - foo: server.plugins.foo + // core setup API's + const coreSetup = server.newPlatform.setup.core; + + // Depend on the NP plugin 'foo' + const pluginsSetup: PluginsSetup = { + foo: server.newPlatform.setup.plugins.foo }; + + const demoSetup = new Plugin().setup(coreSetup, pluginsSetup); } ``` -As the plugins you depend on are migrated to the new platform, their contract will be exposed through `server.newPlatform`, so the legacy override should be removed. Like in core, plugins should take care to preserve their existing APIs to make this step as seamless as possible. +As the plugins you depend on are migrated to the new platform, their contract +will be exposed through `server.newPlatform`, so the `__LEGACY` dependencies +should be removed. Like in core, plugins should take care to preserve their +existing APIs to make this step as seamless as possible. -It is much easier to reliably make breaking changes to plugin APIs in the new platform than it is in the legacy world, so if you're planning a big change, consider doing it after your dependent plugins have migrated rather than as part of your own migration. +It is much easier to reliably make breaking changes to plugin APIs in the new +platform than it is in the legacy world, so if you're planning a big change, +consider doing it after your dependent plugins have migrated rather than as +part of your own migration. -Eventually, all overrides will be removed and your `pluginsSetup` shim is entirely powered by `server.newPlatform.setup.plugins`. +Eventually, all `__LEGACY` dependencies will be removed and your plugin will be +entirely powered by the New Platform and New Platform plugins. -```ts -init(server) { - // plugins shim - const pluginsSetup = { - ...server.newPlatform.setup.plugins - }; -} -``` +> Note: All New Platform plugins are exposed to legacy plugins via +> `server.newPlatform.setup.plugins`. Once you move your plugin over to the +> New Platform you will have to explicitly declare your dependencies on other +> plugins in your `kibana.json` manifest file. At this point, your legacy server-side plugin logic is no longer coupled to legacy plugins. @@ -616,7 +665,7 @@ With the previous steps resolved, this final step should be easy, but the exact Other plugins may want to move subsystems over individually. For instance, you can move routes over to the New Platform in groups rather than all at once. Other examples that could be broken up: - Configuration schema ([see example](./MIGRATION_EXAMPLES.md#declaring-config-schema)) -- HTTP route registration +- HTTP route registration ([see example](./MIGRATION_EXAMPLES.md#http-routes)) - Polling mechanisms (eg. job worker) In general, we recommend moving all at once by ensuring you're not depending on any legacy code before you move over. @@ -748,25 +797,17 @@ import { plugin } from '.'; import { setup as fooSetup, start as fooStart } from '../../foo/public/legacy'; // assumes `foo` lives in `legacy/core_plugins` const pluginInstance = plugin({} as PluginInitializerContext); -const shimCoreSetup = { - ...npSetup.core, - bar: {}, // shim for a core service that hasn't migrated yet -}; -const shimCoreStart = { - ...npStart.core, - bar: {}, -}; -const shimSetupPlugins = { - ...npSetup.plugins, - foo: fooSetup, +const __LEGACYSetup = { + bar: {}, // shim for a core service that hasn't migrated yet + foo: fooSetup, // dependency on a legacy plugin }; -const shimStartPlugins = { - ...npStart.plugins, - foo: fooStart, +const __LEGACYStart = { + bar: {}, // shim for a core service that hasn't migrated yet + foo: fooStart, // dependency on a legacy plugin }; -export const setup = pluginInstance.setup(shimCoreSetup, shimSetupPlugins); -export const start = pluginInstance.start(shimCoreStart, shimStartPlugins); +export const setup = pluginInstance.setup(npSetup.core, npSetup.plugins, __LEGACYSetup); +export const start = pluginInstance.start(npStart.core, npStart.plugins, __LEGACYStart); ``` > As you build your shims, you may be wondering where you will find some legacy services in the new platform. Skip to [the tables below](#how-do-i-build-my-shim-for-new-platform-services) for a list of some of the more common legacy services and where we currently expect them to live. @@ -835,16 +876,13 @@ import { uiThing } from 'ui/thing'; ... const pluginInstance = plugin({} as PluginInitializerContext); -const shimSetupPlugins = { - ...npSetup.plugins, +const __LEGACY = { foo: fooSetup, - __LEGACY: { - uiThing, // eventually this will move out of __LEGACY and into a proper plugin - }, + uiThing, // eventually this will move out of __LEGACY and into a NP plugin }; ... -export const setup = pluginInstance.setup(npSetup.core, shimSetupPlugins); +export const setup = pluginInstance.setup(npSetup.core, npSetup.plugins, __LEGACY); ``` #### 7. Switch to new platform services @@ -1010,10 +1048,10 @@ If you have code that should be available to other plugins on both the client an There are some Core services that are purely presentational, for example `core.overlays.openModal()` or `core.application.createLink()` where UI code does need access to these deeply within your application. However, passing these services down as props throughout your application leads to lots of boilerplate. To avoid this, you have three options: 1. Use an abstraction layer, like Redux, to decouple your UI code from core (**this is the highly preferred option**); or - [redux-thunk](https://github.com/reduxjs/redux-thunk#injecting-a-custom-argument) and [redux-saga](https://redux-saga.js.org/docs/api/#createsagamiddlewareoptions) already have ways to do this. -1. Use React Context to provide these services to large parts of your React tree; or -1. Create a high-order-component that injects core into a React component; or +2. Use React Context to provide these services to large parts of your React tree; or +3. Create a high-order-component that injects core into a React component; or - This would be a stateful module that holds a reference to Core, but provides it as props to components with a `withCore(MyComponent)` interface. This can make testing components simpler. (Note: this module cannot be shared across plugin boundaries, see above). -1. Create a global singleton module that gets imported into each module that needs it. (Note: this module cannot be shared across plugin boundaries, see above). [Example](https://gist.github.com/epixa/06c8eeabd99da3c7545ab295e49acdc3). +4. Create a global singleton module that gets imported into each module that needs it. (Note: this module cannot be shared across plugin boundaries, see above). [Example](https://gist.github.com/epixa/06c8eeabd99da3c7545ab295e49acdc3). If you find that you need many different Core services throughout your application, this may be a code smell and could lead to pain down the road. For instance, if you need access to an HTTP Client or SavedObjectsClient in many places in your React tree, it's likely that a data layer abstraction (like Redux) could make developing your plugin much simpler (see option 1). @@ -1114,7 +1152,7 @@ In server code, `core` can be accessed from either `server.newPlatform` or `kbnS | Legacy Platform | New Platform | Notes | |----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| | `server.config()` | [`initializerContext.config.create()`](/docs/development/core/server/kibana-plugin-server.plugininitializercontext.config.md) | Must also define schema. See _[how to configure plugin](#configure-plugin)_ | -| `server.route` | [`core.http.createRouter`](/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md) | | +| `server.route` | [`core.http.createRouter`](/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md) | [Examples](./MIGRATION_EXAMPLES.md#route-registration) | | `request.getBasePath()` | [`core.http.basePath.get`](/docs/development/core/server/kibana-plugin-server.httpservicesetup.basepath.md) | | | `server.plugins.elasticsearch.getCluster('data')` | [`core.elasticsearch.dataClient$`](/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.dataclient_.md) | Handlers will also include a pre-configured client | | `server.plugins.elasticsearch.getCluster('admin')` | [`core.elasticsearch.adminClient$`](/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.adminclient_.md) | Handlers will also include a pre-configured client | diff --git a/src/core/MIGRATION_EXAMPLES.md b/src/core/MIGRATION_EXAMPLES.md index 79ae8953df0ea6..9eed3a59acaa62 100644 --- a/src/core/MIGRATION_EXAMPLES.md +++ b/src/core/MIGRATION_EXAMPLES.md @@ -3,6 +3,19 @@ This document is a list of examples of how to migrate plugin code from legacy APIs to their New Platform equivalents. +- [Migration Examples](#migration-examples) + - [Configuration](#configuration) + - [Declaring config schema](#declaring-config-schema) + - [Using New Platform config from a Legacy plugin](#using-new-platform-config-from-a-legacy-plugin) + - [Create a New Platform plugin](#create-a-new-platform-plugin) + - [HTTP Routes](#http-routes) + - [1. Legacy route registration](#1-legacy-route-registration) + - [2. New Platform shim using legacy router](#2-new-platform-shim-using-legacy-router) + - [3. New Platform shim using New Platform router](#3-new-platform-shim-using-new-platform-router) + - [4. New Platform plugin](#4-new-platform-plugin) + - [Accessing Services](#accessing-services) + - [Chrome](#chrome) + ## Configuration ### Declaring config schema @@ -149,16 +162,18 @@ This interface has a different API with slightly different behaviors. continue using the `boom` module internally in your plugin, the framework does not have native support for converting Boom exceptions into HTTP responses. -### Route Registration +Because of the incompatibility between the legacy and New Platform HTTP Route +API's it might be helpful to break up your migration work into several stages. +### 1. Legacy route registration ```ts -// Legacy route registration +// legacy/plugins/myplugin/index.ts import Joi from 'joi'; new kibana.Plugin({ init(server) { server.route({ - path: '/api/my-plugin/my-route', + path: '/api/demoplugin/search', method: 'POST', options: { validate: { @@ -173,17 +188,99 @@ new kibana.Plugin({ }); } }); +``` +### 2. New Platform shim using legacy router +Create a New Platform shim and inject the legacy `server.route` into your +plugin's setup function. -// New Platform equivalent +```ts +// legacy/plugins/demoplugin/index.ts +import { Plugin, LegacySetup } from './server/plugin'; +export default (kibana) => { + return new kibana.Plugin({ + id: 'demo_plugin', + + init(server) { + // core shim + const coreSetup: server.newPlatform.setup.core; + const pluginSetup = {}; + const legacySetup: LegacySetup = { + route: server.route + }; + + new Plugin().setup(coreSetup, pluginSetup, legacySetup); + } + } +} +``` +```ts +// legacy/plugins/demoplugin/server/plugin.ts +import { CoreSetup } from 'src/core/server'; +import { Legacy } from 'kibana'; + +export interface LegacySetup { + route: Legacy.Server['route']; +}; + +export interface DemoPluginsSetup {}; + +export class Plugin { + public setup(core: CoreSetup, plugins: DemoPluginsSetup, __LEGACY: LegacySetup) { + __LEGACY.route({ + path: '/api/demoplugin/search', + method: 'POST', + options: { + validate: { + payload: Joi.object({ + field1: Joi.string().required(), + }), + } + }, + async handler(req) { + return { message: `Received field1: ${req.payload.field1}` }; + }, + }); + } +} +``` + +### 3. New Platform shim using New Platform router +We now switch the shim to use the real New Platform HTTP API's in `coreSetup` +instead of relying on the legacy `server.route`. Since our plugin is now using +the New Platform API's we are guaranteed that our HTTP route handling is 100% +compatible with the New Platform. As a result, we will also have to adapt our +route registration accordingly. +```ts +// legacy/plugins/demoplugin/index.ts +import { Plugin } from './server/plugin'; +export default (kibana) => { + return new kibana.Plugin({ + id: 'demo_plugin', + + init(server) { + // core shim + const coreSetup = server.newPlatform.setup.core; + const pluginSetup = {}; + + new Plugin().setup(coreSetup, pluginSetup); + } + } +} +``` +```ts +// legacy/plugins/demoplugin/server/plugin.ts import { schema } from '@kbn/config-schema'; +import { CoreSetup } from 'src/core/server'; + +export interface DemoPluginsSetup {}; class Plugin { - public setup(core) { + public setup(core: CoreSetup, pluginSetup: DemoPluginSetup) { const router = core.http.createRouter(); router.post( { - path: '/api/my-plugin/my-route', + path: '/api/demoplugin/search', validate: { body: schema.object({ field1: schema.string(), @@ -202,6 +299,14 @@ class Plugin { } ``` +#### 4. New Platform plugin +As the final step we delete the shim and move all our code into a New Platform +plugin. Since we were already consuming the New Platform API's no code changes +are necessary inside `plugin.ts`. +```ts +// Move legacy/plugins/demoplugin/server/plugin.ts -> plugins/demoplugin/server/plugin.ts +``` + ### Accessing Services Services in the Legacy Platform were typically available via methods on either