* Initial conversion to asciidoc * Update and split migration guide * Convert MIGRATION_EXAMPLES to asciidoc * build with --focus flag * convert migration guide to asciidoc * cleanup migration_examples * fix wrong Heading size * update links in docs * Apply suggestions from code review Co-authored-by: Rudolf Meijering <skaapgif@gmail.com> * Apply suggestions from code review Co-authored-by: Rudolf Meijering <skaapgif@gmail.com> * add tooling section * explain purpose of each lifecycle method * cleanup docs * cleanup p2 * fix wrong link * resturcture core docs * fix wrong link * update missing links * Apply suggestions from code review Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * address comments * add a commenta about plugin-helpers preconfigured * improve density of tables * fix lik * remove links to the migration guide * address comments * Apply suggestions from code review Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * address @gchaps comments * Apply suggestions from code review Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * change format of ES client change list Co-authored-by: Josh Dover <me@joshdover.com> Co-authored-by: Rudolf Meijering <skaapgif@gmail.com> Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>
347 lines
12 KiB
Text
347 lines
12 KiB
Text
[[kibana-platform-plugin-api]]
|
||
== {kib} Plugin API
|
||
|
||
experimental[]
|
||
|
||
{kib} platform plugins are a significant step toward stabilizing {kib} architecture for all the developers.
|
||
We made 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 present themselves to {kib}
|
||
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.
|
||
In both places, you describe your plugin similarly, and you interact with
|
||
Core and other plugins in the same way.
|
||
|
||
The basic file structure of a {kib} plugin named `demo` that
|
||
has both client-side and server-side code would be:
|
||
|
||
[source,tree]
|
||
----
|
||
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 specify if this plugin has server-side code, browser-side code, or both:
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"id": "demo",
|
||
"version": "kibana",
|
||
"server": true,
|
||
"ui": true
|
||
}
|
||
----
|
||
|
||
Learn about the {kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md[manifest
|
||
file format].
|
||
|
||
NOTE: `package.json` files are irrelevant to and ignored by {kib} for discovering and loading plugins.
|
||
|
||
*[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 class for
|
||
{kib} to load.
|
||
|
||
[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
|
||
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
|
||
for first-party Elastic plugins].
|
||
|
||
[source,typescript]
|
||
----
|
||
import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server';
|
||
|
||
export class MyPlugin implements Plugin {
|
||
constructor(initializerContext: PluginInitializerContext) {}
|
||
|
||
public setup(core: CoreSetup) {
|
||
// called when plugin is setting up during Kibana's startup sequence
|
||
}
|
||
|
||
public start(core: CoreStart) {
|
||
// called after all plugins are set up
|
||
}
|
||
|
||
public stop() {
|
||
// called when plugin is torn down during Kibana's shutdown sequence
|
||
}
|
||
}
|
||
----
|
||
|
||
*[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:
|
||
|
||
[source,typescript]
|
||
----
|
||
import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server';
|
||
|
||
export class MyPlugin implements Plugin {
|
||
constructor(initializerContext: PluginInitializerContext) {}
|
||
|
||
public setup(core: CoreSetup) {
|
||
// called when plugin is setting up during Kibana's startup sequence
|
||
}
|
||
|
||
public start(core: CoreStart) {
|
||
// called after all plugins are set up
|
||
}
|
||
|
||
public stop() {
|
||
// called when plugin is torn down during Kibana's shutdown sequence
|
||
}
|
||
}
|
||
----
|
||
|
||
{kib} does not impose any technical restrictions on how the
|
||
the internals of a plugin are architected, though there are certain
|
||
considerations related to how plugins integrate with core APIs
|
||
and APIs exposed by other plugins that may greatly impact how
|
||
they are built.
|
||
[[plugin-lifecycles]]
|
||
=== Lifecycles & Core Services
|
||
|
||
The various independent domains that makeup `core` are represented by a
|
||
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.
|
||
|
||
{kib} has three lifecycles: `setup`,
|
||
`start`, and `stop`. Each plugin's `setup` functions is called sequentially
|
||
while Kibana is setting up on the server or when it is being loaded in
|
||
the browser. The `start` functions are called sequentially after `setup`
|
||
has been completed for all plugins. The `stop` functions are called
|
||
sequentially while Kibana is gracefully shutting down the server or
|
||
when the browser tab or window is being closed.
|
||
|
||
The table below explains how each lifecycle relates to the state
|
||
of Kibana.
|
||
|
||
[width="100%",cols="10%, 15%, 37%, 38%",options="header",]
|
||
|===
|
||
|lifecycle | purpose| server |browser
|
||
|_setup_
|
||
|perform "registration" work to setup environment for runtime
|
||
|configure REST API endpoint, register saved object types, etc.
|
||
|configure application routes in SPA, register custom UI elements in extension points, etc.
|
||
|
||
|_start_
|
||
|bootstrap runtime logic
|
||
|respond to an incoming request, request Elasticsearch server, etc.
|
||
|start polling Kibana server, update DOM tree in response to user interactions, etc.
|
||
|
||
|_stop_
|
||
|cleanup runtime
|
||
|dispose of active handles before the server shutdown.
|
||
|store session data in the LocalStorage when the user navigates away from {kib}, etc.
|
||
|===
|
||
|
||
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.
|
||
|
||
These are the contracts exposed by the core services for each lifecycle:
|
||
|
||
[cols=",,",options="header",]
|
||
|===
|
||
|lifecycle |server contract|browser contract
|
||
|_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. Third 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>
|
||
getFoo(): string;
|
||
}
|
||
|
||
export interface FoobarPluginStart { <1>
|
||
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 explicitly declare public interfaces for their plugins.
|
||
|
||
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, it must first declare that
|
||
plugin as a dependency in it's {kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md[`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
|
||
|
||
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.
|