Skip to content

Commit 2677b3b

Browse files
Merge branch 'master' into newplatform/data/search-msearch
2 parents 7ac79fd + 2d10350 commit 2677b3b

File tree

18 files changed

+760
-459
lines changed

18 files changed

+760
-459
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CoreSetup](./kibana-plugin-server.coresetup.md) &gt; [getStartServices](./kibana-plugin-server.coresetup.getstartservices.md)
4+
5+
## CoreSetup.getStartServices() method
6+
7+
Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed `start`<!-- -->. This should only be used inside handlers registered during `setup` that will only be executed after `start` lifecycle.
8+
9+
<b>Signature:</b>
10+
11+
```typescript
12+
getStartServices(): Promise<[CoreStart, TPluginsStart]>;
13+
```
14+
<b>Returns:</b>
15+
16+
`Promise<[CoreStart, TPluginsStart]>`
17+
Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,32 @@
1-
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2-
3-
[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CoreSetup](./kibana-plugin-server.coresetup.md)
4-
5-
## CoreSetup interface
6-
7-
Context passed to the plugins `setup` method.
8-
9-
<b>Signature:</b>
10-
11-
```typescript
12-
export interface CoreSetup
13-
```
14-
15-
## Properties
16-
17-
| Property | Type | Description |
18-
| --- | --- | --- |
19-
| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | <code>CapabilitiesSetup</code> | [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) |
20-
| [context](./kibana-plugin-server.coresetup.context.md) | <code>ContextSetup</code> | [ContextSetup](./kibana-plugin-server.contextsetup.md) |
21-
| [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | <code>ElasticsearchServiceSetup</code> | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) |
22-
| [http](./kibana-plugin-server.coresetup.http.md) | <code>HttpServiceSetup</code> | [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) |
23-
| [savedObjects](./kibana-plugin-server.coresetup.savedobjects.md) | <code>SavedObjectsServiceSetup</code> | [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) |
24-
| [uiSettings](./kibana-plugin-server.coresetup.uisettings.md) | <code>UiSettingsServiceSetup</code> | [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) |
25-
| [uuid](./kibana-plugin-server.coresetup.uuid.md) | <code>UuidServiceSetup</code> | [UuidServiceSetup](./kibana-plugin-server.uuidservicesetup.md) |
26-
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CoreSetup](./kibana-plugin-server.coresetup.md)
4+
5+
## CoreSetup interface
6+
7+
Context passed to the plugins `setup` method.
8+
9+
<b>Signature:</b>
10+
11+
```typescript
12+
export interface CoreSetup<TPluginsStart extends object = object>
13+
```
14+
15+
## Properties
16+
17+
| Property | Type | Description |
18+
| --- | --- | --- |
19+
| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | <code>CapabilitiesSetup</code> | [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) |
20+
| [context](./kibana-plugin-server.coresetup.context.md) | <code>ContextSetup</code> | [ContextSetup](./kibana-plugin-server.contextsetup.md) |
21+
| [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | <code>ElasticsearchServiceSetup</code> | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) |
22+
| [http](./kibana-plugin-server.coresetup.http.md) | <code>HttpServiceSetup</code> | [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) |
23+
| [savedObjects](./kibana-plugin-server.coresetup.savedobjects.md) | <code>SavedObjectsServiceSetup</code> | [SavedObjectsServiceSetup](./kibana-plugin-server.savedobjectsservicesetup.md) |
24+
| [uiSettings](./kibana-plugin-server.coresetup.uisettings.md) | <code>UiSettingsServiceSetup</code> | [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) |
25+
| [uuid](./kibana-plugin-server.coresetup.uuid.md) | <code>UuidServiceSetup</code> | [UuidServiceSetup](./kibana-plugin-server.uuidservicesetup.md) |
26+
27+
## Methods
28+
29+
| Method | Description |
30+
| --- | --- |
31+
| [getStartServices()](./kibana-plugin-server.coresetup.getstartservices.md) | Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed <code>start</code>. This should only be used inside handlers registered during <code>setup</code> that will only be executed after <code>start</code> lifecycle. |
32+

src/core/server/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ export interface RequestHandlerContext {
283283
*
284284
* @public
285285
*/
286-
export interface CoreSetup {
286+
export interface CoreSetup<TPluginsStart extends object = object> {
287287
/** {@link CapabilitiesSetup} */
288288
capabilities: CapabilitiesSetup;
289289
/** {@link ContextSetup} */
@@ -298,6 +298,13 @@ export interface CoreSetup {
298298
uiSettings: UiSettingsServiceSetup;
299299
/** {@link UuidServiceSetup} */
300300
uuid: UuidServiceSetup;
301+
/**
302+
* Allows plugins to get access to APIs available in start inside async handlers.
303+
* Promise will not resolve until Core and plugin dependencies have completed `start`.
304+
* This should only be used inside handlers registered during `setup` that will only be executed
305+
* after `start` lifecycle.
306+
*/
307+
getStartServices(): Promise<[CoreStart, TPluginsStart]>;
301308
}
302309

303310
/**

src/core/server/legacy/legacy_service.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,12 @@ export class LegacyService implements CoreService {
256256
startDeps: LegacyServiceStartDeps,
257257
legacyPlugins: LegacyPlugins
258258
) {
259+
const coreStart: CoreStart = {
260+
capabilities: startDeps.core.capabilities,
261+
savedObjects: { getScopedClient: startDeps.core.savedObjects.getScopedClient },
262+
uiSettings: { asScopedToClient: startDeps.core.uiSettings.asScopedToClient },
263+
};
264+
259265
const coreSetup: CoreSetup = {
260266
capabilities: setupDeps.core.capabilities,
261267
context: setupDeps.core.context,
@@ -291,11 +297,7 @@ export class LegacyService implements CoreService {
291297
uuid: {
292298
getInstanceUuid: setupDeps.core.uuid.getInstanceUuid,
293299
},
294-
};
295-
const coreStart: CoreStart = {
296-
capabilities: startDeps.core.capabilities,
297-
savedObjects: { getScopedClient: startDeps.core.savedObjects.getScopedClient },
298-
uiSettings: { asScopedToClient: startDeps.core.uiSettings.asScopedToClient },
300+
getStartServices: () => Promise.resolve([coreStart, startDeps.plugins]),
299301
};
300302

301303
// eslint-disable-next-line @typescript-eslint/no-var-requires

src/core/server/mocks.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ function pluginInitializerContextMock<T>(config: T = {} as T) {
8686
return mock;
8787
}
8888

89+
type CoreSetupMockType = MockedKeys<CoreSetup> & jest.Mocked<Pick<CoreSetup, 'getStartServices'>>;
90+
8991
function createCoreSetupMock() {
9092
const httpService = httpServiceMock.createSetupContract();
9193
const httpMock: jest.Mocked<CoreSetup['http']> = {
@@ -105,14 +107,17 @@ function createCoreSetupMock() {
105107
const uiSettingsMock = {
106108
register: uiSettingsServiceMock.createSetupContract().register,
107109
};
108-
const mock: MockedKeys<CoreSetup> = {
110+
const mock: CoreSetupMockType = {
109111
capabilities: capabilitiesServiceMock.createSetupContract(),
110112
context: contextServiceMock.createSetupContract(),
111113
elasticsearch: elasticsearchServiceMock.createSetup(),
112114
http: httpMock,
113115
savedObjects: savedObjectsServiceMock.createSetupContract(),
114116
uiSettings: uiSettingsMock,
115117
uuid: uuidServiceMock.createSetupContract(),
118+
getStartServices: jest
119+
.fn<Promise<[ReturnType<typeof createCoreStartMock>, object]>, []>()
120+
.mockResolvedValue([createCoreStartMock(), {}]),
116121
};
117122

118123
return mock;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
export const mockPackage = new Proxy(
21+
{ raw: { __dirname: '/tmp' } as any },
22+
{ get: (obj, prop) => obj.raw[prop] }
23+
);
24+
jest.mock('../../../../core/server/utils/package_json', () => ({ pkg: mockPackage }));
25+
26+
export const mockDiscover = jest.fn();
27+
jest.mock('../discovery/plugins_discovery', () => ({ discover: mockDiscover }));
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { mockPackage, mockDiscover } from './plugins_service.test.mocks';
21+
22+
import { join } from 'path';
23+
24+
import { PluginsService } from '../plugins_service';
25+
import { ConfigPath, ConfigService, Env } from '../../config';
26+
import { getEnvOptions } from '../../config/__mocks__/env';
27+
import { BehaviorSubject, from } from 'rxjs';
28+
import { rawConfigServiceMock } from '../../config/raw_config_service.mock';
29+
import { config } from '../plugins_config';
30+
import { loggingServiceMock } from '../../logging/logging_service.mock';
31+
import { coreMock } from '../../mocks';
32+
import { Plugin } from '../types';
33+
import { PluginWrapper } from '../plugin';
34+
35+
describe('PluginsService', () => {
36+
const logger = loggingServiceMock.create();
37+
let pluginsService: PluginsService;
38+
39+
const createPlugin = (
40+
id: string,
41+
{
42+
path = id,
43+
disabled = false,
44+
version = 'some-version',
45+
requiredPlugins = [],
46+
optionalPlugins = [],
47+
kibanaVersion = '7.0.0',
48+
configPath = [path],
49+
server = true,
50+
ui = true,
51+
}: {
52+
path?: string;
53+
disabled?: boolean;
54+
version?: string;
55+
requiredPlugins?: string[];
56+
optionalPlugins?: string[];
57+
kibanaVersion?: string;
58+
configPath?: ConfigPath;
59+
server?: boolean;
60+
ui?: boolean;
61+
}
62+
): PluginWrapper => {
63+
return new PluginWrapper({
64+
path,
65+
manifest: {
66+
id,
67+
version,
68+
configPath: `${configPath}${disabled ? '-disabled' : ''}`,
69+
kibanaVersion,
70+
requiredPlugins,
71+
optionalPlugins,
72+
server,
73+
ui,
74+
},
75+
opaqueId: Symbol(id),
76+
initializerContext: { logger } as any,
77+
});
78+
};
79+
80+
beforeEach(async () => {
81+
mockPackage.raw = {
82+
branch: 'feature-v1',
83+
version: 'v1',
84+
build: {
85+
distributable: true,
86+
number: 100,
87+
sha: 'feature-v1-build-sha',
88+
},
89+
};
90+
91+
const env = Env.createDefault(getEnvOptions());
92+
const config$ = new BehaviorSubject<Record<string, any>>({
93+
plugins: {
94+
initialize: true,
95+
},
96+
});
97+
const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ });
98+
const configService = new ConfigService(rawConfigService, env, logger);
99+
await configService.setSchema(config.path, config.schema);
100+
101+
pluginsService = new PluginsService({
102+
coreId: Symbol('core'),
103+
env,
104+
logger,
105+
configService,
106+
});
107+
});
108+
109+
it("properly resolves `getStartServices` in plugin's lifecycle", async () => {
110+
expect.assertions(5);
111+
112+
const pluginPath = 'plugin-path';
113+
114+
mockDiscover.mockReturnValue({
115+
error$: from([]),
116+
plugin$: from([
117+
createPlugin('plugin-id', {
118+
path: pluginPath,
119+
configPath: 'path',
120+
}),
121+
]),
122+
});
123+
124+
let startDependenciesResolved = false;
125+
let contextFromStart: any = null;
126+
let contextFromStartService: any = null;
127+
128+
const pluginInitializer = () =>
129+
({
130+
setup: async (coreSetup, deps) => {
131+
coreSetup.getStartServices().then(([core, plugins]) => {
132+
startDependenciesResolved = true;
133+
contextFromStartService = { core, plugins };
134+
});
135+
},
136+
start: async (core, plugins) => {
137+
contextFromStart = { core, plugins };
138+
await new Promise(resolve => setTimeout(resolve, 10));
139+
expect(startDependenciesResolved).toBe(false);
140+
},
141+
} as Plugin);
142+
143+
jest.doMock(
144+
join(pluginPath, 'server'),
145+
() => ({
146+
plugin: pluginInitializer,
147+
}),
148+
{
149+
virtual: true,
150+
}
151+
);
152+
153+
await pluginsService.discover();
154+
155+
const setupDeps = coreMock.createInternalSetup();
156+
await pluginsService.setup(setupDeps);
157+
158+
expect(startDependenciesResolved).toBe(false);
159+
160+
const startDeps = coreMock.createInternalStart();
161+
await pluginsService.start(startDeps);
162+
163+
expect(startDependenciesResolved).toBe(true);
164+
expect(contextFromStart!.core).toEqual(contextFromStartService!.core);
165+
expect(contextFromStart!.plugins).toEqual(contextFromStartService!.plugins);
166+
});
167+
});

src/core/server/plugins/plugin.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,43 @@ test('`start` calls plugin.start with context and dependencies', async () => {
237237
expect(mockPluginInstance.start).toHaveBeenCalledWith(context, deps);
238238
});
239239

240+
test("`start` resolves `startDependencies` Promise after plugin's start", async () => {
241+
expect.assertions(2);
242+
243+
const manifest = createPluginManifest();
244+
const opaqueId = Symbol();
245+
const plugin = new PluginWrapper({
246+
path: 'plugin-with-initializer-path',
247+
manifest,
248+
opaqueId,
249+
initializerContext: createPluginInitializerContext(coreContext, opaqueId, manifest),
250+
});
251+
const startContext = { any: 'thing' } as any;
252+
const pluginDeps = { someDep: 'value' };
253+
254+
let startDependenciesResolved = false;
255+
256+
const mockPluginInstance = {
257+
setup: jest.fn(),
258+
start: async () => {
259+
// delay to ensure startDependencies is not resolved until after the plugin instance's start resolves.
260+
await new Promise(resolve => setTimeout(resolve, 10));
261+
expect(startDependenciesResolved).toBe(false);
262+
},
263+
};
264+
mockPluginInitializer.mockReturnValue(mockPluginInstance);
265+
266+
await plugin.setup({} as any, {} as any);
267+
268+
const startDependenciesCheck = plugin.startDependencies.then(resolvedStartDeps => {
269+
startDependenciesResolved = true;
270+
expect(resolvedStartDeps).toEqual([startContext, pluginDeps]);
271+
});
272+
273+
await plugin.start(startContext, pluginDeps);
274+
await startDependenciesCheck;
275+
});
276+
240277
test('`stop` fails if plugin is not set up', async () => {
241278
const manifest = createPluginManifest();
242279
const opaqueId = Symbol();

0 commit comments

Comments
 (0)