kibana/docs/developer/architecture/core/index.asciidoc
Mikhail Shustov ab8a2f7427
[docs] Convert migration guide to asciidoc (#82600)
* 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>
2020-11-24 09:46:19 +01:00

451 lines
15 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[[kibana-platform-api]]
== {kib} Core API
experimental[]
{kib} Core provides a set of low-level API's required to run all {kib} plugins.
These API's are injected into your plugin's lifecycle methods and may be invoked during that lifecycle only:
[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
}
}
----
=== Server-side
[[configuration-service]]
==== Configuration service
{kib} provides `ConfigService` if a plugin developer may want to support
adjustable runtime behavior for their plugins.
Plugins can only read their own configuration values, it is not possible to access the configuration values from {kib} Core or other plugins directly.
[source,js]
----
// in Legacy platform
const basePath = config.get('server.basePath');
// in Kibana Platform 'basePath' belongs to the http service
const basePath = core.http.basePath.get(request);
----
To have access to your plugin config, you _should_:
* Declare plugin-specific `configPath` (will fallback to plugin `id`
if not specified) in {kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md[`kibana.json`] manifest file.
* Export schema validation for the config from plugin's main file. Schema is
mandatory. If a plugin reads from the config without schema declaration,
`ConfigService` will throw an error.
*my_plugin/server/index.ts*
[source,typescript]
----
import { schema, TypeOf } from '@kbn/config-schema';
export const plugin = …
export const config = {
schema: schema.object(…),
};
export type MyPluginConfigType = TypeOf<typeof config.schema>;
----
* Read config value exposed via `PluginInitializerContext`.
*my_plugin/server/index.ts*
[source,typescript]
----
import type { PluginInitializerContext } from 'kibana/server';
export class MyPlugin {
constructor(initializerContext: PluginInitializerContext) {
this.config$ = initializerContext.config.create<MyPluginConfigType>();
// or if config is optional:
this.config$ = initializerContext.config.createIfExists<MyPluginConfigType>();
}
----
If your plugin also has a client-side part, you can also expose
configuration properties to it using the configuration `exposeToBrowser`
allow-list property.
*my_plugin/server/index.ts*
[source,typescript]
----
import { schema, TypeOf } from '@kbn/config-schema';
import type { PluginConfigDescriptor } from 'kibana/server';
const configSchema = schema.object({
secret: schema.string({ defaultValue: 'Only on server' }),
uiProp: schema.string({ defaultValue: 'Accessible from client' }),
});
type ConfigType = TypeOf<typeof configSchema>;
export const config: PluginConfigDescriptor<ConfigType> = {
exposeToBrowser: {
uiProp: true,
},
schema: configSchema,
};
----
Configuration containing only the exposed properties will be then
available on the client-side using the plugin's `initializerContext`:
*my_plugin/public/index.ts*
[source,typescript]
----
interface ClientConfigType {
uiProp: string;
}
export class MyPlugin implements Plugin<PluginSetup, PluginStart> {
constructor(private readonly initializerContext: PluginInitializerContext) {}
public async setup(core: CoreSetup, deps: {}) {
const config = this.initializerContext.config.get<ClientConfigType>();
}
----
All plugins are considered enabled by default. If you want to disable
your plugin, you could declare the `enabled` flag in the plugin
config. This is a special {kib} Platform key. {kib} reads its
value and wont create a plugin instance if `enabled: false`.
[source,js]
----
export const config = {
schema: schema.object({ enabled: schema.boolean({ defaultValue: false }) }),
};
----
[[handle-plugin-configuration-deprecations]]
===== Handle plugin configuration deprecations
If your plugin has deprecated configuration keys, you can describe them using
the `deprecations` config descriptor field.
Deprecations are managed on a per-plugin basis, meaning you dont need to specify
the whole property path, but use the relative path from your plugins
configuration root.
*my_plugin/server/index.ts*
[source,typescript]
----
import { schema, TypeOf } from '@kbn/config-schema';
import type { PluginConfigDescriptor } from 'kibana/server';
const configSchema = schema.object({
newProperty: schema.string({ defaultValue: 'Some string' }),
});
type ConfigType = TypeOf<typeof configSchema>;
export const config: PluginConfigDescriptor<ConfigType> = {
schema: configSchema,
deprecations: ({ rename, unused }) => [
rename('oldProperty', 'newProperty'),
unused('someUnusedProperty'),
],
};
----
In some cases, accessing the whole configuration for deprecations is
necessary. For these edge cases, `renameFromRoot` and `unusedFromRoot`
are also accessible when declaring deprecations.
*my_plugin/server/index.ts*
[source,typescript]
----
export const config: PluginConfigDescriptor<ConfigType> = {
schema: configSchema,
deprecations: ({ renameFromRoot, unusedFromRoot }) => [
renameFromRoot('oldplugin.property', 'myplugin.property'),
unusedFromRoot('oldplugin.deprecated'),
],
};
----
==== Logging service
Allows a plugin to provide status and diagnostic information.
For detailed instructions see the {kib-repo}blob/{branch}/src/core/server/logging/README.md[logging service documentation].
[source,typescript]
----
import type { PluginInitializerContext, CoreSetup, Plugin, Logger } from 'kibana/server';
export class MyPlugin implements Plugin {
private readonly logger: Logger;
constructor(initializerContext: PluginInitializerContext) {
this.logger = initializerContext.logger.get();
}
public setup(core: CoreSetup) {
try {
this.logger.debug('doing something...');
// …
} catch (e) {
this.logger.error('failed doing something...');
}
}
}
----
==== Elasticsearch service
`Elasticsearch service` provides `elasticsearch.client` program API to communicate with Elasticsearch server REST API.
`elasticsearch.client` interacts with Elasticsearch service on behalf of:
- `kibana_system` user via `elasticsearch.client.asInternalUser.*` methods.
- a current end-user via `elasticsearch.client.asCurrentUser.*` methods. In this case Elasticsearch client should be given the current user credentials.
See <<scoped-services>> and <<development-security>>.
{kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.md[Elasticsearch service API docs]
[source,typescript]
----
import { CoreStart, Plugin } from 'kibana/public';
export class MyPlugin implements Plugin {
public start(core: CoreStart) {
async function asyncTask() {
const result = await core.elasticsearch.client.asInternalUser.ping(…);
}
asyncTask();
}
}
----
For advanced use-cases, such as a search, use {kib-repo}blob/{branch}/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md[Data plugin]
include::saved-objects-service.asciidoc[leveloffset=+1]
==== HTTP service
Allows plugins:
* to extend the {kib} server with custom REST API.
* to execute custom logic on an incoming request or server response.
* implement custom authentication and authorization strategy.
See {kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md[HTTP service API docs]
[source,typescript]
----
import { schema } from '@kbn/config-schema';
import type { CoreSetup, Plugin } from 'kibana/server';
export class MyPlugin implements Plugin {
public setup(core: CoreSetup) {
const router = core.http.createRouter();
const validate = {
params: schema.object({
id: schema.string(),
}),
};
router.get({
path: 'my_plugin/{id}',
validate
},
async (context, request, response) => {
const data = await findObject(request.params.id);
if (!data) return response.notFound();
return response.ok({
body: data,
headers: {
'content-type': 'application/json'
}
});
});
}
}
----
==== UI settings service
The program interface to <<advanced-options, UI settings>>.
It makes it possible for Kibana plugins to extend Kibana UI Settings Management with custom settings.
See:
- {kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.uisettingsservicesetup.register.md[UI settings service Setup API docs]
- {kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.uisettingsservicestart.register.md[UI settings service Start API docs]
[source,typescript]
----
import { schema } from '@kbn/config-schema';
import type { CoreSetup,Plugin } from 'kibana/server';
export class MyPlugin implements Plugin {
public setup(core: CoreSetup) {
core.uiSettings.register({
custom: {
value: '42',
schema: schema.string(),
},
});
const router = core.http.createRouter();
router.get({
path: 'my_plugin/{id}',
validate: …,
},
async (context, request, response) => {
const customSetting = await context.uiSettings.client.get('custom');
});
}
}
----
=== Client-side
==== Application service
Kibana has migrated to be a Single Page Application. Plugins should use `Application service` API to instruct Kibana what an application should be loaded & rendered in the UI in response to user interactions.
[source,typescript]
----
import { AppMountParameters, CoreSetup, Plugin, DEFAULT_APP_CATEGORIES } from 'kibana/public';
export class MyPlugin implements Plugin {
public setup(core: CoreSetup) {
core.application.register({ // <1>
category: DEFAULT_APP_CATEGORIES.kibana,
id: 'my-plugin',
title: 'my plugin title',
euiIconType: '/path/to/some.svg',
order: 100,
appRoute: '/app/my_plugin', // <2>
async mount(params: AppMountParameters) { // <3>
// Load application bundle
const { renderApp } = await import('./application');
// Get start services
const [coreStart, depsStart] = await core.getStartServices(); // <4>
// Render the application
return renderApp(coreStart, depsStart, params); // <5>
},
});
}
}
----
<1> See {kib-repo}blob/{branch}/docs/development/core/public/kibana-plugin-core-public.applicationsetup.register.md[application.register interface]
<2> Application specific URL.
<3> `mount` callback is invoked when a user navigates to the application-specific URL.
<4> `core.getStartServices` method provides API available during `start` lifecycle.
<5> `mount` method must return a function that will be called to unmount the application.
NOTE:: you are free to use any UI library to render a plugin application in DOM.
However, we recommend using React and https://elastic.github.io/eui[EUI] for all your basic UI
components to create a consistent UI experience.
==== HTTP service
Provides API to communicate with the {kib} server. Feel free to use another HTTP client library to request 3rd party services.
[source,typescript]
----
import { CoreStart } from 'kibana/public';
interface ResponseType {…};
async function fetchData<ResponseType>(core: CoreStart) {
return await core.http.get<>(
'/api/my_plugin/',
{ query: … },
);
}
----
See {kib-repo}blob/{branch}/docs/development/core/public/kibana-plugin-core-public.httpsetup.md[for all available API].
==== Elasticsearch service
Not available in the browser. Use {kib-repo}blob/{branch}/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md[Data plugin] instead.
== Patterns
[[scoped-services]]
=== Scoped services
Whenever Kibana needs to get access to data saved in elasticsearch, it
should perform a check whether an end-user has access to the data. In
the legacy platform, Kibana requires binding elasticsearch related API
with an incoming request to access elasticsearch service on behalf of a
user.
[source,js]
----
async function handler(req, res) {
const dataCluster = server.plugins.elasticsearch.getCluster('data');
const data = await dataCluster.callWithRequest(req, 'ping');
}
----
The Kibana Platform introduced a handler interface on the server-side to perform that association
internally. Core services, that require impersonation with an incoming
request, are exposed via `context` argument of
{kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.requesthandler.md[the
request handler interface.] The above example looks in the Kibana Platform
as
[source,js]
----
async function handler(context, req, res) {
const data = await context.core.elasticsearch.client.asCurrentUser('ping');
}
----
The
{kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md[request
handler context] exposed the next scoped *core* services:
[width="100%",cols="30%,70%",options="header",]
|===
|Legacy Platform |Kibana Platform
|`request.getSavedObjectsClient`
|{kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md[`context.savedObjects.client`]
|`server.plugins.elasticsearch.getCluster('admin')`
|{kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.iscopedclusterclient.md[`context.elasticsearch.client`]
|`server.plugins.elasticsearch.getCluster('data')`
|{kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.iscopedclusterclient.md[`context.elasticsearch.client`]
|`request.getUiSettingsService`
|{kib-repo}blob/{branch}/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md[`context.uiSettings.client`]
|===
==== Declare a custom scoped service
Plugins can extend the handler context with a custom API that will be
available to the plugin itself and all dependent plugins. For example,
the plugin creates a custom elasticsearch client and wants to use it via
the request handler context:
[source,typescript]
----
import type { CoreSetup, IScopedClusterClient } from 'kibana/server';
export interface MyPluginContext {
client: IScopedClusterClient;
}
// extend RequestHandlerContext when a dependent plugin imports MyPluginContext from the file
declare module 'kibana/server' {
interface RequestHandlerContext {
myPlugin?: MyPluginContext;
}
}
class MyPlugin {
setup(core: CoreSetup) {
const client = core.elasticsearch.createClient('myClient');
core.http.registerRouteHandlerContext('myPlugin', (context, req, res) => {
return { client: client.asScoped(req) };
});
const router = core.http.createRouter();
router.get(
{ path: '/api/my-plugin/', validate: … },
async (context, req, res) => {
const data = await context.myPlugin.client.asCurrentUser('endpoint');
}
);
}
----