[7.x] Migrate most plugins to synchronous lifecycle (#89562) (#90579)

* Migrate most plugins to synchronous lifecycle (#89562)

* first pass

* migrate more plugins

* migrate yet more plugins

* more oss plugins

* fix test file

* change Plugin signature on the client-side too

* fix test types

* migrate OSS client-side plugins

* migrate OSS client-side test plugins

* migrate xpack client-side plugins

* revert fix attempt on fleet plugin

* fix presentation start signature

* fix yet another signature

* add warnings for server-side async plugins in dev mode

* remove unused import

* fix isPromise

* Add client-side deprecations

* update migration examples

* update generated doc

* fix xpack unit tests

* nit

* (will be reverted) explicitly await for license to be ready in the auth hook

* Revert "(will be reverted) explicitly await for license to be ready in the auth hook"

This reverts commit fdf73feb

* restore await on on promise contracts

* Revert "(will be reverted) explicitly await for license to be ready in the auth hook"

This reverts commit fdf73feb

* Revert "restore await on on promise contracts"

This reverts commit c5f2fe51

* add delay before starting tests in FTR

* update deprecation ts doc

* add explicit contract for monitoring setup

* migrate monitoring plugin to sync

* change plugin timeout to 10sec

* use delay instead of silence
# Conflicts:
#	x-pack/plugins/xpack_legacy/server/plugin.ts

* fix mock
This commit is contained in:
Pierre Gayvallet 2021-02-08 13:16:05 +01:00 committed by GitHub
parent f6531a53b2
commit 97f89e256b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
115 changed files with 1002 additions and 550 deletions

View file

@ -71,22 +71,20 @@ export function plugin(initializerContext: PluginInitializerContext) {
*plugins/my_plugin/(public|server)/plugin.ts*
[source,typescript]
----
import type { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import { CoreSetup, Logger, Plugin, PluginInitializerContext, PluginName } from 'kibana/server';
import type { MyPluginConfig } from './config';
export class MyPlugin implements Plugin {
private readonly config$: Observable<MyPluginConfig>;
private readonly config: MyPluginConfig;
private readonly log: Logger;
constructor(private readonly initializerContext: PluginInitializerContext) {
this.log = initializerContext.logger.get();
this.config$ = initializerContext.config.create();
this.config = initializerContext.config.get<MyPluginConfig>();
}
public async setup(core: CoreSetup, deps: Record<PluginName, unknown>) {
const isEnabled = await this.config$.pipe(first()).toPromise();
public setup(core: CoreSetup, deps: Record<PluginName, unknown>) {
const { someConfigValue } = this.config;
}
}
----
@ -96,7 +94,7 @@ Additionally, some plugins need to access the runtime env configuration.
[source,typescript]
----
export class MyPlugin implements Plugin {
public async setup(core: CoreSetup, deps: Record<PluginName, unknown>) {
public setup(core: CoreSetup, deps: Record<PluginName, unknown>) {
const { mode: { dev }, packageInfo: { version } } = this.initializerContext.env
}
----

View file

@ -0,0 +1,27 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md)
## AsyncPlugin interface
> Warning: This API is now obsolete.
>
> Asynchronous lifecycles are deprecated, and should be migrated to sync [plugin](./kibana-plugin-core-public.plugin.md)
>
A plugin with asynchronous lifecycle methods.
<b>Signature:</b>
```typescript
export interface AsyncPlugin<TSetup = void, TStart = void, TPluginsSetup extends object = object, TPluginsStart extends object = object>
```
## Methods
| Method | Description |
| --- | --- |
| [setup(core, plugins)](./kibana-plugin-core-public.asyncplugin.setup.md) | |
| [start(core, plugins)](./kibana-plugin-core-public.asyncplugin.start.md) | |
| [stop()](./kibana-plugin-core-public.asyncplugin.stop.md) | |

View file

@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md) &gt; [setup](./kibana-plugin-core-public.asyncplugin.setup.md)
## AsyncPlugin.setup() method
<b>Signature:</b>
```typescript
setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| core | <code>CoreSetup&lt;TPluginsStart, TStart&gt;</code> | |
| plugins | <code>TPluginsSetup</code> | |
<b>Returns:</b>
`TSetup | Promise<TSetup>`

View file

@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md) &gt; [start](./kibana-plugin-core-public.asyncplugin.start.md)
## AsyncPlugin.start() method
<b>Signature:</b>
```typescript
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| core | <code>CoreStart</code> | |
| plugins | <code>TPluginsStart</code> | |
<b>Returns:</b>
`TStart | Promise<TStart>`

View file

@ -0,0 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md) &gt; [stop](./kibana-plugin-core-public.asyncplugin.stop.md)
## AsyncPlugin.stop() method
<b>Signature:</b>
```typescript
stop?(): void;
```
<b>Returns:</b>
`void`

View file

@ -39,6 +39,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [ApplicationStart](./kibana-plugin-core-public.applicationstart.md) | |
| [AppMeta](./kibana-plugin-core-public.appmeta.md) | Input type for meta data for an application.<!-- -->Meta fields include <code>keywords</code> and <code>searchDeepLinks</code> Keywords is an array of string with which to associate the app, must include at least one unique string as an array. <code>searchDeepLinks</code> is an array of links that represent secondary in-app locations for the app. |
| [AppMountParameters](./kibana-plugin-core-public.appmountparameters.md) | |
| [AsyncPlugin](./kibana-plugin-core-public.asyncplugin.md) | A plugin with asynchronous lifecycle methods. |
| [Capabilities](./kibana-plugin-core-public.capabilities.md) | The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled. |
| [ChromeBadge](./kibana-plugin-core-public.chromebadge.md) | |
| [ChromeBrand](./kibana-plugin-core-public.chromebrand.md) | |

View file

@ -7,7 +7,7 @@
<b>Signature:</b>
```typescript
setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup;
```
## Parameters
@ -19,5 +19,5 @@ setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup |
<b>Returns:</b>
`TSetup | Promise<TSetup>`
`TSetup`

View file

@ -7,7 +7,7 @@
<b>Signature:</b>
```typescript
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
start(core: CoreStart, plugins: TPluginsStart): TStart;
```
## Parameters
@ -19,5 +19,5 @@ start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
<b>Returns:</b>
`TStart | Promise<TStart>`
`TStart`

View file

@ -9,5 +9,5 @@ The `plugin` export at the root of a plugin's `public` directory should conform
<b>Signature:</b>
```typescript
export declare type PluginInitializer<TSetup, TStart, TPluginsSetup extends object = object, TPluginsStart extends object = object> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
export declare type PluginInitializer<TSetup, TStart, TPluginsSetup extends object = object, TPluginsStart extends object = object> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart> | AsyncPlugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
```

View file

@ -0,0 +1,27 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [AsyncPlugin](./kibana-plugin-core-server.asyncplugin.md)
## AsyncPlugin interface
> Warning: This API is now obsolete.
>
> Asynchronous lifecycles are deprecated, and should be migrated to sync [plugin](./kibana-plugin-core-server.plugin.md)
>
A plugin with asynchronous lifecycle methods.
<b>Signature:</b>
```typescript
export interface AsyncPlugin<TSetup = void, TStart = void, TPluginsSetup extends object = object, TPluginsStart extends object = object>
```
## Methods
| Method | Description |
| --- | --- |
| [setup(core, plugins)](./kibana-plugin-core-server.asyncplugin.setup.md) | |
| [start(core, plugins)](./kibana-plugin-core-server.asyncplugin.start.md) | |
| [stop()](./kibana-plugin-core-server.asyncplugin.stop.md) | |

View file

@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [AsyncPlugin](./kibana-plugin-core-server.asyncplugin.md) &gt; [setup](./kibana-plugin-core-server.asyncplugin.setup.md)
## AsyncPlugin.setup() method
<b>Signature:</b>
```typescript
setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| core | <code>CoreSetup</code> | |
| plugins | <code>TPluginsSetup</code> | |
<b>Returns:</b>
`TSetup | Promise<TSetup>`

View file

@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [AsyncPlugin](./kibana-plugin-core-server.asyncplugin.md) &gt; [start](./kibana-plugin-core-server.asyncplugin.start.md)
## AsyncPlugin.start() method
<b>Signature:</b>
```typescript
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| core | <code>CoreStart</code> | |
| plugins | <code>TPluginsStart</code> | |
<b>Returns:</b>
`TStart | Promise<TStart>`

View file

@ -0,0 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [AsyncPlugin](./kibana-plugin-core-server.asyncplugin.md) &gt; [stop](./kibana-plugin-core-server.asyncplugin.stop.md)
## AsyncPlugin.stop() method
<b>Signature:</b>
```typescript
stop?(): void;
```
<b>Returns:</b>
`void`

View file

@ -49,6 +49,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [AppCategory](./kibana-plugin-core-server.appcategory.md) | A category definition for nav links to know where to sort them in the left hand nav |
| [AssistanceAPIResponse](./kibana-plugin-core-server.assistanceapiresponse.md) | |
| [AssistantAPIClientParams](./kibana-plugin-core-server.assistantapiclientparams.md) | |
| [AsyncPlugin](./kibana-plugin-core-server.asyncplugin.md) | A plugin with asynchronous lifecycle methods. |
| [Authenticated](./kibana-plugin-core-server.authenticated.md) | |
| [AuthNotHandled](./kibana-plugin-core-server.authnothandled.md) | |
| [AuthRedirected](./kibana-plugin-core-server.authredirected.md) | |

View file

@ -7,7 +7,7 @@
<b>Signature:</b>
```typescript
setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
setup(core: CoreSetup, plugins: TPluginsSetup): TSetup;
```
## Parameters
@ -19,5 +19,5 @@ setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
<b>Returns:</b>
`TSetup | Promise<TSetup>`
`TSetup`

View file

@ -7,7 +7,7 @@
<b>Signature:</b>
```typescript
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
start(core: CoreStart, plugins: TPluginsStart): TStart;
```
## Parameters
@ -19,5 +19,5 @@ start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
<b>Returns:</b>
`TStart | Promise<TStart>`
`TStart`

View file

@ -9,5 +9,5 @@ The `plugin` export at the root of a plugin's `server` directory should conform
<b>Signature:</b>
```typescript
export declare type PluginInitializer<TSetup, TStart, TPluginsSetup extends object = object, TPluginsStart extends object = object> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
export declare type PluginInitializer<TSetup, TStart, TPluginsSetup extends object = object, TPluginsStart extends object = object> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart> | AsyncPlugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
```

View file

@ -12,7 +12,7 @@ export { get } from './get';
export { mapToObject } from './map_to_object';
export { merge } from './merge';
export { pick } from './pick';
export { withTimeout } from './promise';
export { withTimeout, isPromise } from './promise';
export { isRelativeUrl, modifyUrl, getUrlOrigin, URLMeaningfulParts } from './url';
export { unset } from './unset';
export { getFlattenedObject } from './get_flattened_object';

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { withTimeout } from './promise';
import { withTimeout, isPromise } from './promise';
const delay = (ms: number, resolveValue?: any) =>
new Promise((resolve) => setTimeout(resolve, ms, resolveValue));
@ -50,3 +50,30 @@ describe('withTimeout', () => {
).rejects.toMatchInlineSnapshot(`[Error: from-promise]`);
});
});
describe('isPromise', () => {
it('returns true when arg is a Promise', () => {
expect(isPromise(Promise.resolve('foo'))).toEqual(true);
expect(isPromise(Promise.reject('foo').catch(() => undefined))).toEqual(true);
});
it('returns false when arg is not a Promise', () => {
expect(isPromise(12)).toEqual(false);
expect(isPromise('foo')).toEqual(false);
expect(isPromise({ hello: 'dolly' })).toEqual(false);
expect(isPromise([1, 2, 3])).toEqual(false);
});
it('returns false for objects with a non-function `then` property', () => {
expect(isPromise({ then: 'bar' })).toEqual(false);
});
it('returns false for null and undefined', () => {
expect(isPromise(null)).toEqual(false);
expect(isPromise(undefined)).toEqual(false);
});
it('returns true for Promise-Like objects', () => {
expect(isPromise({ then: () => 12 })).toEqual(true);
});
});

View file

@ -20,3 +20,7 @@ export function withTimeout<T>({
new Promise((resolve, reject) => setTimeout(() => reject(new Error(errorMessage)), timeout)),
]) as Promise<T>;
}
export function isPromise<T>(maybePromise: T | Promise<T>): maybePromise is Promise<T> {
return maybePromise ? typeof (maybePromise as Promise<T>).then === 'function' : false;
}

View file

@ -95,6 +95,8 @@ export async function runTests(options) {
try {
es = await runElasticsearch({ config, options: opts });
await runKibanaServer({ procs, config, options: opts });
// workaround until https://github.com/elastic/kibana/issues/89828 is addressed
await delay(5000);
await runFtr({ configPath, options: opts });
} finally {
try {
@ -160,3 +162,7 @@ async function silence(log, milliseconds) {
)
.toPromise();
}
async function delay(ms) {
await new Promise((resolve) => setTimeout(resolve, ms));
}

View file

@ -53,7 +53,13 @@ import { HttpSetup, HttpStart } from './http';
import { I18nStart } from './i18n';
import { NotificationsSetup, NotificationsStart } from './notifications';
import { OverlayStart } from './overlays';
import { Plugin, PluginInitializer, PluginInitializerContext, PluginOpaqueId } from './plugins';
import {
Plugin,
AsyncPlugin,
PluginInitializer,
PluginInitializerContext,
PluginOpaqueId,
} from './plugins';
import { UiSettingsState, IUiSettingsClient } from './ui_settings';
import { ApplicationSetup, Capabilities, ApplicationStart } from './application';
import { DocLinksStart } from './doc_links';
@ -305,6 +311,7 @@ export {
NotificationsSetup,
NotificationsStart,
Plugin,
AsyncPlugin,
PluginInitializer,
PluginInitializerContext,
SavedObjectsStart,

View file

@ -110,14 +110,14 @@ function pluginInitializerContextMock(config: any = {}) {
return mock;
}
function createCoreContext(): CoreContext {
function createCoreContext({ production = false }: { production?: boolean } = {}): CoreContext {
return {
coreId: Symbol('core context mock'),
env: {
mode: {
dev: true,
name: 'development',
prod: false,
dev: !production,
name: production ? 'production' : 'development',
prod: production,
},
packageInfo: {
version: 'version',

View file

@ -7,6 +7,6 @@
*/
export * from './plugins_service';
export { Plugin, PluginInitializer } from './plugin';
export { Plugin, AsyncPlugin, PluginInitializer } from './plugin';
export { PluginInitializerContext } from './plugin_context';
export { PluginOpaqueId } from '../../server/types';

View file

@ -39,16 +39,16 @@ beforeEach(() => {
});
describe('PluginWrapper', () => {
test('`setup` fails if plugin.setup is not a function', async () => {
test('`setup` fails if plugin.setup is not a function', () => {
mockInitializer.mockReturnValueOnce({ start: jest.fn() } as any);
await expect(plugin.setup({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot(
expect(() => plugin.setup({} as any, {} as any)).toThrowErrorMatchingInlineSnapshot(
`"Instance of plugin \\"plugin-a\\" does not define \\"setup\\" function."`
);
});
test('`setup` fails if plugin.start is not a function', async () => {
test('`setup` fails if plugin.start is not a function', () => {
mockInitializer.mockReturnValueOnce({ setup: jest.fn() } as any);
await expect(plugin.setup({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot(
expect(() => plugin.setup({} as any, {} as any)).toThrowErrorMatchingInlineSnapshot(
`"Instance of plugin \\"plugin-a\\" does not define \\"start\\" function."`
);
});
@ -65,8 +65,8 @@ describe('PluginWrapper', () => {
expect(mockPlugin.setup).toHaveBeenCalledWith(context, deps);
});
test('`start` fails if setup is not called first', async () => {
await expect(plugin.start({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot(
test('`start` fails if setup is not called first', () => {
expect(() => plugin.start({} as any, {} as any)).toThrowErrorMatchingInlineSnapshot(
`"Plugin \\"plugin-a\\" can't be started since it isn't set up."`
);
});

View file

@ -8,6 +8,7 @@
import { Subject } from 'rxjs';
import { first } from 'rxjs/operators';
import { isPromise } from '@kbn/std';
import { DiscoveredPlugin, PluginOpaqueId } from '../../server';
import { PluginInitializerContext } from './plugin_context';
import { read } from './plugin_reader';
@ -23,6 +24,23 @@ export interface Plugin<
TStart = void,
TPluginsSetup extends object = object,
TPluginsStart extends object = object
> {
setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup;
start(core: CoreStart, plugins: TPluginsStart): TStart;
stop?(): void;
}
/**
* A plugin with asynchronous lifecycle methods.
*
* @deprecated Asynchronous lifecycles are deprecated, and should be migrated to sync {@link Plugin | plugin}
* @public
*/
export interface AsyncPlugin<
TSetup = void,
TStart = void,
TPluginsSetup extends object = object,
TPluginsStart extends object = object
> {
setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
@ -40,7 +58,11 @@ export type PluginInitializer<
TStart,
TPluginsSetup extends object = object,
TPluginsStart extends object = object
> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
> = (
core: PluginInitializerContext
) =>
| Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>
| AsyncPlugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
/**
* Lightweight wrapper around discovered plugin that is responsible for instantiating
@ -58,7 +80,9 @@ export class PluginWrapper<
public readonly configPath: DiscoveredPlugin['configPath'];
public readonly requiredPlugins: DiscoveredPlugin['requiredPlugins'];
public readonly optionalPlugins: DiscoveredPlugin['optionalPlugins'];
private instance?: Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
private instance?:
| Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>
| AsyncPlugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
private readonly startDependencies$ = new Subject<[CoreStart, TPluginsStart, TStart]>();
public readonly startDependencies = this.startDependencies$.pipe(first()).toPromise();
@ -81,10 +105,12 @@ export class PluginWrapper<
* @param plugins The dictionary where the key is the dependency name and the value
* is the contract returned by the dependency's `setup` function.
*/
public async setup(setupContext: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup) {
this.instance = await this.createPluginInstance();
return await this.instance.setup(setupContext, plugins);
public setup(
setupContext: CoreSetup<TPluginsStart, TStart>,
plugins: TPluginsSetup
): TSetup | Promise<TSetup> {
this.instance = this.createPluginInstance();
return this.instance.setup(setupContext, plugins);
}
/**
@ -94,16 +120,21 @@ export class PluginWrapper<
* @param plugins The dictionary where the key is the dependency name and the value
* is the contract returned by the dependency's `start` function.
*/
public async start(startContext: CoreStart, plugins: TPluginsStart) {
public start(startContext: CoreStart, plugins: TPluginsStart) {
if (this.instance === undefined) {
throw new Error(`Plugin "${this.name}" can't be started since it isn't set up.`);
}
const startContract = await this.instance.start(startContext, plugins);
this.startDependencies$.next([startContext, plugins, startContract]);
return startContract;
const startContract = this.instance.start(startContext, plugins);
if (isPromise(startContract)) {
return startContract.then((resolvedContract) => {
this.startDependencies$.next([startContext, plugins, resolvedContract]);
return resolvedContract;
});
} else {
this.startDependencies$.next([startContext, plugins, startContract]);
return startContract;
}
}
/**
@ -121,7 +152,7 @@ export class PluginWrapper<
this.instance = undefined;
}
private async createPluginInstance() {
private createPluginInstance() {
const initializer = read(this.name) as PluginInitializer<
TSetup,
TStart,

View file

@ -7,9 +7,12 @@
*/
import { PluginName } from 'kibana/server';
import { Plugin } from './plugin';
import { Plugin, AsyncPlugin } from './plugin';
export type MockedPluginInitializer = jest.Mock<Plugin<unknown, Record<string, unknown>>, any>;
export type MockedPluginInitializer = jest.Mock<
Plugin<unknown, unknown> | AsyncPlugin<unknown, unknown>,
any
>;
export const mockPluginInitializerProvider: jest.Mock<
MockedPluginInitializer,

View file

@ -146,16 +146,16 @@ describe('PluginsService', () => {
it('returns dependency tree of symbols', () => {
const pluginsService = new PluginsService(mockCoreContext, plugins);
expect(pluginsService.getOpaqueIds()).toMatchInlineSnapshot(`
Map {
Symbol(pluginA) => Array [],
Symbol(pluginB) => Array [
Symbol(pluginA),
],
Symbol(pluginC) => Array [
Symbol(pluginA),
],
}
`);
Map {
Symbol(pluginA) => Array [],
Symbol(pluginB) => Array [
Symbol(pluginA),
],
Symbol(pluginC) => Array [
Symbol(pluginA),
],
}
`);
});
});
@ -264,7 +264,7 @@ describe('PluginsService', () => {
jest.runAllTimers(); // setup plugins
await expect(promise).rejects.toMatchInlineSnapshot(
`[Error: Setup lifecycle of "pluginA" plugin wasn't completed in 30sec. Consider disabling the plugin and re-start.]`
`[Error: Setup lifecycle of "pluginA" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.]`
);
});
});
@ -344,7 +344,7 @@ describe('PluginsService', () => {
jest.runAllTimers();
await expect(promise).rejects.toMatchInlineSnapshot(
`[Error: Start lifecycle of "pluginA" plugin wasn't completed in 30sec. Consider disabling the plugin and re-start.]`
`[Error: Start lifecycle of "pluginA" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.]`
);
});
});
@ -366,4 +366,124 @@ describe('PluginsService', () => {
expect(pluginCInstance.stop).toHaveBeenCalled();
});
});
describe('asynchronous plugins', () => {
let consoleSpy: jest.SpyInstance;
beforeEach(() => {
consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => undefined);
});
afterEach(() => {
consoleSpy.mockRestore();
});
const runScenario = async ({
production,
asyncSetup,
asyncStart,
}: {
production: boolean;
asyncSetup: boolean;
asyncStart: boolean;
}) => {
const coreContext = coreMock.createCoreContext({ production });
const syncPlugin = { id: 'sync-plugin', plugin: createManifest('sync-plugin') };
mockPluginInitializers.set(
'sync-plugin',
jest.fn(() => ({
setup: jest.fn(() => 'setup-sync'),
start: jest.fn(() => 'start-sync'),
stop: jest.fn(),
}))
);
const asyncPlugin = { id: 'async-plugin', plugin: createManifest('async-plugin') };
mockPluginInitializers.set(
'async-plugin',
jest.fn(() => ({
setup: jest.fn(() => (asyncSetup ? Promise.resolve('setup-async') : 'setup-sync')),
start: jest.fn(() => (asyncStart ? Promise.resolve('start-async') : 'start-sync')),
stop: jest.fn(),
}))
);
const pluginsService = new PluginsService(coreContext, [syncPlugin, asyncPlugin]);
await pluginsService.setup(mockSetupDeps);
await pluginsService.start(mockStartDeps);
};
it('logs a warning if a plugin returns a promise from its setup contract in dev mode', async () => {
await runScenario({
production: false,
asyncSetup: true,
asyncStart: false,
});
expect(consoleSpy.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"Plugin async-plugin is using asynchronous setup lifecycle. Asynchronous plugins support will be removed in a later version.",
],
]
`);
});
it('does not log warnings if a plugin returns a promise from its setup contract in prod mode', async () => {
await runScenario({
production: true,
asyncSetup: true,
asyncStart: false,
});
expect(consoleSpy).not.toHaveBeenCalled();
});
it('logs a warning if a plugin returns a promise from its start contract in dev mode', async () => {
await runScenario({
production: false,
asyncSetup: false,
asyncStart: true,
});
expect(consoleSpy.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"Plugin async-plugin is using asynchronous start lifecycle. Asynchronous plugins support will be removed in a later version.",
],
]
`);
});
it('does not log warnings if a plugin returns a promise from its start contract in prod mode', async () => {
await runScenario({
production: true,
asyncSetup: false,
asyncStart: true,
});
expect(consoleSpy).not.toHaveBeenCalled();
});
it('logs multiple warnings if both `setup` and `start` return promises', async () => {
await runScenario({
production: false,
asyncSetup: true,
asyncStart: true,
});
expect(consoleSpy.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"Plugin async-plugin is using asynchronous setup lifecycle. Asynchronous plugins support will be removed in a later version.",
],
Array [
"Plugin async-plugin is using asynchronous start lifecycle. Asynchronous plugins support will be removed in a later version.",
],
]
`);
});
});
});

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { withTimeout } from '@kbn/std';
import { withTimeout, isPromise } from '@kbn/std';
import { PluginName, PluginOpaqueId } from '../../server';
import { CoreService } from '../../types';
import { CoreContext } from '../core_system';
@ -98,16 +98,29 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS
{} as Record<PluginName, unknown>
);
const contract = await withTimeout({
promise: plugin.setup(
createPluginSetupContext(this.coreContext, deps, plugin),
pluginDepContracts
),
timeout: 30 * Sec,
errorMessage: `Setup lifecycle of "${pluginName}" plugin wasn't completed in 30sec. Consider disabling the plugin and re-start.`,
});
contracts.set(pluginName, contract);
let contract: unknown;
const contractOrPromise = plugin.setup(
createPluginSetupContext(this.coreContext, deps, plugin),
pluginDepContracts
);
if (isPromise(contractOrPromise)) {
if (this.coreContext.env.mode.dev) {
// eslint-disable-next-line no-console
console.log(
`Plugin ${pluginName} is using asynchronous setup lifecycle. Asynchronous plugins support will be removed in a later version.`
);
}
contract = await withTimeout({
promise: contractOrPromise,
timeout: 10 * Sec,
errorMessage: `Setup lifecycle of "${pluginName}" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.`,
});
} else {
contract = contractOrPromise;
}
contracts.set(pluginName, contract);
this.satupPlugins.push(pluginName);
}
@ -132,14 +145,28 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS
{} as Record<PluginName, unknown>
);
const contract = await withTimeout({
promise: plugin.start(
createPluginStartContext(this.coreContext, deps, plugin),
pluginDepContracts
),
timeout: 30 * Sec,
errorMessage: `Start lifecycle of "${pluginName}" plugin wasn't completed in 30sec. Consider disabling the plugin and re-start.`,
});
let contract: unknown;
const contractOrPromise = plugin.start(
createPluginStartContext(this.coreContext, deps, plugin),
pluginDepContracts
);
if (isPromise(contractOrPromise)) {
if (this.coreContext.env.mode.dev) {
// eslint-disable-next-line no-console
console.log(
`Plugin ${pluginName} is using asynchronous start lifecycle. Asynchronous plugins support will be removed in a later version.`
);
}
contract = await withTimeout({
promise: contractOrPromise,
timeout: 10 * Sec,
errorMessage: `Start lifecycle of "${pluginName}" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.`,
});
} else {
contract = contractOrPromise;
}
contracts.set(pluginName, contract);
}

View file

@ -194,6 +194,16 @@ export type AppUpdatableFields = Pick<App, 'status' | 'navLinkStatus' | 'tooltip
// @public
export type AppUpdater = (app: App) => Partial<AppUpdatableFields> | undefined;
// @public @deprecated
export interface AsyncPlugin<TSetup = void, TStart = void, TPluginsSetup extends object = object, TPluginsStart extends object = object> {
// (undocumented)
setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
// (undocumented)
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
// (undocumented)
stop?(): void;
}
// @public
export interface Capabilities {
[key: string]: Record<string, boolean | Record<string, boolean>>;
@ -990,15 +1000,15 @@ export { PackageInfo }
// @public
export interface Plugin<TSetup = void, TStart = void, TPluginsSetup extends object = object, TPluginsStart extends object = object> {
// (undocumented)
setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
setup(core: CoreSetup<TPluginsStart, TStart>, plugins: TPluginsSetup): TSetup;
// (undocumented)
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
start(core: CoreStart, plugins: TPluginsStart): TStart;
// (undocumented)
stop?(): void;
}
// @public
export type PluginInitializer<TSetup, TStart, TPluginsSetup extends object = object, TPluginsStart extends object = object> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
export type PluginInitializer<TSetup, TStart, TPluginsSetup extends object = object, TPluginsStart extends object = object> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart> | AsyncPlugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
// @public
export interface PluginInitializerContext<ConfigSchema extends object = object> {

View file

@ -235,6 +235,7 @@ export {
export {
DiscoveredPlugin,
Plugin,
AsyncPlugin,
PluginConfigDescriptor,
PluginConfigSchema,
PluginInitializer,

View file

@ -20,7 +20,7 @@ import { config } from '../plugins_config';
import { loggingSystemMock } from '../../logging/logging_system.mock';
import { environmentServiceMock } from '../../environment/environment_service.mock';
import { coreMock } from '../../mocks';
import { Plugin } from '../types';
import { AsyncPlugin } from '../types';
import { PluginWrapper } from '../plugin';
describe('PluginsService', () => {
@ -138,7 +138,7 @@ describe('PluginsService', () => {
expect(startDependenciesResolved).toBe(false);
return pluginStartContract;
},
} as Plugin<void, typeof pluginStartContract, {}, {}>);
} as AsyncPlugin<void, typeof pluginStartContract, {}, {}>);
jest.doMock(
join(pluginPath, 'server'),

View file

@ -100,7 +100,7 @@ test('`constructor` correctly initializes plugin instance', () => {
expect(plugin.optionalPlugins).toEqual(['some-optional-dep']);
});
test('`setup` fails if `plugin` initializer is not exported', async () => {
test('`setup` fails if `plugin` initializer is not exported', () => {
const manifest = createPluginManifest();
const opaqueId = Symbol();
const plugin = new PluginWrapper({
@ -115,14 +115,14 @@ test('`setup` fails if `plugin` initializer is not exported', async () => {
),
});
await expect(
expect(() =>
plugin.setup(createPluginSetupContext(coreContext, setupDeps, plugin), {})
).rejects.toMatchInlineSnapshot(
`[Error: Plugin "some-plugin-id" does not export "plugin" definition (plugin-without-initializer-path).]`
).toThrowErrorMatchingInlineSnapshot(
`"Plugin \\"some-plugin-id\\" does not export \\"plugin\\" definition (plugin-without-initializer-path)."`
);
});
test('`setup` fails if plugin initializer is not a function', async () => {
test('`setup` fails if plugin initializer is not a function', () => {
const manifest = createPluginManifest();
const opaqueId = Symbol();
const plugin = new PluginWrapper({
@ -137,14 +137,14 @@ test('`setup` fails if plugin initializer is not a function', async () => {
),
});
await expect(
expect(() =>
plugin.setup(createPluginSetupContext(coreContext, setupDeps, plugin), {})
).rejects.toMatchInlineSnapshot(
`[Error: Definition of plugin "some-plugin-id" should be a function (plugin-with-wrong-initializer-path).]`
).toThrowErrorMatchingInlineSnapshot(
`"Definition of plugin \\"some-plugin-id\\" should be a function (plugin-with-wrong-initializer-path)."`
);
});
test('`setup` fails if initializer does not return object', async () => {
test('`setup` fails if initializer does not return object', () => {
const manifest = createPluginManifest();
const opaqueId = Symbol();
const plugin = new PluginWrapper({
@ -161,14 +161,14 @@ test('`setup` fails if initializer does not return object', async () => {
mockPluginInitializer.mockReturnValue(null);
await expect(
expect(() =>
plugin.setup(createPluginSetupContext(coreContext, setupDeps, plugin), {})
).rejects.toMatchInlineSnapshot(
`[Error: Initializer for plugin "some-plugin-id" is expected to return plugin instance, but returned "null".]`
).toThrowErrorMatchingInlineSnapshot(
`"Initializer for plugin \\"some-plugin-id\\" is expected to return plugin instance, but returned \\"null\\"."`
);
});
test('`setup` fails if object returned from initializer does not define `setup` function', async () => {
test('`setup` fails if object returned from initializer does not define `setup` function', () => {
const manifest = createPluginManifest();
const opaqueId = Symbol();
const plugin = new PluginWrapper({
@ -186,10 +186,10 @@ test('`setup` fails if object returned from initializer does not define `setup`
const mockPluginInstance = { run: jest.fn() };
mockPluginInitializer.mockReturnValue(mockPluginInstance);
await expect(
expect(() =>
plugin.setup(createPluginSetupContext(coreContext, setupDeps, plugin), {})
).rejects.toMatchInlineSnapshot(
`[Error: Instance of plugin "some-plugin-id" does not define "setup" function.]`
).toThrowErrorMatchingInlineSnapshot(
`"Instance of plugin \\"some-plugin-id\\" does not define \\"setup\\" function."`
);
});
@ -223,7 +223,7 @@ test('`setup` initializes plugin and calls appropriate lifecycle hook', async ()
expect(mockPluginInstance.setup).toHaveBeenCalledWith(setupContext, setupDependencies);
});
test('`start` fails if setup is not called first', async () => {
test('`start` fails if setup is not called first', () => {
const manifest = createPluginManifest();
const opaqueId = Symbol();
const plugin = new PluginWrapper({
@ -238,7 +238,7 @@ test('`start` fails if setup is not called first', async () => {
),
});
await expect(plugin.start({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot(
expect(() => plugin.start({} as any, {} as any)).toThrowErrorMatchingInlineSnapshot(
`"Plugin \\"some-plugin-id\\" can't be started since it isn't set up."`
);
});

View file

@ -10,11 +10,13 @@ import { join } from 'path';
import typeDetect from 'type-detect';
import { Subject } from 'rxjs';
import { first } from 'rxjs/operators';
import { isPromise } from '@kbn/std';
import { isConfigSchema } from '@kbn/config-schema';
import { Logger } from '../logging';
import {
Plugin,
AsyncPlugin,
PluginInitializerContext,
PluginManifest,
PluginInitializer,
@ -49,7 +51,9 @@ export class PluginWrapper<
private readonly log: Logger;
private readonly initializerContext: PluginInitializerContext;
private instance?: Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
private instance?:
| Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>
| AsyncPlugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
private readonly startDependencies$ = new Subject<[CoreStart, TPluginsStart, TStart]>();
public readonly startDependencies = this.startDependencies$.pipe(first()).toPromise();
@ -83,9 +87,11 @@ export class PluginWrapper<
* @param plugins The dictionary where the key is the dependency name and the value
* is the contract returned by the dependency's `setup` function.
*/
public async setup(setupContext: CoreSetup<TPluginsStart>, plugins: TPluginsSetup) {
public setup(
setupContext: CoreSetup<TPluginsStart>,
plugins: TPluginsSetup
): TSetup | Promise<TSetup> {
this.instance = this.createPluginInstance();
return this.instance.setup(setupContext, plugins);
}
@ -96,14 +102,21 @@ export class PluginWrapper<
* @param plugins The dictionary where the key is the dependency name and the value
* is the contract returned by the dependency's `start` function.
*/
public async start(startContext: CoreStart, plugins: TPluginsStart) {
public start(startContext: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart> {
if (this.instance === undefined) {
throw new Error(`Plugin "${this.name}" can't be started since it isn't set up.`);
}
const startContract = await this.instance.start(startContext, plugins);
this.startDependencies$.next([startContext, plugins, startContract]);
return startContract;
const startContract = this.instance.start(startContext, plugins);
if (isPromise(startContract)) {
return startContract.then((resolvedContract) => {
this.startDependencies$.next([startContext, plugins, resolvedContract]);
return resolvedContract;
});
} else {
this.startDependencies$.next([startContext, plugins, startContract]);
return startContract;
}
}
/**

View file

@ -25,7 +25,6 @@ import { PluginsSystem } from './plugins_system';
import { coreMock } from '../mocks';
import { Logger } from '../logging';
const logger = loggingSystemMock.create();
function createPlugin(
id: string,
{
@ -34,8 +33,8 @@ function createPlugin(
server = true,
ui = true,
}: { required?: string[]; optional?: string[]; server?: boolean; ui?: boolean } = {}
) {
return new PluginWrapper({
): PluginWrapper<any, any> {
return new PluginWrapper<any, any>({
path: 'some-path',
manifest: {
id,
@ -53,27 +52,27 @@ function createPlugin(
});
}
let pluginsSystem: PluginsSystem;
const configService = configServiceMock.create();
configService.atPath.mockReturnValue(new BehaviorSubject({ initialize: true }));
let env: Env;
let coreContext: CoreContext;
const setupDeps = coreMock.createInternalSetup();
const startDeps = coreMock.createInternalStart();
let pluginsSystem: PluginsSystem;
let configService: ReturnType<typeof configServiceMock.create>;
let logger: ReturnType<typeof loggingSystemMock.create>;
let env: Env;
let coreContext: CoreContext;
beforeEach(() => {
logger = loggingSystemMock.create();
env = Env.createDefault(REPO_ROOT, getEnvOptions());
configService = configServiceMock.create();
configService.atPath.mockReturnValue(new BehaviorSubject({ initialize: true }));
coreContext = { coreId: Symbol(), env, logger, configService: configService as any };
pluginsSystem = new PluginsSystem(coreContext);
});
afterEach(() => {
jest.clearAllMocks();
});
test('can be setup even without plugins', async () => {
const pluginsSetup = await pluginsSystem.setupPlugins(setupDeps);
@ -208,7 +207,7 @@ test('correctly orders plugins and returns exposed values for "setup" and "start
start: { 'order-2': 'started-as-2' },
},
],
] as Array<[PluginWrapper, Contracts]>);
] as Array<[PluginWrapper<any, any>, Contracts]>);
const setupContextMap = new Map();
const startContextMap = new Map();
@ -434,7 +433,7 @@ describe('setup', () => {
afterAll(() => {
jest.useRealTimers();
});
it('throws timeout error if "setup" was not completed in 30 sec.', async () => {
it('throws timeout error if "setup" was not completed in 10 sec.', async () => {
const plugin: PluginWrapper = createPlugin('timeout-setup');
jest.spyOn(plugin, 'setup').mockImplementation(() => new Promise((i) => i));
pluginsSystem.addPlugin(plugin);
@ -444,7 +443,7 @@ describe('setup', () => {
jest.runAllTimers();
await expect(promise).rejects.toMatchInlineSnapshot(
`[Error: Setup lifecycle of "timeout-setup" plugin wasn't completed in 30sec. Consider disabling the plugin and re-start.]`
`[Error: Setup lifecycle of "timeout-setup" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.]`
);
});
@ -471,8 +470,8 @@ describe('start', () => {
afterAll(() => {
jest.useRealTimers();
});
it('throws timeout error if "start" was not completed in 30 sec.', async () => {
const plugin: PluginWrapper = createPlugin('timeout-start');
it('throws timeout error if "start" was not completed in 10 sec.', async () => {
const plugin = createPlugin('timeout-start');
jest.spyOn(plugin, 'setup').mockResolvedValue({});
jest.spyOn(plugin, 'start').mockImplementation(() => new Promise((i) => i));
@ -485,7 +484,7 @@ describe('start', () => {
jest.runAllTimers();
await expect(promise).rejects.toMatchInlineSnapshot(
`[Error: Start lifecycle of "timeout-start" plugin wasn't completed in 30sec. Consider disabling the plugin and re-start.]`
`[Error: Start lifecycle of "timeout-start" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.]`
);
});
@ -505,3 +504,120 @@ describe('start', () => {
expect(log.info).toHaveBeenCalledWith(`Starting [2] plugins: [order-1,order-0]`);
});
});
describe('asynchronous plugins', () => {
const runScenario = async ({
production,
asyncSetup,
asyncStart,
}: {
production: boolean;
asyncSetup: boolean;
asyncStart: boolean;
}) => {
env = Env.createDefault(
REPO_ROOT,
getEnvOptions({
cliArgs: {
dev: !production,
envName: production ? 'production' : 'development',
},
})
);
coreContext = { coreId: Symbol(), env, logger, configService: configService as any };
pluginsSystem = new PluginsSystem(coreContext);
const syncPlugin = createPlugin('sync-plugin');
jest.spyOn(syncPlugin, 'setup').mockReturnValue('setup-sync');
jest.spyOn(syncPlugin, 'start').mockReturnValue('start-sync');
pluginsSystem.addPlugin(syncPlugin);
const asyncPlugin = createPlugin('async-plugin');
jest
.spyOn(asyncPlugin, 'setup')
.mockReturnValue(asyncSetup ? Promise.resolve('setup-async') : 'setup-sync');
jest
.spyOn(asyncPlugin, 'start')
.mockReturnValue(asyncStart ? Promise.resolve('start-async') : 'start-sync');
pluginsSystem.addPlugin(asyncPlugin);
await pluginsSystem.setupPlugins(setupDeps);
await pluginsSystem.startPlugins(startDeps);
};
it('logs a warning if a plugin returns a promise from its setup contract in dev mode', async () => {
await runScenario({
production: false,
asyncSetup: true,
asyncStart: false,
});
const log = logger.get.mock.results[0].value as jest.Mocked<Logger>;
expect(log.warn.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"Plugin async-plugin is using asynchronous setup lifecycle. Asynchronous plugins support will be removed in a later version.",
],
]
`);
});
it('does not log warnings if a plugin returns a promise from its setup contract in prod mode', async () => {
await runScenario({
production: true,
asyncSetup: true,
asyncStart: false,
});
const log = logger.get.mock.results[0].value as jest.Mocked<Logger>;
expect(log.warn).not.toHaveBeenCalled();
});
it('logs a warning if a plugin returns a promise from its start contract in dev mode', async () => {
await runScenario({
production: false,
asyncSetup: false,
asyncStart: true,
});
const log = logger.get.mock.results[0].value as jest.Mocked<Logger>;
expect(log.warn.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"Plugin async-plugin is using asynchronous start lifecycle. Asynchronous plugins support will be removed in a later version.",
],
]
`);
});
it('does not log warnings if a plugin returns a promise from its start contract in prod mode', async () => {
await runScenario({
production: true,
asyncSetup: false,
asyncStart: true,
});
const log = logger.get.mock.results[0].value as jest.Mocked<Logger>;
expect(log.warn).not.toHaveBeenCalled();
});
it('logs multiple warnings if both `setup` and `start` return promises', async () => {
await runScenario({
production: false,
asyncSetup: true,
asyncStart: true,
});
const log = logger.get.mock.results[0].value as jest.Mocked<Logger>;
expect(log.warn.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"Plugin async-plugin is using asynchronous setup lifecycle. Asynchronous plugins support will be removed in a later version.",
],
Array [
"Plugin async-plugin is using asynchronous start lifecycle. Asynchronous plugins support will be removed in a later version.",
],
]
`);
});
});

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { withTimeout } from '@kbn/std';
import { withTimeout, isPromise } from '@kbn/std';
import { CoreContext } from '../core_context';
import { Logger } from '../logging';
import { PluginWrapper } from './plugin';
@ -94,14 +94,25 @@ export class PluginsSystem {
return depContracts;
}, {} as Record<PluginName, unknown>);
const contract = await withTimeout({
promise: plugin.setup(
createPluginSetupContext(this.coreContext, deps, plugin),
pluginDepContracts
),
timeout: 30 * Sec,
errorMessage: `Setup lifecycle of "${pluginName}" plugin wasn't completed in 30sec. Consider disabling the plugin and re-start.`,
});
let contract: unknown;
const contractOrPromise = plugin.setup(
createPluginSetupContext(this.coreContext, deps, plugin),
pluginDepContracts
);
if (isPromise(contractOrPromise)) {
if (this.coreContext.env.mode.dev) {
this.log.warn(
`Plugin ${pluginName} is using asynchronous setup lifecycle. Asynchronous plugins support will be removed in a later version.`
);
}
contract = await withTimeout({
promise: contractOrPromise,
timeout: 10 * Sec,
errorMessage: `Setup lifecycle of "${pluginName}" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.`,
});
} else {
contract = contractOrPromise;
}
contracts.set(pluginName, contract);
this.satupPlugins.push(pluginName);
@ -132,14 +143,25 @@ export class PluginsSystem {
return depContracts;
}, {} as Record<PluginName, unknown>);
const contract = await withTimeout({
promise: plugin.start(
createPluginStartContext(this.coreContext, deps, plugin),
pluginDepContracts
),
timeout: 30 * Sec,
errorMessage: `Start lifecycle of "${pluginName}" plugin wasn't completed in 30sec. Consider disabling the plugin and re-start.`,
});
let contract: unknown;
const contractOrPromise = plugin.start(
createPluginStartContext(this.coreContext, deps, plugin),
pluginDepContracts
);
if (isPromise(contractOrPromise)) {
if (this.coreContext.env.mode.dev) {
this.log.warn(
`Plugin ${pluginName} is using asynchronous start lifecycle. Asynchronous plugins support will be removed in a later version.`
);
}
contract = await withTimeout({
promise: contractOrPromise,
timeout: 10 * Sec,
errorMessage: `Start lifecycle of "${pluginName}" plugin wasn't completed in 10sec. Consider disabling the plugin and re-start.`,
});
} else {
contract = contractOrPromise;
}
contracts.set(pluginName, contract);
}

View file

@ -242,6 +242,23 @@ export interface Plugin<
TStart = void,
TPluginsSetup extends object = object,
TPluginsStart extends object = object
> {
setup(core: CoreSetup, plugins: TPluginsSetup): TSetup;
start(core: CoreStart, plugins: TPluginsStart): TStart;
stop?(): void;
}
/**
* A plugin with asynchronous lifecycle methods.
*
* @deprecated Asynchronous lifecycles are deprecated, and should be migrated to sync {@link Plugin | plugin}
* @public
*/
export interface AsyncPlugin<
TSetup = void,
TStart = void,
TPluginsSetup extends object = object,
TPluginsStart extends object = object
> {
setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
@ -383,4 +400,8 @@ export type PluginInitializer<
TStart,
TPluginsSetup extends object = object,
TPluginsStart extends object = object
> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
> = (
core: PluginInitializerContext
) =>
| Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>
| AsyncPlugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;

View file

@ -203,6 +203,16 @@ export interface AssistantAPIClientParams extends GenericParams {
path: '/_migration/assistance';
}
// @public @deprecated
export interface AsyncPlugin<TSetup = void, TStart = void, TPluginsSetup extends object = object, TPluginsStart extends object = object> {
// (undocumented)
setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
// (undocumented)
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
// (undocumented)
stop?(): void;
}
// @public (undocumented)
export interface Authenticated extends AuthResultParams {
// (undocumented)
@ -1815,9 +1825,9 @@ export { PackageInfo }
// @public
export interface Plugin<TSetup = void, TStart = void, TPluginsSetup extends object = object, TPluginsStart extends object = object> {
// (undocumented)
setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise<TSetup>;
setup(core: CoreSetup, plugins: TPluginsSetup): TSetup;
// (undocumented)
start(core: CoreStart, plugins: TPluginsStart): TStart | Promise<TStart>;
start(core: CoreStart, plugins: TPluginsStart): TStart;
// (undocumented)
stop?(): void;
}
@ -1836,7 +1846,7 @@ export interface PluginConfigDescriptor<T = any> {
export type PluginConfigSchema<T> = Type<T>;
// @public
export type PluginInitializer<TSetup, TStart, TPluginsSetup extends object = object, TPluginsStart extends object = object> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
export type PluginInitializer<TSetup, TStart, TPluginsSetup extends object = object, TPluginsStart extends object = object> = (core: PluginInitializerContext) => Plugin<TSetup, TStart, TPluginsSetup, TPluginsStart> | AsyncPlugin<TSetup, TStart, TPluginsSetup, TPluginsStart>;
// @public
export interface PluginInitializerContext<ConfigSchema = unknown> {
@ -3135,9 +3145,9 @@ export const validBodyOutput: readonly ["data", "stream"];
// Warnings were encountered during analysis:
//
// src/core/server/http/router/response.ts:306:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:263:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:263:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:266:3 - (ae-forgotten-export) The symbol "SavedObjectsConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:371:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "create"
// src/core/server/plugins/types.ts:280:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:280:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:283:3 - (ae-forgotten-export) The symbol "SavedObjectsConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:388:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "create"
```

View file

@ -26,6 +26,7 @@ const defaultConfig = {
export const apmOSSPluginSetupMock = {
create(config: Partial<APMOSSConfig> = {}): APMOSSPluginSetup {
return {
config: { ...defaultConfig, ...config },
config$: of({ ...defaultConfig, ...config }),
getRegisteredTutorialProvider: jest.fn(),
};

View file

@ -8,7 +8,6 @@
import { Plugin, CoreSetup, PluginInitializerContext } from 'src/core/server';
import { Observable } from 'rxjs';
import { take } from 'rxjs/operators';
import { APMOSSConfig } from './';
import { HomeServerPluginSetup, TutorialProvider } from '../../home/server';
import { tutorialProvider } from './tutorial';
@ -17,10 +16,10 @@ export class APMOSSPlugin implements Plugin<APMOSSPluginSetup> {
constructor(private readonly initContext: PluginInitializerContext) {
this.initContext = initContext;
}
public async setup(core: CoreSetup, plugins: { home: HomeServerPluginSetup }) {
public setup(core: CoreSetup, plugins: { home: HomeServerPluginSetup }) {
const config$ = this.initContext.config.create<APMOSSConfig>();
const config = await config$.pipe(take(1)).toPromise();
const config = this.initContext.config.get<APMOSSConfig>();
const apmTutorialProvider = tutorialProvider({
indexPatternTitle: config.indexPattern,
@ -35,6 +34,7 @@ export class APMOSSPlugin implements Plugin<APMOSSPluginSetup> {
plugins.home.tutorials.registerTutorial(apmTutorialProvider);
return {
config,
config$,
getRegisteredTutorialProvider: () => apmTutorialProvider,
};
@ -45,6 +45,7 @@ export class APMOSSPlugin implements Plugin<APMOSSPluginSetup> {
}
export interface APMOSSPluginSetup {
config: APMOSSConfig;
config$: Observable<APMOSSConfig>;
getRegisteredTutorialProvider(): TutorialProvider;
}

View file

@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
import { first } from 'rxjs/operators';
import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'kibana/server';
import { ProxyConfigCollection } from './lib';
@ -28,7 +27,7 @@ export class ConsoleServerPlugin implements Plugin<ConsoleSetup, ConsoleStart> {
this.log = this.ctx.logger.get();
}
async setup({ http, capabilities, getStartServices, elasticsearch }: CoreSetup) {
setup({ http, capabilities, getStartServices, elasticsearch }: CoreSetup) {
capabilities.registerProvider(() => ({
dev_tools: {
show: true,
@ -36,8 +35,8 @@ export class ConsoleServerPlugin implements Plugin<ConsoleSetup, ConsoleStart> {
},
}));
const config = await this.ctx.config.create().pipe(first()).toPromise();
const globalConfig = await this.ctx.config.legacy.globalConfig$.pipe(first()).toPromise();
const config = this.ctx.config.get();
const globalConfig = this.ctx.config.legacy.get();
const proxyPathFilters = config.proxyFilter.map((str: string) => new RegExp(str));
this.esLegacyConfigService.setup(elasticsearch.legacy.config$);

View file

@ -56,7 +56,7 @@ export class InspectorPublicPlugin implements Plugin<Setup, Start> {
constructor(initializerContext: PluginInitializerContext) {}
public async setup(core: CoreSetup) {
public setup(core: CoreSetup) {
this.views = new InspectorViewRegistry();
this.views.register(getRequestsViewDescription());

View file

@ -7,16 +7,13 @@
*/
import { Plugin, CoreSetup, PluginInitializerContext } from 'kibana/server';
import { first } from 'rxjs/operators';
import { registerRoutes } from './routes';
export class LegacyExportPlugin implements Plugin<{}, {}> {
constructor(private readonly initContext: PluginInitializerContext) {}
public async setup({ http }: CoreSetup) {
const globalConfig = await this.initContext.config.legacy.globalConfig$
.pipe(first())
.toPromise();
public setup({ http }: CoreSetup) {
const globalConfig = this.initContext.config.legacy.get();
const router = http.createRouter();
registerRoutes(

View file

@ -8,7 +8,6 @@
import { Plugin, PluginConfigDescriptor } from 'kibana/server';
import { CoreSetup, PluginInitializerContext } from 'src/core/server';
import { Observable } from 'rxjs';
import { configSchema, MapsLegacyConfig } from '../config';
import { getUiSettings } from './ui_settings';
@ -30,7 +29,7 @@ export const config: PluginConfigDescriptor<MapsLegacyConfig> = {
};
export interface MapsLegacyPluginSetup {
config$: Observable<MapsLegacyConfig>;
config: MapsLegacyConfig;
}
export class MapsLegacyPlugin implements Plugin<MapsLegacyPluginSetup> {
@ -43,10 +42,9 @@ export class MapsLegacyPlugin implements Plugin<MapsLegacyPluginSetup> {
public setup(core: CoreSetup) {
core.uiSettings.register(getUiSettings());
// @ts-ignore
const config$ = this._initializerContext.config.create();
const pluginConfig = this._initializerContext.config.get();
return {
config$,
config: pluginConfig,
};
}

View file

@ -31,10 +31,10 @@ export class PresentationUtilPlugin
return {};
}
public async start(
public start(
coreStart: CoreStart,
startPlugins: PresentationUtilPluginStartDeps
): Promise<PresentationUtilPluginStart> {
): PresentationUtilPluginStart {
pluginServices.setRegistry(registry.start({ coreStart, startPlugins }));
return {

View file

@ -79,7 +79,7 @@ export class RegionMapPlugin implements Plugin<RegionMapPluginSetup, RegionMapPl
this._initializerContext = initializerContext;
}
public async setup(
public setup(
core: CoreSetup,
{ expressions, visualizations, mapsLegacy }: RegionMapPluginSetupDependencies
) {

View file

@ -23,7 +23,7 @@ export class SavedObjectsManagementPlugin
this.logger = this.context.logger.get();
}
public async setup({ http, capabilities }: CoreSetup) {
public setup({ http, capabilities }: CoreSetup) {
this.logger.debug('Setting up SavedObjectsManagement plugin');
registerRoutes({
http,
@ -35,7 +35,7 @@ export class SavedObjectsManagementPlugin
return {};
}
public async start(core: CoreStart) {
public start(core: CoreStart) {
this.logger.debug('Starting up SavedObjectsManagement plugin');
const managementService = new SavedObjectsManagement(core.savedObjects.getTypeRegistry());
this.managementService$.next(managementService);

View file

@ -16,7 +16,7 @@ import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../common/const
export class SharePlugin implements Plugin {
constructor(private readonly initializerContext: PluginInitializerContext) {}
public async setup(core: CoreSetup) {
public setup(core: CoreSetup) {
createRoutes(core, this.initializerContext.logger.get());
core.savedObjects.registerType(url);
core.uiSettings.register({

View file

@ -72,7 +72,7 @@ export class TileMapPlugin implements Plugin<TileMapPluginSetup, TileMapPluginSt
this.initializerContext = initializerContext;
}
public async setup(
public setup(
core: CoreSetup,
{ expressions, visualizations, mapsLegacy }: TileMapPluginSetupDependencies
) {

View file

@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
import { first } from 'rxjs/operators';
import {
PluginInitializerContext,
Logger,
@ -27,20 +26,15 @@ export class UsageCollectionPlugin implements Plugin<CollectorSet> {
this.logger = this.initializerContext.logger.get();
}
public async setup(core: CoreSetup) {
const config = await this.initializerContext.config
.create<ConfigType>()
.pipe(first())
.toPromise();
public setup(core: CoreSetup) {
const config = this.initializerContext.config.get<ConfigType>();
const collectorSet = new CollectorSet({
logger: this.logger.get('collector-set'),
maximumWaitTimeForAllCollectorsInS: config.maximumWaitTimeForAllCollectorsInS,
});
const globalConfig = await this.initializerContext.config.legacy.globalConfig$
.pipe(first())
.toPromise();
const globalConfig = this.initializerContext.config.legacy.get();
const router = core.http.createRouter();
setupRoutes({

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public';
import { PluginInitializerContext, CoreSetup, CoreStart, AsyncPlugin } from 'kibana/public';
import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public';
import { VisualizationsSetup } from '../../visualizations/public';
import { UsageCollectionSetup } from '../../usage_collection/public';
@ -34,8 +34,7 @@ export interface TablePluginStartDependencies {
/** @internal */
export class TableVisPlugin
implements
Plugin<Promise<void>, void, TablePluginSetupDependencies, TablePluginStartDependencies> {
implements AsyncPlugin<void, void, TablePluginSetupDependencies, TablePluginStartDependencies> {
initializerContext: PluginInitializerContext<ClientConfigType>;
constructor(initializerContext: PluginInitializerContext) {

View file

@ -8,7 +8,7 @@
import { PluginConfigDescriptor, PluginInitializerContext } from '../../../../src/core/server';
import { configSchema, ConfigSchema } from '../config';
import { Plugin } from './plugin';
import { TimelionPlugin } from './plugin';
export { PluginSetupContract } from './plugin';
@ -25,4 +25,4 @@ export const config: PluginConfigDescriptor<ConfigSchema> = {
],
};
export const plugin = (initializerContext: PluginInitializerContext) =>
new Plugin(initializerContext);
new TimelionPlugin(initializerContext);

View file

@ -7,13 +7,12 @@
*/
import { i18n } from '@kbn/i18n';
import { first } from 'rxjs/operators';
import { TypeOf, schema } from '@kbn/config-schema';
import { RecursiveReadonly } from '@kbn/utility-types';
import { deepFreeze } from '@kbn/std';
import type { PluginStart, DataRequestHandlerContext } from '../../../../src/plugins/data/server';
import { CoreSetup, PluginInitializerContext } from '../../../../src/core/server';
import { CoreSetup, PluginInitializerContext, Plugin } from '../../../../src/core/server';
import { configSchema } from '../config';
import loadFunctions from './lib/load_functions';
import { functionsRoute } from './routes/functions';
@ -39,16 +38,12 @@ export interface TimelionPluginStartDeps {
/**
* Represents Timelion Plugin instance that will be managed by the Kibana plugin system.
*/
export class Plugin {
export class TimelionPlugin
implements Plugin<RecursiveReadonly<PluginSetupContract>, void, TimelionPluginStartDeps> {
constructor(private readonly initializerContext: PluginInitializerContext) {}
public async setup(
core: CoreSetup<TimelionPluginStartDeps>
): Promise<RecursiveReadonly<PluginSetupContract>> {
const config = await this.initializerContext.config
.create<TypeOf<typeof configSchema>>()
.pipe(first())
.toPromise();
public setup(core: CoreSetup<TimelionPluginStartDeps>): RecursiveReadonly<PluginSetupContract> {
const config = this.initializerContext.config.get<TypeOf<typeof configSchema>>();
const configManager = new ConfigManager(this.initializerContext.config);

View file

@ -43,14 +43,14 @@ export interface MetricsPluginStartDependencies {
}
/** @internal */
export class MetricsPlugin implements Plugin<Promise<void>, void> {
export class MetricsPlugin implements Plugin<void, void> {
initializerContext: PluginInitializerContext;
constructor(initializerContext: PluginInitializerContext) {
this.initializerContext = initializerContext;
}
public async setup(
public setup(
core: CoreSetup,
{ expressions, visualizations, charts, visualize }: MetricsPluginSetupDependencies
) {

View file

@ -54,14 +54,14 @@ export interface VegaPluginStartDependencies {
}
/** @internal */
export class VegaPlugin implements Plugin<Promise<void>, void> {
export class VegaPlugin implements Plugin<void, void> {
initializerContext: PluginInitializerContext<ConfigSchema>;
constructor(initializerContext: PluginInitializerContext<ConfigSchema>) {
this.initializerContext = initializerContext;
}
public async setup(
public setup(
core: CoreSetup,
{ inspector, data, expressions, visualizations, mapsLegacy }: VegaPluginSetupDependencies
) {

View file

@ -46,7 +46,7 @@ export class VisTypeVislibPlugin
Plugin<void, void, VisTypeVislibPluginSetupDependencies, VisTypeVislibPluginStartDependencies> {
constructor(public initializerContext: PluginInitializerContext) {}
public async setup(
public setup(
core: VisTypeVislibCoreSetup,
{ expressions, visualizations, charts }: VisTypeVislibPluginSetupDependencies
) {

View file

@ -59,7 +59,7 @@ export class VisTypeXyPlugin
VisTypeXyPluginSetupDependencies,
VisTypeXyPluginStartDependencies
> {
public async setup(
public setup(
core: VisTypeXyCoreSetup,
{ expressions, visualizations, charts, usageCollection }: VisTypeXyPluginSetupDependencies
) {

View file

@ -84,7 +84,7 @@ export class VisualizePlugin
constructor(private initializerContext: PluginInitializerContext) {}
public async setup(
public setup(
core: CoreSetup<VisualizePluginStartDependencies>,
{ home, urlForwarding, data }: VisualizePluginSetupDependencies
) {

View file

@ -10,7 +10,7 @@ import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
import { renderApp } from './app';
export class CoreAppLinkPlugin implements Plugin<CoreAppLinkPluginSetup, CoreAppLinkPluginStart> {
public async setup(core: CoreSetup, deps: {}) {
public setup(core: CoreSetup, deps: {}) {
core.application.register({
id: 'applink_start',
title: 'AppLink Start',

View file

@ -42,7 +42,7 @@ export class CorePluginBPlugin
};
}
public async start(core: CoreStart, deps: {}) {
public start(core: CoreStart, deps: {}) {
return {
sendSystemRequest: async (asSystemRequest: boolean) => {
const response = await core.http.post<string>('/core_plugin_b/system_request', {

View file

@ -6,9 +6,7 @@
*/
import type { PublicMethodsOf } from '@kbn/utility-types';
import { first } from 'rxjs/operators';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { Observable } from 'rxjs';
import {
PluginInitializerContext,
Plugin,
@ -136,11 +134,9 @@ const includedHiddenTypes = [
ALERT_SAVED_OBJECT_TYPE,
];
export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, PluginStartContract> {
private readonly config: Promise<ActionsConfig>;
export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartContract> {
private readonly logger: Logger;
private actionsConfig?: ActionsConfig;
private readonly actionsConfig: ActionsConfig;
private taskRunnerFactory?: TaskRunnerFactory;
private actionTypeRegistry?: ActionTypeRegistry;
private actionExecutor?: ActionExecutor;
@ -151,20 +147,20 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
private isESOUsingEphemeralEncryptionKey?: boolean;
private readonly telemetryLogger: Logger;
private readonly preconfiguredActions: PreConfiguredAction[];
private readonly kibanaIndexConfig: Observable<{ kibana: { index: string } }>;
private readonly kibanaIndexConfig: { kibana: { index: string } };
constructor(initContext: PluginInitializerContext) {
this.config = initContext.config.create<ActionsConfig>().pipe(first()).toPromise();
this.actionsConfig = initContext.config.get<ActionsConfig>();
this.logger = initContext.logger.get('actions');
this.telemetryLogger = initContext.logger.get('usage');
this.preconfiguredActions = [];
this.kibanaIndexConfig = initContext.config.legacy.globalConfig$;
this.kibanaIndexConfig = initContext.config.legacy.get();
}
public async setup(
public setup(
core: CoreSetup<ActionsPluginsStart>,
plugins: ActionsPluginsSetup
): Promise<PluginSetupContract> {
): PluginSetupContract {
this.licenseState = new LicenseState(plugins.licensing.license$);
this.isESOUsingEphemeralEncryptionKey =
plugins.encryptedSavedObjects.usingEphemeralEncryptionKey;
@ -190,7 +186,6 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
// get executions count
const taskRunnerFactory = new TaskRunnerFactory(actionExecutor);
this.actionsConfig = (await this.config) as ActionsConfig;
const actionsConfigUtils = getActionsConfigurationUtilities(this.actionsConfig);
for (const preconfiguredId of Object.keys(this.actionsConfig.preconfigured)) {
@ -229,20 +224,18 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
);
}
this.kibanaIndexConfig.subscribe((config) => {
core.http.registerRouteHandlerContext<ActionsRequestHandlerContext, 'actions'>(
'actions',
this.createRouteHandlerContext(core, config.kibana.index)
core.http.registerRouteHandlerContext<ActionsRequestHandlerContext, 'actions'>(
'actions',
this.createRouteHandlerContext(core, this.kibanaIndexConfig.kibana.index)
);
if (usageCollection) {
initializeActionsTelemetry(
this.telemetryLogger,
plugins.taskManager,
core,
this.kibanaIndexConfig.kibana.index
);
if (usageCollection) {
initializeActionsTelemetry(
this.telemetryLogger,
plugins.taskManager,
core,
config.kibana.index
);
}
});
}
// Routes
const router = core.http.createRouter<ActionsRequestHandlerContext>();
@ -304,7 +297,7 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
request
);
const kibanaIndex = (await kibanaIndexConfig.pipe(first()).toPromise()).kibana.index;
const kibanaIndex = kibanaIndexConfig.kibana.index;
return new ActionsClient({
unsecuredSavedObjectsClient,

View file

@ -61,7 +61,7 @@ export class APMPlugin implements Plugin<APMPluginSetup> {
this.initContext = initContext;
}
public async setup(
public setup(
core: CoreSetup,
plugins: {
apmOss: APMOSSPluginSetup;
@ -98,7 +98,10 @@ export class APMPlugin implements Plugin<APMPluginSetup> {
});
}
this.currentConfig = await mergedConfig$.pipe(take(1)).toPromise();
this.currentConfig = mergeConfigs(
plugins.apmOss.config,
this.initContext.config.get<APMXPackConfig>()
);
if (
plugins.taskManager &&

View file

@ -5,8 +5,7 @@
* 2.0.
*/
import { take } from 'rxjs/operators';
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/server';
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext, Logger } from 'src/core/server';
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
import { SecurityPluginSetup } from '../../security/server';
import { LicensingPluginStart } from '../../licensing/server';
@ -27,14 +26,17 @@ interface StartDeps {
}
export class BeatsManagementPlugin implements Plugin<{}, {}, SetupDeps, StartDeps> {
private readonly logger: Logger;
private securitySetup?: SecurityPluginSetup;
private beatsLibs?: CMServerLibs;
constructor(
private readonly initializerContext: PluginInitializerContext<BeatsManagementConfigType>
) {}
) {
this.logger = initializerContext.logger.get();
}
public async setup(core: CoreSetup<StartDeps>, { features, security }: SetupDeps) {
public setup(core: CoreSetup<StartDeps>, { features, security }: SetupDeps) {
this.securitySetup = security;
const router = core.http.createRouter<BeatsManagementRequestHandlerContext>();
@ -64,8 +66,8 @@ export class BeatsManagementPlugin implements Plugin<{}, {}, SetupDeps, StartDep
return {};
}
public async start({ elasticsearch }: CoreStart, { licensing }: StartDeps) {
const config = await this.initializerContext.config.create().pipe(take(1)).toPromise();
public start({ elasticsearch }: CoreStart, { licensing }: StartDeps) {
const config = this.initializerContext.config.get();
const logger = this.initializerContext.logger.get();
const kibanaVersion = this.initializerContext.env.packageInfo.version;
@ -78,7 +80,9 @@ export class BeatsManagementPlugin implements Plugin<{}, {}, SetupDeps, StartDep
kibanaVersion,
});
await this.beatsLibs.database.putTemplate(INDEX_NAMES.BEATS, beatsIndexTemplate);
this.beatsLibs.database.putTemplate(INDEX_NAMES.BEATS, beatsIndexTemplate).catch((e) => {
this.logger.error(`Error create beats template: ${e.message}`);
});
return {};
}

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import { first } from 'rxjs/operators';
import { CoreSetup, PluginInitializerContext, Plugin, Logger, CoreStart } from 'src/core/server';
import { ExpressionsServerSetup } from 'src/plugins/expressions/server';
import { BfetchServerSetup } from 'src/plugins/bfetch/server';
@ -34,7 +33,7 @@ export class CanvasPlugin implements Plugin {
this.logger = initializerContext.logger.get();
}
public async setup(coreSetup: CoreSetup, plugins: PluginsSetup) {
public setup(coreSetup: CoreSetup, plugins: PluginsSetup) {
coreSetup.savedObjects.registerType(customElementType);
coreSetup.savedObjects.registerType(workpadType);
coreSetup.savedObjects.registerType(workpadTemplateType);
@ -84,9 +83,7 @@ export class CanvasPlugin implements Plugin {
);
// we need the kibana index provided by global config for the Canvas usage collector
const globalConfig = await this.initializerContext.config.legacy.globalConfig$
.pipe(first())
.toPromise();
const globalConfig = this.initializerContext.config.legacy.get();
registerCanvasUsageCollector(plugins.usageCollection, globalConfig.kibana.index);
setupInterpreter(plugins.expressions);

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import { first, map } from 'rxjs/operators';
import { IContextProvider, KibanaRequest, Logger, PluginInitializerContext } from 'kibana/server';
import { CoreSetup, CoreStart } from 'src/core/server';
@ -38,8 +37,8 @@ import { createCaseClient } from './client';
import { registerConnectors } from './connectors';
import type { CasesRequestHandlerContext } from './types';
function createConfig$(context: PluginInitializerContext) {
return context.config.create<ConfigType>().pipe(map((config) => config));
function createConfig(context: PluginInitializerContext) {
return context.config.get<ConfigType>();
}
export interface PluginsSetup {
@ -60,7 +59,7 @@ export class CasePlugin {
}
public async setup(core: CoreSetup, plugins: PluginsSetup) {
const config = await createConfig$(this.initializerContext).pipe(first()).toPromise();
const config = createConfig(this.initializerContext);
if (!config.enabled) {
return;
@ -118,7 +117,7 @@ export class CasePlugin {
});
}
public async start(core: CoreStart) {
public start(core: CoreStart) {
this.log.debug(`Starting Case Workflow`);
this.alertsService!.initialize(core.elasticsearch.client);

View file

@ -45,7 +45,7 @@ export class CloudPlugin implements Plugin<CloudSetup> {
this.isCloudEnabled = false;
}
public async setup(core: CoreSetup, { home }: CloudSetupDependencies) {
public setup(core: CoreSetup, { home }: CloudSetupDependencies) {
const { id, resetPasswordUrl, deploymentUrl } = this.config;
this.isCloudEnabled = getIsCloudEnabled(id);

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import { first } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'src/core/server';
import { CloudConfigType } from './config';
@ -28,25 +26,24 @@ export interface CloudSetup {
export class CloudPlugin implements Plugin<CloudSetup> {
private readonly logger: Logger;
private readonly config$: Observable<CloudConfigType>;
private readonly config: CloudConfigType;
constructor(private readonly context: PluginInitializerContext) {
this.logger = this.context.logger.get();
this.config$ = this.context.config.create<CloudConfigType>();
this.config = this.context.config.get<CloudConfigType>();
}
public async setup(core: CoreSetup, { usageCollection }: PluginsSetup) {
public setup(core: CoreSetup, { usageCollection }: PluginsSetup) {
this.logger.debug('Setting up Cloud plugin');
const config = await this.config$.pipe(first()).toPromise();
const isCloudEnabled = getIsCloudEnabled(config.id);
const isCloudEnabled = getIsCloudEnabled(this.config.id);
registerCloudUsageCollector(usageCollection, { isCloudEnabled });
return {
cloudId: config.id,
cloudId: this.config.id,
isCloudEnabled,
apm: {
url: config.apm?.url,
secretToken: config.apm?.secret_token,
url: this.config.apm?.url,
secretToken: this.config.apm?.secret_token,
},
};
}

View file

@ -5,22 +5,18 @@
* 2.0.
*/
import { first } from 'rxjs/operators';
import { TypeOf } from '@kbn/config-schema';
import { PluginInitializerContext } from 'src/core/server';
import { PluginInitializerContext, Plugin } from 'src/core/server';
import { CodeConfigSchema } from './config';
/**
* Represents Code Plugin instance that will be managed by the Kibana plugin system.
*/
export class CodePlugin {
export class CodePlugin implements Plugin {
constructor(private readonly initializerContext: PluginInitializerContext) {}
public async setup() {
const config = await this.initializerContext.config
.create<TypeOf<typeof CodeConfigSchema>>()
.pipe(first())
.toPromise();
const config = this.initializerContext.config.get<TypeOf<typeof CodeConfigSchema>>();
if (config && Object.keys(config).length > 0) {
this.initializerContext.logger

View file

@ -7,7 +7,7 @@
import { PluginInitializerContext } from 'src/core/server';
import { ConfigSchema } from './config';
import { Plugin } from './plugin';
import { EncryptedSavedObjectsPlugin } from './plugin';
export { EncryptedSavedObjectTypeRegistration, EncryptionError } from './crypto';
export { EncryptedSavedObjectsPluginSetup, EncryptedSavedObjectsPluginStart } from './plugin';
@ -15,4 +15,4 @@ export { EncryptedSavedObjectsClient } from './saved_objects';
export const config = { schema: ConfigSchema };
export const plugin = (initializerContext: PluginInitializerContext) =>
new Plugin(initializerContext);
new EncryptedSavedObjectsPlugin(initializerContext);

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { Plugin } from './plugin';
import { EncryptedSavedObjectsPlugin } from './plugin';
import { ConfigSchema } from './config';
import { coreMock } from 'src/core/server/mocks';
@ -13,12 +13,12 @@ import { securityMock } from '../../security/server/mocks';
describe('EncryptedSavedObjects Plugin', () => {
describe('setup()', () => {
it('exposes proper contract', async () => {
const plugin = new Plugin(
it('exposes proper contract', () => {
const plugin = new EncryptedSavedObjectsPlugin(
coreMock.createPluginInitializerContext(ConfigSchema.validate({}, { dist: true }))
);
await expect(plugin.setup(coreMock.createSetup(), { security: securityMock.createSetup() }))
.resolves.toMatchInlineSnapshot(`
expect(plugin.setup(coreMock.createSetup(), { security: securityMock.createSetup() }))
.toMatchInlineSnapshot(`
Object {
"createMigration": [Function],
"registerType": [Function],
@ -29,14 +29,14 @@ describe('EncryptedSavedObjects Plugin', () => {
});
describe('start()', () => {
it('exposes proper contract', async () => {
const plugin = new Plugin(
it('exposes proper contract', () => {
const plugin = new EncryptedSavedObjectsPlugin(
coreMock.createPluginInitializerContext(ConfigSchema.validate({}, { dist: true }))
);
await plugin.setup(coreMock.createSetup(), { security: securityMock.createSetup() });
plugin.setup(coreMock.createSetup(), { security: securityMock.createSetup() });
const startContract = plugin.start();
await expect(startContract).toMatchInlineSnapshot(`
expect(startContract).toMatchInlineSnapshot(`
Object {
"getClient": [Function],
"isEncryptionError": [Function],

View file

@ -5,9 +5,8 @@
* 2.0.
*/
import { first, map } from 'rxjs/operators';
import nodeCrypto from '@elastic/node-crypto';
import { Logger, PluginInitializerContext, CoreSetup } from 'src/core/server';
import { Logger, PluginInitializerContext, CoreSetup, Plugin } from 'src/core/server';
import { TypeOf } from '@kbn/config-schema';
import { SecurityPluginSetup } from '../../security/server';
import { createConfig, ConfigSchema } from './config';
@ -40,7 +39,9 @@ export interface EncryptedSavedObjectsPluginStart {
/**
* Represents EncryptedSavedObjects Plugin instance that will be managed by the Kibana plugin system.
*/
export class Plugin {
export class EncryptedSavedObjectsPlugin
implements
Plugin<EncryptedSavedObjectsPluginSetup, EncryptedSavedObjectsPluginStart, PluginsSetup> {
private readonly logger: Logger;
private savedObjectsSetup!: ClientInstanciator;
@ -48,17 +49,11 @@ export class Plugin {
this.logger = this.initializerContext.logger.get();
}
public async setup(
core: CoreSetup,
deps: PluginsSetup
): Promise<EncryptedSavedObjectsPluginSetup> {
const config = await this.initializerContext.config
.create<TypeOf<typeof ConfigSchema>>()
.pipe(
map((rawConfig) => createConfig(rawConfig, this.initializerContext.logger.get('config')))
)
.pipe(first())
.toPromise();
public setup(core: CoreSetup, deps: PluginsSetup): EncryptedSavedObjectsPluginSetup {
const config = createConfig(
this.initializerContext.config.get<TypeOf<typeof ConfigSchema>>(),
this.initializerContext.logger.get('config')
);
const auditLogger = new EncryptedSavedObjectsAuditLogger(
deps.security?.audit.getLogger('encryptedSavedObjects')
);

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import {
Plugin,
PluginInitializerContext,
@ -66,19 +64,19 @@ export interface RouteDependencies {
}
export class EnterpriseSearchPlugin implements Plugin {
private config: Observable<ConfigType>;
private logger: Logger;
private readonly config: ConfigType;
private readonly logger: Logger;
constructor(initializerContext: PluginInitializerContext) {
this.config = initializerContext.config.create<ConfigType>();
this.config = initializerContext.config.get<ConfigType>();
this.logger = initializerContext.logger.get();
}
public async setup(
public setup(
{ capabilities, http, savedObjects, getStartServices }: CoreSetup<PluginsStart>,
{ usageCollection, security, features }: PluginsSetup
) {
const config = await this.config.pipe(first()).toPromise();
const config = this.config;
const log = this.logger;
/**

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import {
CoreSetup,
CoreStart,
@ -24,7 +22,6 @@ import type {
IEventLogConfig,
IEventLogService,
IEventLogger,
IEventLogConfig$,
IEventLogClientService,
} from './types';
import { findRoute } from './routes';
@ -48,32 +45,29 @@ interface PluginStartDeps {
}
export class Plugin implements CorePlugin<IEventLogService, IEventLogClientService> {
private readonly config$: IEventLogConfig$;
private readonly config: IEventLogConfig;
private systemLogger: Logger;
private eventLogService?: EventLogService;
private esContext?: EsContext;
private eventLogger?: IEventLogger;
private globalConfig$: Observable<SharedGlobalConfig>;
private globalConfig: SharedGlobalConfig;
private eventLogClientService?: EventLogClientService;
private savedObjectProviderRegistry: SavedObjectProviderRegistry;
private kibanaVersion: PluginInitializerContext['env']['packageInfo']['version'];
constructor(private readonly context: PluginInitializerContext) {
this.systemLogger = this.context.logger.get();
this.config$ = this.context.config.create<IEventLogConfig>();
this.globalConfig$ = this.context.config.legacy.globalConfig$;
this.config = this.context.config.get<IEventLogConfig>();
this.globalConfig = this.context.config.legacy.get();
this.savedObjectProviderRegistry = new SavedObjectProviderRegistry();
this.kibanaVersion = this.context.env.packageInfo.version;
}
async setup(core: CoreSetup): Promise<IEventLogService> {
const globalConfig = await this.globalConfig$.pipe(first()).toPromise();
const kibanaIndex = globalConfig.kibana.index;
setup(core: CoreSetup): IEventLogService {
const kibanaIndex = this.globalConfig.kibana.index;
this.systemLogger.debug('setting up plugin');
const config = await this.config$.pipe(first()).toPromise();
this.esContext = createEsContext({
logger: this.systemLogger,
// TODO: get index prefix from config.get(kibana.index)
@ -85,7 +79,7 @@ export class Plugin implements CorePlugin<IEventLogService, IEventLogClientServi
});
this.eventLogService = new EventLogService({
config,
config: this.config,
esContext: this.esContext,
systemLogger: this.systemLogger,
kibanaUUID: this.context.env.instanceUuid,
@ -112,7 +106,7 @@ export class Plugin implements CorePlugin<IEventLogService, IEventLogClientServi
return this.eventLogService;
}
async start(core: CoreStart, { spaces }: PluginStartDeps): Promise<IEventLogClientService> {
start(core: CoreStart, { spaces }: PluginStartDeps): IEventLogClientService {
this.systemLogger.debug('starting plugin');
if (!this.esContext) throw new Error('esContext not initialized');

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import { Observable } from 'rxjs';
import { schema, TypeOf } from '@kbn/config-schema';
import type { IRouter, KibanaRequest, RequestHandlerContext } from 'src/core/server';
@ -25,7 +24,6 @@ export const ConfigSchema = schema.object({
});
export type IEventLogConfig = TypeOf<typeof ConfigSchema>;
export type IEventLogConfig$ = Observable<Readonly<IEventLogConfig>>;
// the object exposed by plugin.setup()
export interface IEventLogService {

View file

@ -6,7 +6,7 @@
*/
import { PluginInitializerContext } from '../../../../src/core/server';
import { Plugin } from './plugin';
import { FeaturesPlugin } from './plugin';
// These exports are part of public Features plugin contract, any change in signature of exported
// functions or removal of exports should be considered as a breaking change. Ideally we should
@ -25,4 +25,4 @@ export {
export { PluginSetupContract, PluginStartContract } from './plugin';
export const plugin = (initializerContext: PluginInitializerContext) =>
new Plugin(initializerContext);
new FeaturesPlugin(initializerContext);

View file

@ -6,7 +6,7 @@
*/
import { coreMock, savedObjectsServiceMock } from 'src/core/server/mocks';
import { Plugin } from './plugin';
import { FeaturesPlugin } from './plugin';
describe('Features Plugin', () => {
let initContext: ReturnType<typeof coreMock.createPluginInitializerContext>;
@ -31,7 +31,7 @@ describe('Features Plugin', () => {
});
it('returns OSS + registered kibana features', async () => {
const plugin = new Plugin(initContext);
const plugin = new FeaturesPlugin(initContext);
const { registerKibanaFeature } = await plugin.setup(coreSetup, {});
registerKibanaFeature({
id: 'baz',
@ -58,7 +58,7 @@ describe('Features Plugin', () => {
});
it('returns OSS + registered kibana features with timelion when available', async () => {
const plugin = new Plugin(initContext);
const plugin = new FeaturesPlugin(initContext);
const { registerKibanaFeature: registerFeature } = await plugin.setup(coreSetup, {
visTypeTimelion: { uiEnabled: true },
});
@ -88,7 +88,7 @@ describe('Features Plugin', () => {
});
it('registers kibana features with not hidden saved objects types', async () => {
const plugin = new Plugin(initContext);
const plugin = new FeaturesPlugin(initContext);
await plugin.setup(coreSetup, {});
const { getKibanaFeatures } = plugin.start(coreStart);
@ -101,7 +101,7 @@ describe('Features Plugin', () => {
});
it('returns registered elasticsearch features', async () => {
const plugin = new Plugin(initContext);
const plugin = new FeaturesPlugin(initContext);
const { registerElasticsearchFeature } = await plugin.setup(coreSetup, {});
registerElasticsearchFeature({
id: 'baz',
@ -123,7 +123,7 @@ describe('Features Plugin', () => {
});
it('registers a capabilities provider', async () => {
const plugin = new Plugin(initContext);
const plugin = new FeaturesPlugin(initContext);
await plugin.setup(coreSetup, {});
expect(coreSetup.capabilities.registerProvider).toHaveBeenCalledTimes(1);

View file

@ -12,6 +12,7 @@ import {
CoreStart,
SavedObjectsServiceStart,
Logger,
Plugin,
PluginInitializerContext,
} from '../../../../src/core/server';
import { Capabilities as UICapabilities } from '../../../../src/core/server';
@ -59,7 +60,9 @@ interface TimelionSetupContract {
/**
* Represents Features Plugin instance that will be managed by the Kibana plugin system.
*/
export class Plugin {
export class FeaturesPlugin
implements
Plugin<RecursiveReadonly<PluginSetupContract>, RecursiveReadonly<PluginStartContract>> {
private readonly logger: Logger;
private readonly featureRegistry: FeatureRegistry = new FeatureRegistry();
private isTimelionEnabled: boolean = false;
@ -68,10 +71,10 @@ export class Plugin {
this.logger = this.initializerContext.logger.get();
}
public async setup(
public setup(
core: CoreSetup,
{ visTypeTimelion }: { visTypeTimelion?: TimelionSetupContract }
): Promise<RecursiveReadonly<PluginSetupContract>> {
): RecursiveReadonly<PluginSetupContract> {
this.isTimelionEnabled = visTypeTimelion !== undefined && visTypeTimelion.uiEnabled;
defineRoutes({

View file

@ -155,7 +155,7 @@ export class FleetPlugin implements Plugin<FleetSetup, FleetStart, FleetSetupDep
return {};
}
public async start(core: CoreStart): Promise<FleetStart> {
public start(core: CoreStart): FleetStart {
let successPromise: ReturnType<FleetStart['isInitialized']>;
return {

View file

@ -12,7 +12,7 @@ import {
CoreStart,
ElasticsearchServiceStart,
Logger,
Plugin,
AsyncPlugin,
PluginInitializerContext,
SavedObjectsServiceStart,
HttpServiceSetup,
@ -169,7 +169,7 @@ export interface FleetStartContract {
}
export class FleetPlugin
implements Plugin<FleetSetupContract, FleetStartContract, FleetSetupDeps, FleetStartDeps> {
implements AsyncPlugin<FleetSetupContract, FleetStartContract, FleetSetupDeps, FleetStartDeps> {
private licensing$!: Observable<ILicense>;
private config$: Observable<FleetConfigType>;
private cloud: CloudSetup | undefined;

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import { Observable } from 'rxjs';
import { take } from 'rxjs/operators';
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/server';
import { LicensingPluginStart } from '../../licensing/server';
import { LicenseChecker, ILicenseChecker } from '../common/license_checker';
@ -33,20 +31,19 @@ export class GlobalSearchPlugin
GlobalSearchPluginSetupDeps,
GlobalSearchPluginStartDeps
> {
private readonly config$: Observable<GlobalSearchConfigType>;
private readonly config: GlobalSearchConfigType;
private readonly searchService = new SearchService();
private searchServiceStart?: SearchServiceStart;
private licenseChecker?: ILicenseChecker;
constructor(context: PluginInitializerContext) {
this.config$ = context.config.create<GlobalSearchConfigType>();
this.config = context.config.get<GlobalSearchConfigType>();
}
public async setup(core: CoreSetup<{}, GlobalSearchPluginStart>) {
const config = await this.config$.pipe(take(1)).toPromise();
public setup(core: CoreSetup<{}, GlobalSearchPluginStart>) {
const { registerResultProvider } = this.searchService.setup({
basePath: core.http.basePath,
config,
config: this.config,
});
registerRoutes(core.http.createRouter());

View file

@ -20,7 +20,7 @@ import { graphWorkspace } from './saved_objects';
export class GraphPlugin implements Plugin {
private licenseState: LicenseState | null = null;
public async setup(
public setup(
core: CoreSetup,
{
licensing,

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import { i18n } from '@kbn/i18n';
import {
CoreSetup,
@ -52,22 +50,19 @@ const indexLifecycleDataEnricher = async (
};
export class IndexLifecycleManagementServerPlugin implements Plugin<void, void, any, any> {
private readonly config$: Observable<IndexLifecycleManagementConfig>;
private readonly config: IndexLifecycleManagementConfig;
private readonly license: License;
private readonly logger: Logger;
constructor(initializerContext: PluginInitializerContext) {
this.logger = initializerContext.logger.get();
this.config$ = initializerContext.config.create();
this.config = initializerContext.config.get();
this.license = new License();
}
async setup(
{ http }: CoreSetup,
{ licensing, indexManagement, features }: Dependencies
): Promise<void> {
setup({ http }: CoreSetup, { licensing, indexManagement, features }: Dependencies): void {
const router = http.createRouter();
const config = await this.config$.pipe(first()).toPromise();
const config = this.config;
this.license.setup(
{

View file

@ -8,8 +8,7 @@
import { Server } from '@hapi/hapi';
import { schema, TypeOf } from '@kbn/config-schema';
import { i18n } from '@kbn/i18n';
import { Observable } from 'rxjs';
import { CoreSetup, PluginInitializerContext } from 'src/core/server';
import { CoreSetup, PluginInitializerContext, Plugin } from 'src/core/server';
import { InfraStaticSourceConfiguration } from '../common/http_api/source_api';
import { inventoryViewSavedObjectType } from '../common/saved_objects/inventory_view';
import { metricsExplorerViewSavedObjectType } from '../common/saved_objects/metrics_explorer_view';
@ -79,22 +78,15 @@ export interface InfraPluginSetup {
) => void;
}
export class InfraServerPlugin {
private config$: Observable<InfraConfig>;
public config = {} as InfraConfig;
export class InfraServerPlugin implements Plugin<InfraPluginSetup> {
public config: InfraConfig;
public libs: InfraBackendLibs | undefined;
constructor(context: PluginInitializerContext) {
this.config$ = context.config.create<InfraConfig>();
this.config = context.config.get<InfraConfig>();
}
async setup(core: CoreSetup<InfraServerPluginStartDeps>, plugins: InfraServerPluginSetupDeps) {
await new Promise<void>((resolve) => {
this.config$.subscribe((configValue) => {
this.config = configValue;
resolve();
});
});
setup(core: CoreSetup<InfraServerPluginStartDeps>, plugins: InfraServerPluginSetupDeps) {
const framework = new KibanaFramework(core, this.config, plugins);
const sources = new InfraSources({
config: this.config,

View file

@ -123,7 +123,7 @@ export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPl
};
}
public async start(core: CoreStart) {
public start(core: CoreStart) {
this.coreStart = core;
if (!this.refresh || !this.license$) {
throw new Error('Setup has not been completed');

View file

@ -6,7 +6,6 @@
*/
import { Observable, Subject, Subscription, timer } from 'rxjs';
import { take } from 'rxjs/operators';
import moment from 'moment';
import { createHash } from 'crypto';
import stringify from 'json-stable-stringify';
@ -83,7 +82,7 @@ function sign({
export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPluginStart, {}, {}> {
private stop$ = new Subject();
private readonly logger: Logger;
private readonly config$: Observable<LicenseConfigType>;
private readonly config: LicenseConfigType;
private loggingSubscription?: Subscription;
private featureUsage = new FeatureUsageService();
@ -92,13 +91,12 @@ export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPl
constructor(private readonly context: PluginInitializerContext) {
this.logger = this.context.logger.get();
this.config$ = this.context.config.create<LicenseConfigType>();
this.config = this.context.config.get<LicenseConfigType>();
}
public async setup(core: CoreSetup<{}, LicensingPluginStart>) {
public setup(core: CoreSetup<{}, LicensingPluginStart>) {
this.logger.debug('Setting up Licensing plugin');
const config = await this.config$.pipe(take(1)).toPromise();
const pollingFrequency = config.api_polling_frequency;
const pollingFrequency = this.config.api_polling_frequency;
async function callAsInternalUser(
...args: Parameters<ILegacyScopedClusterClient['callAsInternalUser']>
@ -225,7 +223,7 @@ export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPl
return error.message;
}
public async start() {
public start() {
if (!this.refresh || !this.license$) {
throw new Error('Setup has not been completed');
}

View file

@ -1,18 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { map } from 'rxjs/operators';
import { PluginInitializerContext } from 'kibana/server';
import { Observable } from 'rxjs';
import { ConfigType } from './config';
export const createConfig$ = (
context: PluginInitializerContext
): Observable<Readonly<ConfigType>> => {
return context.config.create<ConfigType>().pipe(map((config) => config));
};

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import { first } from 'rxjs/operators';
import { Logger, Plugin, PluginInitializerContext } from 'kibana/server';
import type { CoreSetup, CoreStart } from 'src/core/server';
@ -23,7 +22,6 @@ import type {
ListsRequestHandlerContext,
PluginsStart,
} from './types';
import { createConfig$ } from './create_config';
import { getSpaceId } from './get_space_id';
import { getUser } from './get_user';
import { initSavedObjects } from './saved_objects';
@ -32,17 +30,17 @@ import { ExceptionListClient } from './services/exception_lists/exception_list_c
export class ListPlugin
implements Plugin<Promise<ListPluginSetup>, ListsPluginStart, {}, PluginsStart> {
private readonly logger: Logger;
private readonly config: ConfigType;
private spaces: SpacesServiceStart | undefined | null;
private config: ConfigType | undefined | null;
private security: SecurityPluginStart | undefined | null;
constructor(private readonly initializerContext: PluginInitializerContext) {
this.logger = this.initializerContext.logger.get();
this.config = this.initializerContext.config.get<ConfigType>();
}
public async setup(core: CoreSetup): Promise<ListPluginSetup> {
const config = await createConfig$(this.initializerContext).pipe(first()).toPromise();
this.config = config;
const { config } = this;
initSavedObjects(core.savedObjects);

View file

@ -7,7 +7,6 @@
import { i18n } from '@kbn/i18n';
import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'src/core/server';
import { take } from 'rxjs/operators';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server';
import { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server';
// @ts-ignore
@ -134,12 +133,11 @@ export class MapsPlugin implements Plugin {
}
// @ts-ignore
async setup(core: CoreSetup, plugins: SetupDeps) {
setup(core: CoreSetup, plugins: SetupDeps) {
const { usageCollection, home, licensing, features, mapsLegacy } = plugins;
// @ts-ignore
const mapsLegacyConfig = mapsLegacy.config;
const config$ = this._initializerContext.config.create();
const mapsLegacyConfig = await mapsLegacy.config$.pipe(take(1)).toPromise();
const currentConfig = await config$.pipe(take(1)).toPromise();
const currentConfig = this._initializerContext.config.get();
// @ts-ignore
const mapsEnabled = currentConfig.enabled;

View file

@ -49,7 +49,7 @@ export class MonitoringPlugin
Plugin<boolean, void, MonitoringSetupPluginDependencies, MonitoringStartPluginDependencies> {
constructor(private initializerContext: PluginInitializerContext<MonitoringConfig>) {}
public async setup(
public setup(
core: CoreSetup<MonitoringStartPluginDependencies>,
plugins: MonitoringSetupPluginDependencies
) {

View file

@ -7,13 +7,15 @@
import { TypeOf } from '@kbn/config-schema';
import { PluginInitializerContext, PluginConfigDescriptor } from '../../../../src/core/server';
import { Plugin } from './plugin';
import { MonitoringPlugin } from './plugin';
import { configSchema } from './config';
import { deprecations } from './deprecations';
export { KibanaSettingsCollector } from './kibana_monitoring/collectors';
export { MonitoringConfig } from './config';
export const plugin = (initContext: PluginInitializerContext) => new Plugin(initContext);
export { MonitoringPluginSetup, IBulkUploader } from './types';
export const plugin = (initContext: PluginInitializerContext) => new MonitoringPlugin(initContext);
export const config: PluginConfigDescriptor<TypeOf<typeof configSchema>> = {
schema: configSchema,
deprecations,

View file

@ -6,16 +6,9 @@
*/
import { coreMock } from 'src/core/server/mocks';
import { Plugin } from './plugin';
import { combineLatest } from 'rxjs';
import { MonitoringPlugin } from './plugin';
import { AlertsFactory } from './alerts';
jest.mock('rxjs', () => ({
// @ts-ignore
...jest.requireActual('rxjs'),
combineLatest: jest.fn(),
}));
jest.mock('./es_client/instantiate_client', () => ({
instantiateClient: jest.fn().mockImplementation(() => ({
cluster: {},
@ -32,30 +25,11 @@ jest.mock('./kibana_monitoring/collectors', () => ({
registerCollectors: jest.fn(),
}));
describe('Monitoring plugin', () => {
const initializerContext = {
logger: {
get: jest.fn().mockImplementation(() => ({
info: jest.fn(),
})),
},
config: {
create: jest.fn().mockImplementation(() => ({
pipe: jest.fn().mockImplementation(() => ({
toPromise: jest.fn(),
})),
})),
legacy: {
globalConfig$: {},
},
},
env: {
packageInfo: {
version: '1.0.0',
},
},
};
jest.mock('./config', () => ({
createConfig: (config: any) => config,
}));
describe('Monitoring plugin', () => {
const coreSetup = coreMock.createSetup();
coreSetup.http.getServerInfo.mockReturnValue({ port: 5601 } as any);
coreSetup.status.overall$.subscribe = jest.fn();
@ -71,7 +45,6 @@ describe('Monitoring plugin', () => {
},
};
let config = {};
const defaultConfig = {
ui: {
elasticsearch: {},
@ -83,20 +56,7 @@ describe('Monitoring plugin', () => {
},
};
beforeEach(() => {
config = defaultConfig;
(combineLatest as jest.Mock).mockImplementation(() => {
return {
pipe: jest.fn().mockImplementation(() => {
return {
toPromise: jest.fn().mockImplementation(() => {
return [config, 2];
}),
};
}),
};
});
});
const initializerContext = coreMock.createPluginInitializerContext(defaultConfig);
afterEach(() => {
(setupPlugins.alerts.registerType as jest.Mock).mockReset();
@ -104,14 +64,14 @@ describe('Monitoring plugin', () => {
});
it('always create the bulk uploader', async () => {
const plugin = new Plugin(initializerContext as any);
const plugin = new MonitoringPlugin(initializerContext as any);
await plugin.setup(coreSetup, setupPlugins as any);
expect(coreSetup.status.overall$.subscribe).toHaveBeenCalled();
});
it('should register all alerts', async () => {
const alerts = AlertsFactory.getAll();
const plugin = new Plugin(initializerContext as any);
const plugin = new MonitoringPlugin(initializerContext as any);
await plugin.setup(coreSetup as any, setupPlugins as any);
expect(setupPlugins.alerts.registerType).toHaveBeenCalledTimes(alerts.length);
});

View file

@ -6,8 +6,6 @@
*/
import Boom from '@hapi/boom';
import { combineLatest } from 'rxjs';
import { first, map } from 'rxjs/operators';
import { i18n } from '@kbn/i18n';
import { has, get } from 'lodash';
import { TypeOf } from '@kbn/config-schema';
@ -21,6 +19,7 @@ import {
CoreStart,
CustomHttpResponseOptions,
ResponseError,
Plugin,
} from 'kibana/server';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server';
import {
@ -43,6 +42,7 @@ import { AlertsFactory } from './alerts';
import {
MonitoringCore,
MonitoringLicenseService,
MonitoringPluginSetup,
LegacyShimDependencies,
IBulkUploader,
PluginsSetup,
@ -67,7 +67,8 @@ const wrapError = (error: any): CustomHttpResponseOptions<ResponseError> => {
};
};
export class Plugin {
export class MonitoringPlugin
implements Plugin<MonitoringPluginSetup, void, PluginsSetup, PluginsStart> {
private readonly initializerContext: PluginInitializerContext;
private readonly log: Logger;
private readonly getLogger: (...scopes: string[]) => Logger;
@ -83,15 +84,9 @@ export class Plugin {
this.getLogger = (...scopes: string[]) => initializerContext.logger.get(LOGGING_TAG, ...scopes);
}
async setup(core: CoreSetup, plugins: PluginsSetup) {
const [config, legacyConfig] = await combineLatest([
this.initializerContext.config
.create<TypeOf<typeof configSchema>>()
.pipe(map((rawConfig) => createConfig(rawConfig))),
this.initializerContext.config.legacy.globalConfig$,
])
.pipe(first())
.toPromise();
setup(core: CoreSetup, plugins: PluginsSetup) {
const config = createConfig(this.initializerContext.config.get<TypeOf<typeof configSchema>>());
const legacyConfig = this.initializerContext.config.legacy.get();
CoreServices.init(core);

View file

@ -94,6 +94,10 @@ export interface IBulkUploader {
stop: () => void;
}
export interface MonitoringPluginSetup {
getKibanaStats: IBulkUploader['getKibanaStats'];
}
export interface LegacyRequest {
logger: Logger;
getLogger: (...scopes: string[]) => Logger;

View file

@ -6,7 +6,6 @@
*/
import { PluginInitializerContext, Plugin, CoreSetup } from 'src/core/server';
import { take } from 'rxjs/operators';
import { ObservabilityConfig } from '.';
import {
bootstrapAnnotations,
@ -28,10 +27,8 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
this.initContext = initContext;
}
public async setup(core: CoreSetup, plugins: {}): Promise<ObservabilityPluginSetup> {
const config$ = this.initContext.config.create<ObservabilityConfig>();
const config = await config$.pipe(take(1)).toPromise();
public setup(core: CoreSetup, plugins: {}): ObservabilityPluginSetup {
const config = this.initContext.config.get<ObservabilityConfig>();
let annotationsApiPromise: Promise<AnnotationsAPI> | undefined;

View file

@ -5,14 +5,10 @@
* 2.0.
*/
import { map } from 'rxjs/operators';
import { PluginInitializerContext } from 'kibana/server';
import { Observable } from 'rxjs';
import { ConfigType } from './config';
export const createConfig$ = (
context: PluginInitializerContext
): Observable<Readonly<ConfigType>> => {
return context.config.create<ConfigType>().pipe(map((config) => config));
export const createConfig = (context: PluginInitializerContext): Readonly<ConfigType> => {
return context.config.get<ConfigType>();
};

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import { first } from 'rxjs/operators';
import {
PluginInitializerContext,
CoreSetup,
@ -14,7 +13,7 @@ import {
Logger,
} from '../../../../src/core/server';
import { createConfig$ } from './create_config';
import { createConfig } from './create_config';
import { OsqueryPluginSetup, OsqueryPluginStart, SetupPlugins, StartPlugins } from './types';
import { defineRoutes } from './routes';
import { osquerySearchStrategyProvider } from './search_strategy/osquery';
@ -26,9 +25,9 @@ export class OsqueryPlugin implements Plugin<OsqueryPluginSetup, OsqueryPluginSt
this.logger = this.initializerContext.logger.get();
}
public async setup(core: CoreSetup<StartPlugins, OsqueryPluginStart>, plugins: SetupPlugins) {
public setup(core: CoreSetup<StartPlugins, OsqueryPluginStart>, plugins: SetupPlugins) {
this.logger.debug('osquery: Setup');
const config = await createConfig$(this.initializerContext).pipe(first()).toPromise();
const config = createConfig(this.initializerContext);
if (!config.enabled) {
return {};

View file

@ -22,7 +22,7 @@ export class PainlessLabServerPlugin implements Plugin {
this.license = new License();
}
async setup({ http }: CoreSetup, { licensing }: Dependencies) {
setup({ http }: CoreSetup, { licensing }: Dependencies) {
const router = http.createRouter();
this.license.setup(

View file

@ -8,8 +8,6 @@
import { i18n } from '@kbn/i18n';
import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'src/core/server';
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import { PLUGIN } from '../common/constants';
import { Dependencies, LicenseStatus, RouteDependencies } from './types';
@ -29,17 +27,16 @@ export class RemoteClustersServerPlugin
implements Plugin<RemoteClustersPluginSetup, void, any, any> {
licenseStatus: LicenseStatus;
log: Logger;
config$: Observable<ConfigType>;
config: ConfigType;
constructor({ logger, config }: PluginInitializerContext) {
this.log = logger.get();
this.config$ = config.create();
this.config = config.get();
this.licenseStatus = { valid: false };
}
async setup({ http }: CoreSetup, { features, licensing, cloud }: Dependencies) {
setup({ http }: CoreSetup, { features, licensing, cloud }: Dependencies) {
const router = http.createRouter();
const config = await this.config$.pipe(first()).toPromise();
const routeDependencies: RouteDependencies = {
router,
@ -89,7 +86,7 @@ export class RemoteClustersServerPlugin
});
return {
isUiEnabled: config.ui.enabled,
isUiEnabled: this.config.ui.enabled,
};
}

View file

@ -21,7 +21,7 @@ export class SearchProfilerServerPlugin implements Plugin {
this.licenseStatus = { valid: false };
}
async setup({ http }: CoreSetup, { licensing }: AppServerPluginDependencies) {
setup({ http }: CoreSetup, { licensing }: AppServerPluginDependencies) {
const router = http.createRouter();
profileRoute.register({
router,

View file

@ -15,7 +15,7 @@ import type {
import { ConfigSchema } from './config';
import { securityConfigDeprecationProvider } from './config_deprecations';
import {
Plugin,
SecurityPlugin,
SecurityPluginSetup,
SecurityPluginStart,
PluginSetupDependencies,
@ -51,4 +51,4 @@ export const plugin: PluginInitializer<
RecursiveReadonly<SecurityPluginSetup>,
RecursiveReadonly<SecurityPluginStart>,
PluginSetupDependencies
> = (initializerContext: PluginInitializerContext) => new Plugin(initializerContext);
> = (initializerContext: PluginInitializerContext) => new SecurityPlugin(initializerContext);

Some files were not shown because too many files have changed in this diff Show more