Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1ef8237
Initial conversion to asciidoc
joshdover Oct 15, 2020
3d827bb
Update and split migration guide
joshdover Oct 15, 2020
c2811cc
Convert MIGRATION_EXAMPLES to asciidoc
joshdover Oct 15, 2020
79a9d9a
Merge branch 'master' into pr/80784
mshustov Nov 2, 2020
d5511b4
build with --focus flag
mshustov Nov 2, 2020
cd8586b
convert migration guide to asciidoc
mshustov Nov 4, 2020
b44e946
cleanup migration_examples
mshustov Nov 4, 2020
ef52c00
fix wrong Heading size
mshustov Nov 4, 2020
bba76b0
update links in docs
mshustov Nov 5, 2020
5f0fb90
Apply suggestions from code review
mshustov Nov 8, 2020
1d8bdc0
Apply suggestions from code review
mshustov Nov 8, 2020
0daa5d5
add tooling section
mshustov Nov 8, 2020
32aec24
Merge remote-tracking branch 'origin/pr/80784' into pr/80784
mshustov Nov 8, 2020
c3f7703
explain purpose of each lifecycle method
mshustov Nov 8, 2020
79c374a
cleanup docs
mshustov Nov 9, 2020
e28abd8
cleanup p2
mshustov Nov 9, 2020
1d369e9
fix wrong link
mshustov Nov 9, 2020
00c3bc7
resturcture core docs
mshustov Nov 9, 2020
6a06a77
Merge branch 'master' into pr/80784
mshustov Nov 9, 2020
ca05f02
fix wrong link
mshustov Nov 9, 2020
f78893a
update missing links
mshustov Nov 9, 2020
c2d057c
Apply suggestions from code review
mshustov Nov 11, 2020
54d343a
Merge remote-tracking branch 'origin/pr/80784' into pr/80784
mshustov Nov 11, 2020
cb9b192
address comments
mshustov Nov 11, 2020
58d0b26
add a commenta about plugin-helpers preconfigured
mshustov Nov 11, 2020
510b01b
improve density of tables
mshustov Nov 11, 2020
0ae5731
fix lik
mshustov Nov 11, 2020
e128d9b
remove links to the migration guide
mshustov Nov 11, 2020
9628fb9
Merge branch 'master' into pr/80784
mshustov Nov 13, 2020
b20d6af
address comments
mshustov Nov 17, 2020
8e85f83
Apply suggestions from code review
mshustov Nov 20, 2020
4453bfb
Merge remote-tracking branch 'origin/pr/80784' into pr/80784
mshustov Nov 20, 2020
eed5920
address @gchaps comments
mshustov Nov 20, 2020
091ab65
Apply suggestions from code review
mshustov Nov 24, 2020
86c6beb
Merge remote-tracking branch 'origin/pr/80784' into pr/80784
mshustov Nov 24, 2020
b44350c
change format of ES client change list
mshustov Nov 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
452 changes: 452 additions & 0 deletions docs/developer/architecture/core/core-api.asciidoc

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[[development-plugin-saved-objects]]
== Using Saved Objects
[[saved-objects-service]]
== Saved Objects service

Saved Objects allow {kib} plugins to use {es} like a primary
`Saved Objects service` allows {kib} plugins to use {es} like a primary
database. Think of it as an Object Document Mapper for {es}. Once a
plugin has registered one or more Saved Object types, the Saved Objects client
can be used to query or perform create, read, update and delete operations on
Expand Down
17 changes: 12 additions & 5 deletions docs/developer/architecture/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@

[IMPORTANT]
==============================================
{kib} developer services and apis are in a state of constant development. We cannot provide backwards compatibility at this time due to the high rate of change.
The {kib} Platform Plugin API and developer services are in a state of
constant development. We cannot provide backwards compatibility at this time due
to the high rate of change.
==============================================

To begin with plugin development, we recommend reading our overview of how plugins work:

* <<kibana-platform-plugin-api>>.

Our developer services are changing all the time. One of the best ways to discover and learn about them is to read the available
READMEs from all the plugins inside our {kib-repo}tree/{branch}/src/plugins[open source plugins folder] and our
{kib-repo}/tree/{branch}/x-pack/plugins[commercial plugins folder].
Expand All @@ -14,17 +20,18 @@ A few services also automatically generate api documentation which can be browse

A few notable services are called out below.

* <<kibana-platform-core-api>>
* <<development-security>>
* <<development-plugin-saved-objects>>
* <<add-data-tutorials>>
* <<development-visualize-index>>

include::security/index.asciidoc[leveloffset=+1]
include::kibana-platform-plugin-api.asciidoc[leveloffset=+1]

include::development-plugin-saved-objects.asciidoc[leveloffset=+1]
include::core/core-api.asciidoc[leveloffset=+1]

include::security/index.asciidoc[leveloffset=+1]

include::add-data-tutorials.asciidoc[leveloffset=+1]

include::development-visualize-index.asciidoc[leveloffset=+1]


348 changes: 348 additions & 0 deletions docs/developer/architecture/kibana-platform-plugin-api.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
[[kibana-platform-plugin-api]]
== Kibana Platform Plugin API

experimental[]

Plugins in the Kibana Platform are not especially novel or complicated to
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
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
in both places you describe your plugin similarly, and you interact with
Core and/or other plugins in the same way.

The basic file structure of a Kibana Platform plugin named `demo` that
had both client-side and server-side code would be:

[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:

[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
file format]

Note that `package.json` files are irrelevant to and ignored by the new
platform.

*[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.

[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 { 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:

[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
}
public start(core: CoreStart) {
// called after all plugins are set up
}
public stop() {
// called when plugin is torn down during Kibana's shutdown sequence
}
}
----

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.
[[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.

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
when the browser tab or window is being closed.

The table below explains how each lifecycle event relates to the state
of Kibana.

[width="100%",cols="15%,38%,47%",options="header",]
|===
|lifecycle event |server |browser
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Should I do X in setup or in start?" has created a lot of confusion even internally. We can do it as a follow-up, but I think we can flesh out this lifecycles section more. At the moment this often describes what Kibana is doing like "bootstrapping" or "serving traffic" but I think it's more relevant to describe what plugins should use this lifecycle for e.g. for setup: register HTTP routes, saved object types, browser routes etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added c3f7703 PTAL

|_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.

These are the contracts exposed by the core services for each lifecycle
event:

[cols=",,",options="header",]
|===
|lifecycle event |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. 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>
getFoo(): string;
}
export interface FoobarPluginStart {
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.

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
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

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.
Loading