-
Notifications
You must be signed in to change notification settings - Fork 8.5k
[docs] Convert migration guide to asciidoc #82600
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
1ef8237
3d827bb
c2811cc
79a9d9a
d5511b4
cd8586b
b44e946
ef52c00
bba76b0
5f0fb90
1d8bdc0
0daa5d5
32aec24
c3f7703
79c374a
e28abd8
1d369e9
00c3bc7
6a06a77
ca05f02
f78893a
c2d057c
54d343a
cb9b192
58d0b26
510b01b
0ae5731
e128d9b
9628fb9
b20d6af
8e85f83
4453bfb
eed5920
091ab65
86c6beb
b44350c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,348 @@ | ||
| [[kibana-platform-plugin-api]] | ||
| == Kibana Platform Plugin API | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| experimental[] | ||
|
|
||
| Plugins in the Kibana Platform are not especially novel or complicated to | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| describe. Our intention wasn’t to build some clever system that | ||
| magically solved problems through abstractions and layers of obscurity, | ||
| and we wanted to make sure plugins could continue to use most of the | ||
| same technologies they use today, at least from a technical perspective. | ||
|
|
||
| === Anatomy of a plugin | ||
|
|
||
| Plugins are defined as classes and exposed to the platform itself | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| through a simple wrapper function. A plugin can have browser-side code, | ||
| server-side code, or both. There is no architectural difference between | ||
| a plugin in the browser and a plugin on the server, which is to say that | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| in both places you describe your plugin similarly, and you interact with | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Core and/or other plugins in the same way. | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| The basic file structure of a Kibana Platform plugin named `demo` that | ||
| had both client-side and server-side code would be: | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source,tree] | ||
| ---- | ||
| src/plugins | ||
| demo | ||
| kibana.json [1] | ||
| public | ||
| index.ts [2] | ||
| plugin.ts [3] | ||
| server | ||
| index.ts [4] | ||
| plugin.ts [5] | ||
| ---- | ||
|
|
||
| *[1] `kibana.json`* is a static manifest file that is used to identify the | ||
| plugin and to determine what kind of code the platform should execute from the | ||
| plugin: | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source,json] | ||
| ---- | ||
| { | ||
| "id": "demo", | ||
| "version": "kibana", | ||
| "server": true, | ||
| "ui": true | ||
| } | ||
| ---- | ||
|
|
||
| More details about {kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md[manifest | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| file format] | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Note that `package.json` files are irrelevant to and ignored by the new | ||
| platform. | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| *[2] `public/index.ts`* is the entry point into the client-side code of | ||
| this plugin. It must export a function named `plugin`, which will | ||
| receive {kib-repo}blob/{branch}/docs/development/core/public/kibana-plugin-core-public.plugininitializercontext.md[a standard set of core capabilities] as an argument. | ||
| It should return an instance of its plugin definition for | ||
| the platform to register at load time. | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source,typescript] | ||
| ---- | ||
| import type { PluginInitializerContext } from 'kibana/server'; | ||
| import { MyPlugin } from './plugin'; | ||
| export function plugin(initializerContext: PluginInitializerContext) { | ||
| return new MyPlugin(initializerContext); | ||
| } | ||
| ---- | ||
|
|
||
| *[3] `public/plugin.ts`* is the client-side plugin definition itself. | ||
| Technically speaking it does not need to be a class or even a separate | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| file from the entry point, but _all plugins at Elastic_ should be | ||
| consistent in this way. _See all {kib-repo}blob/{branch}/src/core/CONVENTIONS.md[conventions | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| for first-party Elastic plugins]_. | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source,typescript] | ||
| ---- | ||
| import type { PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server'; | ||
| export class Plugin { | ||
| constructor(initializerContext: PluginInitializerContext) {} | ||
| public setup(core: CoreSetup) { | ||
| // called when plugin is setting up | ||
| } | ||
| public start(core: CoreStart) { | ||
| // called after all plugins are set up | ||
| } | ||
| public stop() { | ||
| // called when plugin is torn down, aka window.onbeforeunload | ||
| } | ||
| } | ||
| ---- | ||
|
|
||
| *[4] `server/index.ts`* is the entry-point into the server-side code of | ||
| this plugin. {kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.plugininitializercontext.md[It is identical] in almost every way to the client-side | ||
| entry-point: | ||
|
|
||
|
|
||
| [source,typescript] | ||
| ---- | ||
| import type { PluginInitializerContext } from 'kibana/server'; | ||
| import { MyPlugin } from './plugin'; | ||
| export function plugin(initializerContext: PluginInitializerContext) { | ||
| return new MyPlugin(initializerContext); | ||
| } | ||
| ---- | ||
|
|
||
| *[5] `server/plugin.ts`* is the server-side plugin definition. The | ||
| _shape_ of this plugin is the same as it’s client-side counter-part: | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source,typescript] | ||
| ---- | ||
| import type { PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server'; | ||
| export class MyPlugin { | ||
| constructor(initializerContext: PluginInitializerContext) {} | ||
| public setup(core: CoreSetup) { | ||
| // called when plugin is setting up during Kibana's startup sequence | ||
mshustov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| public start(core: CoreStart) { | ||
| // called after all plugins are set up | ||
mshustov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| public stop() { | ||
| // called when plugin is torn down during Kibana's shutdown sequence | ||
mshustov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| ---- | ||
|
|
||
| The platform does not impose any technical restrictions on how the | ||
| the internals of the plugin are architected, though there are certain | ||
| considerations related to how plugins interact with the core and how plugins | ||
| interact with other plugins that may greatly impact how they are built. | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| [[plugin-lifecycles]] | ||
| === Lifecycles & Core Services | ||
|
|
||
| The various independent domains that makeup `core` are represented by a | ||
mshustov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| series of services and many of those services expose public interfaces | ||
| that are provided to _all_ plugins. Services expose different features | ||
| at different parts of their _lifecycle_. We describe the lifecycle of | ||
| core services and plugins with specifically-named functions on the | ||
| service definition. | ||
|
|
||
| In the Kibana Platform, there are three lifecycle functions today: `setup`, | ||
| `start`, and `stop`. The `setup` functions are invoked sequentially | ||
| while Kibana is setting up on the server or when it is being loaded in | ||
| the browser. The `start` functions are invoked sequentially after setup | ||
| has been completed for all plugins. The `stop` functions are invoked | ||
| sequentially while Kibana is gracefully shutting down on the server or | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| when the browser tab or window is being closed. | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| The table below explains how each lifecycle event relates to the state | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| of Kibana. | ||
|
|
||
| [width="100%",cols="15%,38%,47%",options="header",] | ||
| |=== | ||
| |lifecycle event |server |browser | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| |_setup_ |bootstrapping and configuring routes |loading plugin bundles | ||
| and configuring applications | ||
|
|
||
| |_start_ |server is now serving traffic |browser is now showing UI to | ||
| the user | ||
|
|
||
| |_stop_ |server has received a request to shutdown |user is navigating | ||
| away from Kibana | ||
| |=== | ||
|
|
||
| There is no equivalent behavior to `start` or `stop` in legacy plugins. | ||
| Conversely, there is no equivalent to `uiExports` in Kibana Platform plugins. | ||
| As a general rule of thumb, features that were registered via `uiExports` are | ||
| now registered during the `setup` phase. Most of everything else should move | ||
| to the `start` phase. | ||
|
|
||
| The lifecycle-specific contracts exposed by core services are always | ||
| passed as the first argument to the equivalent lifecycle function in a | ||
| plugin. For example, the core `http` service exposes a function | ||
| `createRouter` to all plugin `setup` functions. To use this function to register | ||
| an HTTP route handler, a plugin just accesses it off of the first: | ||
| argument: | ||
|
|
||
| [source, typescript] | ||
| ---- | ||
| import type { CoreSetup } from 'kibana/server'; | ||
| export class MyPlugin { | ||
| public setup(core: CoreSetup) { | ||
| const router = core.http.createRouter(); | ||
| // handler is called when '/path' resource is requested with `GET` method | ||
| router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); | ||
| } | ||
| } | ||
| ---- | ||
|
|
||
| Different service interfaces can and will be passed to `setup`, `start`, and | ||
| `stop` because certain functionality makes sense in the context of a | ||
| running plugin while other types of functionality may have restrictions | ||
| or may only make sense in the context of a plugin that is stopping. | ||
|
|
||
| For example, the `stop` function in the browser gets invoked as part of | ||
| the `window.onbeforeunload` event, which means you can’t necessarily | ||
| execute asynchronous code here reliably. For that reason, | ||
| `core` likely wouldn’t provide any asynchronous functions to plugin | ||
| `stop` functions in the browser. | ||
|
|
||
| The current lifecycle function for all plugins will be executed before the next | ||
| lifecycle starts. That is to say that all `setup` functions are executed before | ||
| any `start` functions are executed. Core services that expose functionality to | ||
| plugins always have their lifecycle functions executed before any plugins. | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| These are the contracts exposed by the core services for each lifecycle | ||
| event: | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [cols=",,",options="header",] | ||
| |=== | ||
| |lifecycle event |server contract|browser contract | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| |_contructor_ | ||
| |{kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.plugininitializercontext.md[PluginInitializerContext] | ||
| |{kib-repo}blob/{branch}/docs/development/core/public/kibana-plugin-core-public.plugininitializercontext.md[PluginInitializerContext] | ||
|
|
||
| |_setup_ | ||
| |{kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.coresetup.md[CoreSetup] | ||
| |{kib-repo}blob/{branch}/docs/development/core/public/kibana-plugin-core-public.coresetup.md[CoreSetup] | ||
|
|
||
| |_start_ | ||
| |{kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.corestart.md[CoreStart] | ||
| |{kib-repo}blob/{branch}/docs/development/core/public/kibana-plugin-core-public.corestart.md[CoreStart] | ||
|
|
||
| |_stop_ | | ||
| |=== | ||
|
|
||
| === Integrating with other plugins | ||
|
|
||
| Plugins can expose public interfaces for other plugins to consume. Like | ||
| `core`, those interfaces are bound to the lifecycle functions `setup` | ||
| and/or `start`. | ||
|
|
||
| Anything returned from `setup` or `start` will act as the interface, and | ||
| while not a technical requirement, all first-party Elastic plugins | ||
| will expose types for that interface as well. 3rd party plugins | ||
| wishing to allow other plugins to integrate with it are also highly | ||
| encouraged to expose types for their plugin interfaces. | ||
|
|
||
| *foobar plugin.ts:* | ||
|
|
||
| [source, typescript] | ||
| ---- | ||
| import type { Plugin } from 'kibana/server'; | ||
| export interface FoobarPluginSetup { // <1> | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| getFoo(): string; | ||
| } | ||
| export interface FoobarPluginStart { | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| getBar(): string; | ||
| } | ||
| export class MyPlugin implements Plugin<FoobarPluginSetup, FoobarPluginStart> { | ||
| public setup(): FoobarPluginSetup { | ||
| return { | ||
| getFoo() { | ||
| return 'foo'; | ||
| }, | ||
| }; | ||
| } | ||
| public start(): FoobarPluginStart { | ||
| return { | ||
| getBar() { | ||
| return 'bar'; | ||
| }, | ||
| }; | ||
| } | ||
| } | ||
| ---- | ||
| <1> We highly encourage plugin authors to declare a plugin public interfaces explicitly. | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Unlike core, capabilities exposed by plugins are _not_ automatically | ||
| injected into all plugins. Instead, if a plugin wishes to use the public | ||
| interface provided by another plugin, they must first declare that | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| plugin as a dependency in their `kibana.json` manifest file. | ||
|
|
||
| *demo kibana.json:* | ||
|
|
||
| [source,json] | ||
| ---- | ||
| { | ||
| "id": "demo", | ||
| "requiredPlugins": ["foobar"], | ||
| "server": true, | ||
| "ui": true | ||
| } | ||
| ---- | ||
|
|
||
| With that specified in the plugin manifest, the appropriate interfaces | ||
| are then available via the second argument of `setup` and/or `start`: | ||
|
|
||
| *demo plugin.ts:* | ||
|
|
||
| [source,typescript] | ||
| ---- | ||
| import type { CoreSetup, CoreStart } from 'kibana/server'; | ||
| import type { FoobarPluginSetup, FoobarPluginStart } from '../../foobar/server'; | ||
| interface DemoSetupPlugins { <1> | ||
| foobar: FoobarPluginSetup; | ||
| } | ||
| interface DemoStartPlugins { | ||
| foobar: FoobarPluginStart; | ||
| } | ||
| export class AnotherPlugin { | ||
| public setup(core: CoreSetup, plugins: DemoSetupPlugins) { <2> | ||
| const { foobar } = plugins; | ||
| foobar.getFoo(); // 'foo' | ||
| foobar.getBar(); // throws because getBar does not exist | ||
| } | ||
| public start(core: CoreStart, plugins: DemoStartPlugins) { <3> | ||
| const { foobar } = plugins; | ||
| foobar.getFoo(); // throws because getFoo does not exist | ||
| foobar.getBar(); // 'bar' | ||
| } | ||
| public stop() {} | ||
| } | ||
| ---- | ||
| <1> The interface for plugin's dependencies must be manually composed. You can | ||
| do this by importing the appropriate type from the plugin and constructing an | ||
| interface where the property name is the plugin's ID. | ||
| <2> These manually constructed types should then be used to specify the type of | ||
| the second argument to the plugin. | ||
| <3> Notice that the type for the setup and start lifecycles are different. Plugin lifecycle | ||
| functions can only access the APIs that are exposed _during_ that lifecycle. | ||
|
|
||
| === Migrating legacy plugins to Kibana Platform | ||
mshustov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| In Kibana 7.10, support for legacy plugins was removed. See | ||
| <<migrating-legacy-plugins>> for detailed information on how to convert existing | ||
| legacy plugins to this new API. | ||
Uh oh!
There was an error while loading. Please reload this page.