Skip to content

Commit

Permalink
Merge branch 'master' of github.com:elastic/kibana
Browse files Browse the repository at this point in the history
  • Loading branch information
rashmivkulkarni committed Feb 9, 2021
2 parents ce6e07e + 8e4f899 commit 82ca014
Show file tree
Hide file tree
Showing 97 changed files with 1,758 additions and 920 deletions.
Binary file added dev_docs/assets/applications.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dev_docs/assets/platform_plugins_core.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
291 changes: 65 additions & 226 deletions dev_docs/kibana_platform_plugin_intro.mdx

Large diffs are not rendered by default.

226 changes: 226 additions & 0 deletions dev_docs/tutorials/building_a_plugin.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
---
id: kibDevTutorialBuildAPlugin
slug: /kibana-dev-docs/tutorials/build-a-plugin
title: Kibana plugin tutorial
summary: Anatomy of a Kibana plugin and how to build one
date: 2021-02-05
tags: ['kibana','onboarding', 'dev', 'tutorials']
---

Prereading material:

- <DocLink id="kibPlatformIntro" />

## The anatomy of a plugin

Plugins are defined as classes and present themselves to Kibana 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 Kibana plugin named demo that has both client-side and server-side code would be:

```
plugins/
demo
kibana.json [1]
public
index.ts [2]
plugin.ts [3]
server
index.ts [4]
plugin.ts [5]
common
index.ts [6]
```

### [1] kibana.json

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

```
{
"id": "demo",
"version": "kibana",
"server": true,
"ui": true
}
```

### [2] public/index.ts

`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 a standard set of
core capabilities as an argument. It should return an instance of its plugin class for Kibana to load.

```
import type { PluginInitializerContext } from 'kibana/server';
import { DemoPlugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
return new DemoPlugin(initializerContext);
}
```

### [3] public/plugin.ts

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


```ts
import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server';

export class DemoPlugin 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

`server/index.ts` is the entry-point into the server-side code of this plugin. It is identical in almost every way to the client-side entry-point:

### [5] server/plugin.ts

`server/plugin.ts` is the server-side plugin definition. The shape of this plugin is the same as it’s client-side counter-part:

```ts
import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server';

export class DemoPlugin 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
}
}
```

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

### [6] common/index.ts

`common/index.ts` is the entry-point into code that can be used both server-side or client side.

## How plugin's interact with each other, and Core

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:

```ts
import type { CoreSetup } from 'kibana/server';

export class DemoPlugin {
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' }));
}
}
```

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 kibana.json manifest file.

** foobar plugin.ts: **

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


** demo kibana.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:

```ts
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 DemoPlugin {
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.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
"@babel/runtime": "^7.12.5",
"@elastic/datemath": "link:packages/elastic-datemath",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary",
"@elastic/ems-client": "7.11.0",
"@elastic/ems-client": "7.12.0",
"@elastic/eui": "31.4.0",
"@elastic/filesaver": "1.1.2",
"@elastic/good": "^9.0.1-kibana3",
Expand Down
19 changes: 18 additions & 1 deletion x-pack/plugins/apm/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,24 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
order: 8500,
euiIconType: 'logoObservability',
category: DEFAULT_APP_CATEGORIES.observability,

meta: {
keywords: [
'RUM',
'Real User Monitoring',
'DEM',
'Digital Experience Monitoring',
'EUM',
'End User Monitoring',
'UX',
'Javascript',
'APM',
'Mobile',
'digital',
'performance',
'web performance',
'web perf',
],
},
async mount(params: AppMountParameters<unknown>) {
// Load application bundle and Get start service
const [{ renderApp }, [coreStart, corePlugins]] = await Promise.all([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { shallow } from 'enzyme';

import { EuiEmptyPrompt, EuiFieldSearch } from '@elastic/eui';

import { Loading } from '../../../../../../applications/shared/loading';
import { Loading } from '../../../../../shared/loading';
import { ViewContentHeader } from '../../../../components/shared/view_content_header';

import { AddSourceList } from './add_source_list';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
EuiEmptyPrompt,
} from '@elastic/eui';

import { Loading } from '../../../../../../applications/shared/loading';
import { Loading } from '../../../../../shared/loading';
import { AppLogic } from '../../../../app_logic';
import noSharedSourcesIcon from '../../../../assets/share_circle.svg';
import { ContentSection } from '../../../../components/shared/content_section';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { shallow } from 'enzyme';

import { EuiCheckboxGroup } from '@elastic/eui';

import { Loading } from '../../../../../../applications/shared/loading';
import { Loading } from '../../../../../shared/loading';

import { ConfigureOauth } from './configure_oauth';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import {
} from '@elastic/eui';
import { EuiCheckboxGroupIdToSelectedMap } from '@elastic/eui/src/components/form/checkbox/checkbox_group';

import { Loading } from '../../../../../../applications/shared/loading';
import { parseQueryParams } from '../../../../../../applications/shared/query_params';
import { Loading } from '../../../../../shared/loading';
import { parseQueryParams } from '../../../../../shared/query_params';

import { AddSourceLogic } from './add_source_logic';
import { CONFIG_OAUTH_LABEL, CONFIG_OAUTH_BUTTON } from './constants';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';

import { LicensingLogic } from '../../../../../../applications/shared/licensing';
import { LicensingLogic } from '../../../../../shared/licensing';
import { AppLogic } from '../../../../app_logic';
import { DOCUMENT_PERMISSIONS_DOCS_URL } from '../../../../routes';
import { FeatureIds, Configuration, Features } from '../../../../types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useActions, useValues } from 'kea';
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { parseQueryParams } from '../../../../../../applications/shared/query_params';
import { parseQueryParams } from '../../../../../shared/query_params';

import { AddSourceLogic } from './add_source_logic';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { LicensingLogic } from '../../../../../../applications/shared/licensing';
import { LicensingLogic } from '../../../../../shared/licensing';
import { ApiKey } from '../../../../components/shared/api_key';
import {
PUBLIC_KEY_LABEL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';

import { LicensingLogic } from '../../../../../../applications/shared/licensing';
import { LicensingLogic } from '../../../../../shared/licensing';
import { AppLogic } from '../../../../app_logic';
import { LicenseBadge } from '../../../../components/shared/license_badge';
import { ENT_SEARCH_LICENSE_MANAGEMENT } from '../../../../routes';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import {
EuiLink,
} from '@elastic/eui';

import { Loading } from '../../../../../applications/shared/loading';
import { DEFAULT_META } from '../../../../shared/constants';
import { Loading } from '../../../../shared/loading';
import { ComponentLoader } from '../../../components/shared/component_loader';
import { TablePaginationBar } from '../../../components/shared/table_pagination_bar';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';

import { Loading } from '../../../../../applications/shared/loading';
import { Loading } from '../../../../shared/loading';
import { TruncatedContent } from '../../../../shared/truncate';
import { ComponentLoader } from '../../../components/shared/component_loader';
import { TablePaginationBar } from '../../../components/shared/table_pagination_bar';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useActions, useValues } from 'kea';
import { EuiCallOut, EuiEmptyPrompt, EuiSpacer, EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { LicensingLogic } from '../../../../applications/shared/licensing';
import { LicensingLogic } from '../../../shared/licensing';
import { Loading } from '../../../shared/loading';
import { EuiButtonTo } from '../../../shared/react_router_helpers';
import { AppLogic } from '../../app_logic';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { Redirect, Route, Switch, useLocation } from 'react-router-dom';
import { Location } from 'history';
import { useActions, useValues } from 'kea';

import { LicensingLogic } from '../../../../applications/shared/licensing';
import { FlashMessages } from '../../../shared/flash_messages';
import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { LicensingLogic } from '../../../shared/licensing';
import { SendWorkplaceSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
import { AppLogic } from '../../app_logic';
import { NAV } from '../../constants';
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/file_upload/server/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function fileUploadRoutes(router: IRouter) {
accepts: ['application/json'],
maxBytes: MAX_FILE_SIZE_BYTES,
},
tags: ['access:ml:canFindFileStructure'],
tags: ['access:fileUpload:import'],
},
},
async (context, request, response) => {
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/infra/common/alerting/metrics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export const INFRA_ALERT_PREVIEW_PATH = '/api/infra/alerting/preview';

export const TOO_MANY_BUCKETS_PREVIEW_EXCEPTION = 'TOO_MANY_BUCKETS_PREVIEW_EXCEPTION';
export interface TooManyBucketsPreviewExceptionMetadata {
TOO_MANY_BUCKETS_PREVIEW_EXCEPTION: any;
maxBuckets: number;
TOO_MANY_BUCKETS_PREVIEW_EXCEPTION: boolean;
maxBuckets: any;
}
export const isTooManyBucketsPreviewException = (
value: any
Expand Down
Loading

0 comments on commit 82ca014

Please sign in to comment.