Merge remote-tracking branch 'origin/master' into feature/merge-code

This commit is contained in:
Fuyao Zhao 2019-03-21 12:08:17 -07:00
commit 046c922d2c
377 changed files with 7730 additions and 4890 deletions

View file

@ -155,6 +155,7 @@ module.exports = {
'x-pack/{dev-tools,tasks,scripts,test,build_chromium}/**/*',
'x-pack/**/{__tests__,__test__,__jest__,__fixtures__,__mocks__}/**/*',
'x-pack/**/*.test.js',
'x-pack/test_utils/**/*',
'x-pack/gulpfile.js',
'x-pack/plugins/apm/public/utils/testHelpers.js',
],

View file

@ -468,6 +468,9 @@ To include your change in the Release Notes:
To NOT include your changes in the Release Notes, please use label`non-issue`. PRs with the following labels also won't be included in the Release Notes:
`build`, `docs`, `test`, `non-issue`, `jenkins`, `backport`, and `chore`.
To NOT include your changes in the Release Notes, please use label`non-issue`. PRs with the following labels also won't be included in the Release Notes:
`build`, `docs`, `test_*`,`test-*`, `non-issue`, `jenkins`, `backport`, and `chore`.
We also produce a blog post that details more important breaking API changes every minor and major release. If the PR includes a breaking API change, apply the label `release_note:dev_docs`. Additionally add a brief summary of the break at the bottom of the PR using the format below:

View file

@ -4,8 +4,6 @@
[partintro]
--
beta[]
Congratulations on finding the Canvas application in {kib}. You are in for a treat.
Canvas is a whole new way of making data look amazing. Canvas combines data with
colors, shapes, text, and your own imagination to bring dynamic, multi-page,

View file

@ -1,7 +1,7 @@
[[canvas-client-functions]]
=== Canvas client functions
beta[]These functions must execute in a browser. They are only available
These functions must execute in a browser. They are only available
from within the Canvas application, not via the Canvas HTTP API. These functions must
execute in the browser because they use browser specific APIs, such as location,
or interact with the workpad to read filters.

View file

@ -1,7 +1,7 @@
[[canvas-common-functions]]
=== Canvas common functions
beta[]The common functions can run anywhere, which means they'll execute wherever
The common functions can run anywhere, which means they'll execute wherever
the expression is currently executing. For example, if the engine is currently
running on the server, the functions will run on the server.

View file

@ -1,7 +1,7 @@
[[canvas-function-reference]]
== Canvas function reference
beta[] Behind the scenes, Canvas is driven by a powerful expression language,
Behind the scenes, Canvas is driven by a powerful expression language,
with dozens of functions and other capabilities, including table transforms,
type casting, and sub-expressions.

View file

@ -1,7 +1,7 @@
[[canvas-getting-started]]
== Getting started with Canvas
beta[]Your best bet to getting started with Canvas is to check out one
Your best bet to getting started with Canvas is to check out one
(or all) of the sample data sets that ship with {kib}.
. Click the {kib} logo in the upper left hand corner of your browser to navigate

View file

@ -1,7 +1,7 @@
[[canvas-server-functions]]
=== Canvas server functions
beta[]These functions can only execute on the server. This may be for performance
These functions can only execute on the server. This may be for performance
or security reasons, or because the function uses an API only available on the
{kib} server. If the expression is executing in the browser, it will transfer to
the server when it hits one of these functions.

View file

@ -2,7 +2,7 @@
[[canvas-tinymath-functions]]
=== TinyMath functions
beta[]TinyMath provides a set of functions that can be used with the Canvas expression
TinyMath provides a set of functions that can be used with the Canvas expression
language to perform complex math calculations. Read on for detailed information about
the functions available in TinyMath, including what parameters each function accepts,
the return value of that function, and examples of how each function behaves.

View file

@ -1,7 +1,7 @@
[[canvas-workpad]]
=== Using the workpad
beta[]Now that you have a workpad with sample data that you can mess with, lets mess with it.
Now that you have a workpad with sample data that you can mess with, lets mess with it.
Well start out by making a few stylistic changes.
. Click the gauge chart in the top left of the workpad (fun fact, these are actually pie charts).

View file

@ -24,17 +24,16 @@ image::images/index-lifecycle-policies-create.png[][UI for creating an index lif
==== Defining the phases of the index lifecycle
You can define up to four phases in the index lifecycle. For each phase, you
can enable actions to optimize performance for that phase. Transitioning
between phases is based on the age of the index.
can enable actions to optimize performance for that phase.
The four phases in the index lifecycle are:
* *Hot.* The index is actively being queried and written to. You can optionally
* *Hot.* The index is actively being queried and written to. You can
roll over to a new index when the
original index reaches a specified size or age. When a rollover occurs, a new
original index reaches a specified size, document count, or age. When a rollover occurs, a new
index is created, added to the index alias, and designated as the new “hot”
index. You can still query the previous indices, but you only ever write to
the “hot” index. See {ref}/indices-rollover-index.html[Rollover index] for more information.
the “hot” index. See <<setting-a-rollover-action>>.
* *Warm.* The index is typically searched at a lower rate than when the data is
hot. The index is not used for storing new data, but might occasionally add
@ -60,6 +59,27 @@ delete phases are optional. For example, you might define all four phases for
one policy and only a hot and delete phase for another. See {ref}/_actions.html[Actions]
for more information on the actions available in each phase.
[[setting-a-rollover-action]]
==== Setting a rollover action
The {ref}/indices-rollover-index.html[rollover] action enables you to automatically roll over to a new index based
on the index size, document count, or age. Rolling over to a new index based on
these criteria is preferable to time-based rollovers. Rolling over at an arbitrary
time often results in many small indices, which can have a negative impact on performance and resource usage.
When you create an index lifecycle policy, the rollover action is enabled
by default. The default size for triggering the rollover is 50 gigabytes, and
the default age is 30 days. The rollover occurs when any of the criteria are met.
With the rollover action enabled, you can move to the warm phase on rollover or you can
time the move for a specified number of hours or days after the rollover. The
move to the cold and delete phases is based on the time from the rollover.
If you are using daily indices (created by Logstash or another client) and you
want to use the index lifecycle policy to manage aging data, you can
disable the rollover action in the hot phase. You can then
transition to the warm, cold, and delete phases based on the time of index creation.
==== Setting the index priority
For the hot, warm, and cold phases, you can set a priority for recovering

View file

@ -135,7 +135,7 @@ suppress all logging output other than error messages.
suppress all logging output.
`logging.timezone`:: *Default: UTC* Set to the canonical timezone id
(e.g. `US/Pacific`) to log events using that timezone. A list of timezones can
(for example, `America/Los_Angeles`) to log events using that timezone. A list of timezones can
be referenced at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
[[logging-verbose]]`logging.verbose:`:: *Default: false* Set the value of this

3
kibana.d.ts vendored
View file

@ -20,7 +20,8 @@
/**
* All exports from TS source files (where the implementation is actually done in TS).
*/
export * from './target/types/type_exports';
export { Public, Server } from 'src/core';
/**
* All exports from TS ambient definitions (where types are added for JS source in a .d.ts file).
*/

View file

@ -67,8 +67,7 @@
"uiFramework:createComponent": "cd packages/kbn-ui-framework && yarn createComponent",
"uiFramework:documentComponent": "cd packages/kbn-ui-framework && yarn documentComponent",
"kbn:watch": "node scripts/kibana --dev --logging.json=false",
"build:types": "tsc --p tsconfig.types.json",
"kbn:bootstrap": "yarn build:types && node scripts/register_git_hook"
"kbn:bootstrap": "node scripts/register_git_hook"
},
"repository": {
"type": "git",
@ -83,7 +82,8 @@
"packages/*",
"x-pack",
"x-pack/plugins/*",
"test/plugin_functional/plugins/*"
"test/plugin_functional/plugins/*",
"test/interpreter_functional/plugins/*"
],
"nohoist": [
"**/@types/*",
@ -96,7 +96,7 @@
},
"dependencies": {
"@elastic/datemath": "5.0.2",
"@elastic/eui": "9.4.0",
"@elastic/eui": "9.4.2",
"@elastic/filesaver": "1.1.2",
"@elastic/good": "8.1.1-kibana2",
"@elastic/numeral": "2.3.2",
@ -391,6 +391,7 @@
"normalize-path": "^3.0.0",
"pixelmatch": "4.0.2",
"pkg-up": "^2.0.0",
"pngjs": "^3.4.0",
"postcss": "^7.0.5",
"postcss-url": "^8.0.0",
"prettier": "^1.14.3",

View file

@ -43,6 +43,7 @@ export function getProjectPaths(rootPath: string, options: IProjectPathOptions)
// In anyway, have a plugin declaring their own dependencies is the
// correct and the expect behavior.
projectPaths.push(resolve(rootPath, 'test/plugin_functional/plugins/*'));
projectPaths.push(resolve(rootPath, 'test/interpreter_functional/plugins/*'));
if (!ossOnly) {
projectPaths.push(resolve(rootPath, 'x-pack'));

View file

@ -1,6 +1,6 @@
- Start Date: 2019-03-05
- RFC PR: (leave this empty)
- Kibana Issue: (leave this empty)
- RFC PR: [#32507](https://github.com/elastic/kibana/pull/32507)
- Kibana Issue: [#33045](https://github.com/elastic/kibana/issues/33045)
# Summary

View file

@ -22,4 +22,5 @@ require('@kbn/test').runTestsCli([
require.resolve('../test/functional/config.js'),
require.resolve('../test/api_integration/config.js'),
require.resolve('../test/plugin_functional/config.js'),
require.resolve('../test/interpreter_functional/config.js'),
]);

23
src/core/index.ts Normal file
View file

@ -0,0 +1,23 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as Public from './public';
import * as Server from './server';
export { Public, Server };

View file

@ -16,30 +16,30 @@
* specific language governing permissions and limitations
* under the License.
*/
import { BasePathService, BasePathStart } from './base_path_service';
import { BasePathService, BasePathSetup } from './base_path_service';
const createStartContractMock = () => {
const startContract: jest.Mocked<BasePathStart> = {
const createSetupContractMock = () => {
const setupContract: jest.Mocked<BasePathSetup> = {
get: jest.fn(),
addToPath: jest.fn(),
removeFromPath: jest.fn(),
};
startContract.get.mockReturnValue('get');
startContract.addToPath.mockReturnValue('addToPath');
startContract.removeFromPath.mockReturnValue('removeFromPath');
return startContract;
setupContract.get.mockReturnValue('get');
setupContract.addToPath.mockReturnValue('addToPath');
setupContract.removeFromPath.mockReturnValue('removeFromPath');
return setupContract;
};
type BasePathServiceContract = PublicMethodsOf<BasePathService>;
const createMock = () => {
const mocked: jest.Mocked<BasePathServiceContract> = {
start: jest.fn(),
setup: jest.fn(),
};
mocked.start.mockReturnValue(createStartContractMock());
mocked.setup.mockReturnValue(createSetupContractMock());
return mocked;
};
export const basePathServiceMock = {
create: createMock,
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -20,85 +20,85 @@
import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock';
import { BasePathService } from './base_path_service';
function setup(options: any = {}) {
function setupService(options: any = {}) {
const injectedBasePath: string =
options.injectedBasePath === undefined ? '/foo/bar' : options.injectedBasePath;
const service = new BasePathService();
const injectedMetadata = injectedMetadataServiceMock.createStartContract();
const injectedMetadata = injectedMetadataServiceMock.createSetupContract();
injectedMetadata.getBasePath.mockReturnValue(injectedBasePath);
const start = service.start({
const setupContract = service.setup({
injectedMetadata,
});
return {
service,
start,
setupContract,
injectedBasePath,
};
}
describe('start.get()', () => {
describe('setup.get()', () => {
it('returns an empty string if no basePath is injected', () => {
const { start } = setup({ injectedBasePath: null });
expect(start.get()).toBe('');
const { setupContract } = setupService({ injectedBasePath: null });
expect(setupContract.get()).toBe('');
});
it('returns the injected basePath', () => {
const { start } = setup();
expect(start.get()).toBe('/foo/bar');
const { setupContract } = setupService();
expect(setupContract.get()).toBe('/foo/bar');
});
});
describe('start.addToPath()', () => {
describe('setup.addToPath()', () => {
it('adds the base path to the path if it is relative and starts with a slash', () => {
const { start } = setup();
expect(start.addToPath('/a/b')).toBe('/foo/bar/a/b');
const { setupContract } = setupService();
expect(setupContract.addToPath('/a/b')).toBe('/foo/bar/a/b');
});
it('leaves the query string and hash of path unchanged', () => {
const { start } = setup();
expect(start.addToPath('/a/b?x=y#c/d/e')).toBe('/foo/bar/a/b?x=y#c/d/e');
const { setupContract } = setupService();
expect(setupContract.addToPath('/a/b?x=y#c/d/e')).toBe('/foo/bar/a/b?x=y#c/d/e');
});
it('returns the path unchanged if it does not start with a slash', () => {
const { start } = setup();
expect(start.addToPath('a/b')).toBe('a/b');
const { setupContract } = setupService();
expect(setupContract.addToPath('a/b')).toBe('a/b');
});
it('returns the path unchanged it it has a hostname', () => {
const { start } = setup();
expect(start.addToPath('http://localhost:5601/a/b')).toBe('http://localhost:5601/a/b');
const { setupContract } = setupService();
expect(setupContract.addToPath('http://localhost:5601/a/b')).toBe('http://localhost:5601/a/b');
});
});
describe('start.removeFromPath()', () => {
describe('setup.removeFromPath()', () => {
it('removes the basePath if relative path starts with it', () => {
const { start } = setup();
expect(start.removeFromPath('/foo/bar/a/b')).toBe('/a/b');
const { setupContract } = setupService();
expect(setupContract.removeFromPath('/foo/bar/a/b')).toBe('/a/b');
});
it('leaves query string and hash intact', () => {
const { start } = setup();
expect(start.removeFromPath('/foo/bar/a/b?c=y#1234')).toBe('/a/b?c=y#1234');
const { setupContract } = setupService();
expect(setupContract.removeFromPath('/foo/bar/a/b?c=y#1234')).toBe('/a/b?c=y#1234');
});
it('ignores urls with hostnames', () => {
const { start } = setup();
expect(start.removeFromPath('http://localhost:5601/foo/bar/a/b')).toBe(
const { setupContract } = setupService();
expect(setupContract.removeFromPath('http://localhost:5601/foo/bar/a/b')).toBe(
'http://localhost:5601/foo/bar/a/b'
);
});
it('returns slash if path is just basePath', () => {
const { start } = setup();
expect(start.removeFromPath('/foo/bar')).toBe('/');
const { setupContract } = setupService();
expect(setupContract.removeFromPath('/foo/bar')).toBe('/');
});
it('returns full path if basePath is not its own segment', () => {
const { start } = setup();
expect(start.removeFromPath('/foo/barhop')).toBe('/foo/barhop');
const { setupContract } = setupService();
expect(setupContract.removeFromPath('/foo/barhop')).toBe('/foo/barhop');
});
});

View file

@ -17,15 +17,15 @@
* under the License.
*/
import { InjectedMetadataStart } from '../injected_metadata';
import { InjectedMetadataSetup } from '../injected_metadata';
import { modifyUrl } from '../utils';
interface Deps {
injectedMetadata: InjectedMetadataStart;
injectedMetadata: InjectedMetadataSetup;
}
export class BasePathService {
public start({ injectedMetadata }: Deps) {
public setup({ injectedMetadata }: Deps) {
const basePath = injectedMetadata.getBasePath() || '';
return {
@ -71,4 +71,4 @@ export class BasePathService {
}
}
export type BasePathStart = ReturnType<BasePathService['start']>;
export type BasePathSetup = ReturnType<BasePathService['setup']>;

View file

@ -17,4 +17,4 @@
* under the License.
*/
export { BasePathService, BasePathStart } from './base_path_service';
export { BasePathService, BasePathSetup } from './base_path_service';

View file

@ -17,10 +17,10 @@
* under the License.
*/
import { BehaviorSubject } from 'rxjs';
import { Brand, Breadcrumb, ChromeService, ChromeStart } from './chrome_service';
import { Brand, Breadcrumb, ChromeService, ChromeSetup } from './chrome_service';
const createStartContractMock = () => {
const startContract: jest.Mocked<ChromeStart> = {
const createSetupContractMock = () => {
const setupContract: jest.Mocked<ChromeSetup> = {
setBrand: jest.fn(),
getBrand$: jest.fn(),
setIsVisible: jest.fn(),
@ -35,26 +35,26 @@ const createStartContractMock = () => {
getHelpExtension$: jest.fn(),
setHelpExtension: jest.fn(),
};
startContract.getBrand$.mockReturnValue(new BehaviorSubject({} as Brand));
startContract.getIsVisible$.mockReturnValue(new BehaviorSubject(false));
startContract.getIsCollapsed$.mockReturnValue(new BehaviorSubject(false));
startContract.getApplicationClasses$.mockReturnValue(new BehaviorSubject(['class-name']));
startContract.getBreadcrumbs$.mockReturnValue(new BehaviorSubject([{} as Breadcrumb]));
startContract.getHelpExtension$.mockReturnValue(new BehaviorSubject(undefined));
return startContract;
setupContract.getBrand$.mockReturnValue(new BehaviorSubject({} as Brand));
setupContract.getIsVisible$.mockReturnValue(new BehaviorSubject(false));
setupContract.getIsCollapsed$.mockReturnValue(new BehaviorSubject(false));
setupContract.getApplicationClasses$.mockReturnValue(new BehaviorSubject(['class-name']));
setupContract.getBreadcrumbs$.mockReturnValue(new BehaviorSubject([{} as Breadcrumb]));
setupContract.getHelpExtension$.mockReturnValue(new BehaviorSubject(undefined));
return setupContract;
};
type ChromeServiceContract = PublicMethodsOf<ChromeService>;
const createMock = () => {
const mocked: jest.Mocked<ChromeServiceContract> = {
start: jest.fn(),
setup: jest.fn(),
stop: jest.fn(),
};
mocked.start.mockReturnValue(createStartContractMock());
mocked.setup.mockReturnValue(createSetupContractMock());
return mocked;
};
export const chromeServiceMock = {
create: createMock,
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -32,10 +32,10 @@ const store = new Map();
import { ChromeService } from './chrome_service';
function defaultStartDeps(): any {
function defaultSetupDeps(): any {
return {
notifications: notificationServiceMock.createStartContract(),
injectedMetadata: injectedMetadataServiceMock.createStartContract(),
notifications: notificationServiceMock.createSetupContract(),
injectedMetadata: injectedMetadataServiceMock.createSetupContract(),
};
}
@ -43,13 +43,13 @@ beforeEach(() => {
store.clear();
});
describe('start', () => {
describe('setup', () => {
it('adds legacy browser warning if browserSupportsCsp is disabled and warnLegacyBrowsers is enabled', () => {
const service = new ChromeService({ browserSupportsCsp: false });
const startDeps = defaultStartDeps();
startDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true });
service.start(startDeps);
expect(startDeps.notifications.toasts.addWarning.mock.calls).toMatchInlineSnapshot(`
const setupDeps = defaultSetupDeps();
setupDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true });
service.setup(setupDeps);
expect(setupDeps.notifications.toasts.addWarning.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
"Your browser does not meet the security requirements for Kibana.",
@ -60,34 +60,34 @@ Array [
it('does not add legacy browser warning if browser supports CSP', () => {
const service = new ChromeService({ browserSupportsCsp: true });
const startDeps = defaultStartDeps();
startDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true });
service.start(startDeps);
expect(startDeps.notifications.toasts.addWarning).not.toBeCalled();
const setupDeps = defaultSetupDeps();
setupDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true });
service.setup(setupDeps);
expect(setupDeps.notifications.toasts.addWarning).not.toBeCalled();
});
it('does not add legacy browser warning if warnLegacyBrowsers is disabled', () => {
const service = new ChromeService({ browserSupportsCsp: false });
const startDeps = defaultStartDeps();
startDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: false });
service.start(startDeps);
expect(startDeps.notifications.toasts.addWarning).not.toBeCalled();
const setupDeps = defaultSetupDeps();
setupDeps.injectedMetadata.getCspConfig.mockReturnValue({ warnLegacyBrowsers: false });
service.setup(setupDeps);
expect(setupDeps.notifications.toasts.addWarning).not.toBeCalled();
});
describe('brand', () => {
it('updates/emits the brand as it changes', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const promise = start
const setup = service.setup(defaultSetupDeps());
const promise = setup
.getBrand$()
.pipe(toArray())
.toPromise();
start.setBrand({
setup.setBrand({
logo: 'big logo',
smallLogo: 'not so big logo',
});
start.setBrand({
setup.setBrand({
logo: 'big logo without small logo',
});
service.stop();
@ -111,15 +111,15 @@ Array [
describe('visibility', () => {
it('updates/emits the visibility', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const promise = start
const setup = service.setup(defaultSetupDeps());
const promise = setup
.getIsVisible$()
.pipe(toArray())
.toPromise();
start.setIsVisible(true);
start.setIsVisible(false);
start.setIsVisible(true);
setup.setIsVisible(true);
setup.setIsVisible(false);
setup.setIsVisible(true);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
@ -132,19 +132,19 @@ Array [
`);
});
it('always emits false if embed query string is in hash when started', async () => {
it('always emits false if embed query string is in hash when set up', async () => {
window.history.pushState(undefined, '', '#/home?a=b&embed=true');
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const promise = start
const setup = service.setup(defaultSetupDeps());
const promise = setup
.getIsVisible$()
.pipe(toArray())
.toPromise();
start.setIsVisible(true);
start.setIsVisible(false);
start.setIsVisible(true);
setup.setIsVisible(true);
setup.setIsVisible(false);
setup.setIsVisible(true);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
@ -161,15 +161,15 @@ Array [
describe('is collapsed', () => {
it('updates/emits isCollapsed', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const promise = start
const setup = service.setup(defaultSetupDeps());
const promise = setup
.getIsCollapsed$()
.pipe(toArray())
.toPromise();
start.setIsCollapsed(true);
start.setIsCollapsed(false);
start.setIsCollapsed(true);
setup.setIsCollapsed(true);
setup.setIsCollapsed(false);
setup.setIsCollapsed(true);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
@ -184,12 +184,12 @@ Array [
it('only stores true in localStorage', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const setup = service.setup(defaultSetupDeps());
start.setIsCollapsed(true);
setup.setIsCollapsed(true);
expect(store.size).toBe(1);
start.setIsCollapsed(false);
setup.setIsCollapsed(false);
expect(store.size).toBe(0);
});
});
@ -197,19 +197,19 @@ Array [
describe('application classes', () => {
it('updates/emits the application classes', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const promise = start
const setup = service.setup(defaultSetupDeps());
const promise = setup
.getApplicationClasses$()
.pipe(toArray())
.toPromise();
start.addApplicationClass('foo');
start.addApplicationClass('foo');
start.addApplicationClass('bar');
start.addApplicationClass('bar');
start.addApplicationClass('baz');
start.removeApplicationClass('bar');
start.removeApplicationClass('foo');
setup.addApplicationClass('foo');
setup.addApplicationClass('foo');
setup.addApplicationClass('bar');
setup.addApplicationClass('bar');
setup.addApplicationClass('baz');
setup.removeApplicationClass('bar');
setup.removeApplicationClass('foo');
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
@ -249,16 +249,16 @@ Array [
describe('breadcrumbs', () => {
it('updates/emits the current set of breadcrumbs', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const promise = start
const setup = service.setup(defaultSetupDeps());
const promise = setup
.getBreadcrumbs$()
.pipe(toArray())
.toPromise();
start.setBreadcrumbs([{ text: 'foo' }, { text: 'bar' }]);
start.setBreadcrumbs([{ text: 'foo' }]);
start.setBreadcrumbs([{ text: 'bar' }]);
start.setBreadcrumbs([]);
setup.setBreadcrumbs([{ text: 'foo' }, { text: 'bar' }]);
setup.setBreadcrumbs([{ text: 'foo' }]);
setup.setBreadcrumbs([{ text: 'bar' }]);
setup.setBreadcrumbs([]);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
@ -291,14 +291,14 @@ Array [
describe('help extension', () => {
it('updates/emits the current help extension', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const promise = start
const setup = service.setup(defaultSetupDeps());
const promise = setup
.getHelpExtension$()
.pipe(toArray())
.toPromise();
start.setHelpExtension(() => () => undefined);
start.setHelpExtension(undefined);
setup.setHelpExtension(() => () => undefined);
setup.setHelpExtension(undefined);
service.stop();
await expect(promise).resolves.toMatchInlineSnapshot(`
@ -315,14 +315,14 @@ Array [
describe('stop', () => {
it('completes applicationClass$, isCollapsed$, breadcrumbs$, isVisible$, and brand$ observables', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const setup = service.setup(defaultSetupDeps());
const promise = Rx.combineLatest(
start.getBrand$(),
start.getApplicationClasses$(),
start.getIsCollapsed$(),
start.getBreadcrumbs$(),
start.getIsVisible$(),
start.getHelpExtension$()
setup.getBrand$(),
setup.getApplicationClasses$(),
setup.getIsCollapsed$(),
setup.getBreadcrumbs$(),
setup.getIsVisible$(),
setup.getHelpExtension$()
).toPromise();
service.stop();
@ -331,17 +331,17 @@ describe('stop', () => {
it('completes immediately if service already stopped', async () => {
const service = new ChromeService({ browserSupportsCsp: true });
const start = service.start(defaultStartDeps());
const setup = service.setup(defaultSetupDeps());
service.stop();
await expect(
Rx.combineLatest(
start.getBrand$(),
start.getApplicationClasses$(),
start.getIsCollapsed$(),
start.getBreadcrumbs$(),
start.getIsVisible$(),
start.getHelpExtension$()
setup.getBrand$(),
setup.getApplicationClasses$(),
setup.getIsCollapsed$(),
setup.getBreadcrumbs$(),
setup.getIsVisible$(),
setup.getHelpExtension$()
).toPromise()
).resolves.toBe(undefined);
});

View file

@ -22,8 +22,8 @@ import * as Url from 'url';
import { i18n } from '@kbn/i18n';
import * as Rx from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { InjectedMetadataStart } from '../injected_metadata';
import { NotificationsStart } from '../notifications';
import { InjectedMetadataSetup } from '../injected_metadata';
import { NotificationsSetup } from '../notifications';
const IS_COLLAPSED_KEY = 'core.chrome.isCollapsed';
@ -49,9 +49,9 @@ interface ConstructorParams {
browserSupportsCsp: boolean;
}
interface StartDeps {
injectedMetadata: InjectedMetadataStart;
notifications: NotificationsStart;
interface SetupDeps {
injectedMetadata: InjectedMetadataSetup;
notifications: NotificationsSetup;
}
export class ChromeService {
@ -62,7 +62,7 @@ export class ChromeService {
this.browserSupportsCsp = browserSupportsCsp;
}
public start({ injectedMetadata, notifications }: StartDeps) {
public setup({ injectedMetadata, notifications }: SetupDeps) {
const FORCE_HIDDEN = isEmbedParamInHash();
const brand$ = new Rx.BehaviorSubject<Brand>({});
@ -202,4 +202,4 @@ export class ChromeService {
}
}
export type ChromeStart = ReturnType<ChromeService['start']>;
export type ChromeSetup = ReturnType<ChromeService['setup']>;

View file

@ -17,4 +17,4 @@
* under the License.
*/
export { Breadcrumb, ChromeService, ChromeStart, Brand, HelpExtension } from './chrome_service';
export { Breadcrumb, ChromeService, ChromeSetup, Brand, HelpExtension } from './chrome_service';

View file

@ -250,105 +250,105 @@ describe('#stop', () => {
rootDomElement,
});
coreSystem.start();
coreSystem.setup();
expect(rootDomElement.innerHTML).not.toBe('');
coreSystem.stop();
expect(rootDomElement.innerHTML).toBe('');
});
});
describe('#start()', () => {
function startCore(rootDomElement = defaultCoreSystemParams.rootDomElement) {
describe('#setup()', () => {
function setupCore(rootDomElement = defaultCoreSystemParams.rootDomElement) {
const core = createCoreSystem({
rootDomElement,
});
return core.start();
return core.setup();
}
it('clears the children of the rootDomElement and appends container for legacyPlatform and notifications', () => {
const root = document.createElement('div');
root.innerHTML = '<p>foo bar</p>';
startCore(root);
setupCore(root);
expect(root.innerHTML).toBe('<div></div><div></div>');
});
it('calls injectedMetadata#start()', () => {
startCore();
it('calls injectedMetadata#setup()', () => {
setupCore();
expect(MockInjectedMetadataService.start).toHaveBeenCalledTimes(1);
expect(MockInjectedMetadataService.setup).toHaveBeenCalledTimes(1);
});
it('calls http#start()', () => {
startCore();
expect(MockHttpService.start).toHaveBeenCalledTimes(1);
it('calls http#setup()', () => {
setupCore();
expect(MockHttpService.setup).toHaveBeenCalledTimes(1);
});
it('calls basePath#start()', () => {
startCore();
expect(MockBasePathService.start).toHaveBeenCalledTimes(1);
it('calls basePath#setup()', () => {
setupCore();
expect(MockBasePathService.setup).toHaveBeenCalledTimes(1);
});
it('calls uiSettings#start()', () => {
startCore();
expect(MockUiSettingsService.start).toHaveBeenCalledTimes(1);
it('calls uiSettings#setup()', () => {
setupCore();
expect(MockUiSettingsService.setup).toHaveBeenCalledTimes(1);
});
it('calls i18n#start()', () => {
startCore();
expect(MockI18nService.start).toHaveBeenCalledTimes(1);
it('calls i18n#setup()', () => {
setupCore();
expect(MockI18nService.setup).toHaveBeenCalledTimes(1);
});
it('calls fatalErrors#start()', () => {
startCore();
expect(MockFatalErrorsService.start).toHaveBeenCalledTimes(1);
it('calls fatalErrors#setup()', () => {
setupCore();
expect(MockFatalErrorsService.setup).toHaveBeenCalledTimes(1);
});
it('calls notifications#start()', () => {
startCore();
expect(MockNotificationsService.start).toHaveBeenCalledTimes(1);
it('calls notifications#setup()', () => {
setupCore();
expect(MockNotificationsService.setup).toHaveBeenCalledTimes(1);
});
it('calls chrome#start()', () => {
startCore();
expect(MockChromeService.start).toHaveBeenCalledTimes(1);
it('calls chrome#setup()', () => {
setupCore();
expect(MockChromeService.setup).toHaveBeenCalledTimes(1);
});
});
describe('LegacyPlatform targetDomElement', () => {
it('only mounts the element when started, before starting the legacyPlatformService', () => {
it('only mounts the element when set up, before setting up the legacyPlatformService', () => {
const rootDomElement = document.createElement('div');
const core = createCoreSystem({
rootDomElement,
});
let targetDomElementParentInStart: HTMLElement;
MockLegacyPlatformService.start.mockImplementation(() => {
targetDomElementParentInStart = targetDomElement.parentElement;
let targetDomElementParentInSetup: HTMLElement;
MockLegacyPlatformService.setup.mockImplementation(() => {
targetDomElementParentInSetup = targetDomElement.parentElement;
});
// targetDomElement should not have a parent element when the LegacyPlatformService is constructed
const [[{ targetDomElement }]] = LegacyPlatformServiceConstructor.mock.calls;
expect(targetDomElement).toHaveProperty('parentElement', null);
// starting the core system should mount the targetDomElement as a child of the rootDomElement
core.start();
expect(targetDomElementParentInStart!).toBe(rootDomElement);
// setting up the core system should mount the targetDomElement as a child of the rootDomElement
core.setup();
expect(targetDomElementParentInSetup!).toBe(rootDomElement);
});
});
describe('Notifications targetDomElement', () => {
it('only mounts the element when started, before starting the notificationsService', () => {
it('only mounts the element when set up, before setting up the notificationsService', () => {
const rootDomElement = document.createElement('div');
const core = createCoreSystem({
rootDomElement,
});
let targetDomElementParentInStart: HTMLElement;
let targetDomElementParentInSetup: HTMLElement;
MockNotificationsService.start.mockImplementation(
MockNotificationsService.setup.mockImplementation(
(): any => {
targetDomElementParentInStart = targetDomElement.parentElement;
targetDomElementParentInSetup = targetDomElement.parentElement;
}
);
@ -356,8 +356,8 @@ describe('Notifications targetDomElement', () => {
const [[{ targetDomElement }]] = NotificationServiceConstructor.mock.calls;
expect(targetDomElement).toHaveProperty('parentElement', null);
// starting the core system should mount the targetDomElement as a child of the rootDomElement
core.start();
expect(targetDomElementParentInStart!).toBe(rootDomElement);
// setting up the core system should mount the targetDomElement as a child of the rootDomElement
core.setup();
expect(targetDomElementParentInSetup!).toBe(rootDomElement);
});
});

View file

@ -38,10 +38,12 @@ interface Params {
}
/**
* The CoreSystem is the root of the new platform, and starts all parts
* The CoreSystem is the root of the new platform, and setups all parts
* of Kibana in the UI, including the LegacyPlatform which is managed
* by the LegacyPlatformService. As we migrate more things to the new
* platform the CoreSystem will get many more Services.
*
* @internal
*/
export class CoreSystem {
private readonly fatalErrors: FatalErrorsService;
@ -100,7 +102,7 @@ export class CoreSystem {
});
}
public start() {
public setup() {
try {
// ensure the rootDomElement is empty
this.rootDomElement.textContent = '';
@ -108,24 +110,24 @@ export class CoreSystem {
this.rootDomElement.appendChild(this.notificationsTargetDomElement);
this.rootDomElement.appendChild(this.legacyPlatformTargetDomElement);
const i18n = this.i18n.start();
const notifications = this.notifications.start({ i18n });
const injectedMetadata = this.injectedMetadata.start();
const fatalErrors = this.fatalErrors.start({ i18n });
const http = this.http.start({ fatalErrors });
const basePath = this.basePath.start({ injectedMetadata });
const uiSettings = this.uiSettings.start({
const i18n = this.i18n.setup();
const notifications = this.notifications.setup({ i18n });
const injectedMetadata = this.injectedMetadata.setup();
const fatalErrors = this.fatalErrors.setup({ i18n });
const http = this.http.setup({ fatalErrors });
const basePath = this.basePath.setup({ injectedMetadata });
const uiSettings = this.uiSettings.setup({
notifications,
http,
injectedMetadata,
basePath,
});
const chrome = this.chrome.start({
const chrome = this.chrome.setup({
injectedMetadata,
notifications,
});
this.legacyPlatform.start({
this.legacyPlatform.setup({
i18n,
injectedMetadata,
fatalErrors,

View file

@ -21,7 +21,7 @@ exports[`#add() deletes all children of rootDomElement and renders <FatalErrorSc
</div>
`;
exports[`start.add() deletes all children of rootDomElement and renders <FatalErrorScreen /> into it: fatal error screen component 1`] = `
exports[`setup.add() deletes all children of rootDomElement and renders <FatalErrorScreen /> into it: fatal error screen component 1`] = `
Array [
Array [
<I18nContext>
@ -36,7 +36,7 @@ Array [
]
`;
exports[`start.add() deletes all children of rootDomElement and renders <FatalErrorScreen /> into it: fatal error screen container 1`] = `
exports[`setup.add() deletes all children of rootDomElement and renders <FatalErrorScreen /> into it: fatal error screen container 1`] = `
<div>
<div />
</div>

View file

@ -16,29 +16,29 @@
* specific language governing permissions and limitations
* under the License.
*/
import { FatalErrorsService, FatalErrorsStart } from './fatal_errors_service';
import { FatalErrorsService, FatalErrorsSetup } from './fatal_errors_service';
const createStartContractMock = () => {
const startContract: jest.Mocked<FatalErrorsStart> = {
const createSetupContractMock = () => {
const setupContract: jest.Mocked<FatalErrorsSetup> = {
add: jest.fn<never, any>(() => undefined as never),
get$: jest.fn(),
};
return startContract;
return setupContract;
};
type FatalErrorsServiceContract = PublicMethodsOf<FatalErrorsService>;
const createMock = () => {
const mocked: jest.Mocked<FatalErrorsServiceContract> = {
start: jest.fn(),
setup: jest.fn(),
add: jest.fn<never, any>(() => undefined as never),
};
mocked.start.mockReturnValue(createStartContractMock());
mocked.setup.mockReturnValue(createSetupContractMock());
return mocked;
};
export const fatalErrorsServiceMock = {
create: createMock,
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -33,7 +33,7 @@ jest.mock('react-dom', () => {
import { FatalErrorsService } from './fatal_errors_service';
function setup() {
function setupService() {
const rootDomElement = document.createElement('div');
const injectedMetadata = {
@ -68,7 +68,7 @@ afterEach(() => {
describe('#add()', () => {
it('calls stopCoreSystem() param', () => {
const { stopCoreSystem, fatalErrors } = setup();
const { stopCoreSystem, fatalErrors } = setupService();
expect(stopCoreSystem).not.toHaveBeenCalled();
expect(() => {
@ -79,7 +79,7 @@ describe('#add()', () => {
});
it('deletes all children of rootDomElement and renders <FatalErrorScreen /> into it', () => {
const { fatalErrors, rootDomElement } = setup();
const { fatalErrors, rootDomElement } = setupService();
rootDomElement.innerHTML = `
<h1>Loading...</h1>
@ -96,21 +96,21 @@ describe('#add()', () => {
});
});
describe('start.add()', () => {
describe('setup.add()', () => {
it('exposes a function that passes its two arguments to fatalErrors.add()', () => {
const { fatalErrors, i18n } = setup();
const { fatalErrors, i18n } = setupService();
jest.spyOn(fatalErrors, 'add').mockImplementation(() => undefined as never);
expect(fatalErrors.add).not.toHaveBeenCalled();
const { add } = fatalErrors.start({ i18n });
const { add } = fatalErrors.setup({ i18n });
add('foo', 'bar');
expect(fatalErrors.add).toHaveBeenCalledTimes(1);
expect(fatalErrors.add).toHaveBeenCalledWith('foo', 'bar');
});
it('deletes all children of rootDomElement and renders <FatalErrorScreen /> into it', () => {
const { fatalErrors, i18n, rootDomElement } = setup();
const { fatalErrors, i18n, rootDomElement } = setupService();
rootDomElement.innerHTML = `
<h1>Loading...</h1>
@ -120,7 +120,7 @@ describe('start.add()', () => {
expect(mockRender).not.toHaveBeenCalled();
expect(rootDomElement.children).toHaveLength(2);
const { add } = fatalErrors.start({ i18n });
const { add } = fatalErrors.setup({ i18n });
expect(() => add(new Error('foo'))).toThrowError();
expect(rootDomElement).toMatchSnapshot('fatal error screen container');
@ -128,14 +128,14 @@ describe('start.add()', () => {
});
});
describe('start.get$()', () => {
describe('setup.get$()', () => {
it('provides info about the errors passed to fatalErrors.add()', () => {
const { fatalErrors, i18n } = setup();
const { fatalErrors, i18n } = setupService();
const start = fatalErrors.start({ i18n });
const setup = fatalErrors.setup({ i18n });
const onError = jest.fn();
start.get$().subscribe(onError);
setup.get$().subscribe(onError);
expect(onError).not.toHaveBeenCalled();
expect(() => {

View file

@ -22,7 +22,7 @@ import { render } from 'react-dom';
import * as Rx from 'rxjs';
import { first, tap } from 'rxjs/operators';
import { I18nStart } from '../i18n';
import { I18nSetup } from '../i18n';
import { InjectedMetadataService } from '../injected_metadata';
import { FatalErrorsScreen } from './fatal_errors_screen';
import { ErrorInfo, getErrorInfo } from './get_error_info';
@ -34,12 +34,12 @@ export interface FatalErrorsParams {
}
interface Deps {
i18n: I18nStart;
i18n: I18nSetup;
}
export class FatalErrorsService {
private readonly errorInfo$ = new Rx.ReplaySubject<ErrorInfo>();
private i18n?: I18nStart;
private i18n?: I18nSetup;
constructor(private params: FatalErrorsParams) {
this.errorInfo$
@ -69,7 +69,7 @@ export class FatalErrorsService {
throw error;
};
public start({ i18n }: Deps) {
public setup({ i18n }: Deps) {
this.i18n = i18n;
return {
@ -92,7 +92,7 @@ export class FatalErrorsService {
const container = document.createElement('div');
this.params.rootDomElement.appendChild(container);
// If error occurred before I18nService has been started we don't have any
// If error occurred before I18nService has been set up we don't have any
// i18n context to provide.
const I18nContext = this.i18n ? this.i18n.Context : React.Fragment;
@ -109,4 +109,4 @@ export class FatalErrorsService {
}
}
export type FatalErrorsStart = ReturnType<FatalErrorsService['start']>;
export type FatalErrorsSetup = ReturnType<FatalErrorsService['setup']>;

View file

@ -52,7 +52,7 @@ function formatErrorMessage(error: any) {
}
/**
* Format the stack trace from a message so that it starts with the message, which
* Format the stack trace from a message so that it setups with the message, which
* some browsers do automatically and some don't
*/
function formatStack(err: Error) {

View file

@ -17,4 +17,4 @@
* under the License.
*/
export { FatalErrorsStart, FatalErrorsService } from './fatal_errors_service';
export { FatalErrorsSetup, FatalErrorsService } from './fatal_errors_service';

View file

@ -16,27 +16,27 @@
* specific language governing permissions and limitations
* under the License.
*/
import { HttpService, HttpStart } from './http_service';
import { HttpService, HttpSetup } from './http_service';
const createStartContractMock = () => {
const startContract: jest.Mocked<HttpStart> = {
const createSetupContractMock = () => {
const setupContract: jest.Mocked<HttpSetup> = {
addLoadingCount: jest.fn(),
getLoadingCount$: jest.fn(),
};
return startContract;
return setupContract;
};
type HttpServiceContract = PublicMethodsOf<HttpService>;
const createMock = () => {
const mocked: jest.Mocked<HttpServiceContract> = {
start: jest.fn(),
setup: jest.fn(),
stop: jest.fn(),
};
mocked.start.mockReturnValue(createStartContractMock());
mocked.setup.mockReturnValue(createSetupContractMock());
return mocked;
};
export const httpServiceMock = {
create: createMock,
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -23,27 +23,27 @@ import { toArray } from 'rxjs/operators';
import { fatalErrorsServiceMock } from '../fatal_errors/fatal_errors_service.mock';
import { HttpService } from './http_service';
function setup() {
function setupService() {
const service = new HttpService();
const fatalErrors = fatalErrorsServiceMock.createStartContract();
const start = service.start({ fatalErrors });
const fatalErrors = fatalErrorsServiceMock.createSetupContract();
const setup = service.setup({ fatalErrors });
return { service, fatalErrors, start };
return { service, fatalErrors, setup };
}
describe('addLoadingCount()', async () => {
it('subscribes to passed in sources, unsubscribes on stop', () => {
const { service, start } = setup();
const { service, setup } = setupService();
const unsubA = jest.fn();
const subA = jest.fn().mockReturnValue(unsubA);
start.addLoadingCount(new Rx.Observable(subA));
setup.addLoadingCount(new Rx.Observable(subA));
expect(subA).toHaveBeenCalledTimes(1);
expect(unsubA).not.toHaveBeenCalled();
const unsubB = jest.fn();
const subB = jest.fn().mockReturnValue(unsubB);
start.addLoadingCount(new Rx.Observable(subB));
setup.addLoadingCount(new Rx.Observable(subB));
expect(subB).toHaveBeenCalledTimes(1);
expect(unsubB).not.toHaveBeenCalled();
@ -56,35 +56,35 @@ describe('addLoadingCount()', async () => {
});
it('adds a fatal error if source observables emit an error', async () => {
const { start, fatalErrors } = setup();
const { setup, fatalErrors } = setupService();
start.addLoadingCount(Rx.throwError(new Error('foo bar')));
setup.addLoadingCount(Rx.throwError(new Error('foo bar')));
expect(fatalErrors.add.mock.calls).toMatchSnapshot();
});
it('adds a fatal error if source observable emits a negative number', async () => {
const { start, fatalErrors } = setup();
const { setup, fatalErrors } = setupService();
start.addLoadingCount(Rx.of(1, 2, 3, 4, -9));
setup.addLoadingCount(Rx.of(1, 2, 3, 4, -9));
expect(fatalErrors.add.mock.calls).toMatchSnapshot();
});
});
describe('getLoadingCount$()', async () => {
it('emits 0 initially, the right count when sources emit their own count, and ends with zero', async () => {
const { service, start } = setup();
const { service, setup } = setupService();
const countA$ = new Rx.Subject<number>();
const countB$ = new Rx.Subject<number>();
const countC$ = new Rx.Subject<number>();
const promise = start
const promise = setup
.getLoadingCount$()
.pipe(toArray())
.toPromise();
start.addLoadingCount(countA$);
start.addLoadingCount(countB$);
start.addLoadingCount(countC$);
setup.addLoadingCount(countA$);
setup.addLoadingCount(countB$);
setup.addLoadingCount(countC$);
countA$.next(100);
countB$.next(10);
@ -99,15 +99,15 @@ describe('getLoadingCount$()', async () => {
});
it('only emits when loading count changes', async () => {
const { service, start } = setup();
const { service, setup } = setupService();
const count$ = new Rx.Subject<number>();
const promise = start
const promise = setup
.getLoadingCount$()
.pipe(toArray())
.toPromise();
start.addLoadingCount(count$);
setup.addLoadingCount(count$);
count$.next(0);
count$.next(0);
count$.next(0);

View file

@ -28,17 +28,17 @@ import {
tap,
} from 'rxjs/operators';
import { FatalErrorsStart } from '../fatal_errors';
import { FatalErrorsSetup } from '../fatal_errors';
interface Deps {
fatalErrors: FatalErrorsStart;
fatalErrors: FatalErrorsSetup;
}
export class HttpService {
private readonly loadingCount$ = new Rx.BehaviorSubject(0);
private readonly stop$ = new Rx.Subject();
public start({ fatalErrors }: Deps) {
public setup({ fatalErrors }: Deps) {
return {
addLoadingCount: (count$: Rx.Observable<number>) => {
count$
@ -83,4 +83,4 @@ export class HttpService {
}
}
export type HttpStart = ReturnType<HttpService['start']>;
export type HttpSetup = ReturnType<HttpService['setup']>;

View file

@ -17,4 +17,4 @@
* under the License.
*/
export { HttpService, HttpStart } from './http_service';
export { HttpService, HttpSetup } from './http_service';

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`#start() returns \`Context\` component 1`] = `
exports[`#setup() returns \`Context\` component 1`] = `
<MockI18nProvider>
<MockEuiContext
i18n={

View file

@ -16,26 +16,26 @@
* specific language governing permissions and limitations
* under the License.
*/
import { I18nService, I18nStart } from './i18n_service';
import { I18nService, I18nSetup } from './i18n_service';
const createStartContractMock = () => {
const startContract: jest.Mocked<I18nStart> = {
const createSetupContractMock = () => {
const setupContract: jest.Mocked<I18nSetup> = {
Context: jest.fn(),
};
return startContract;
return setupContract;
};
type I18nServiceContract = PublicMethodsOf<I18nService>;
const createMock = () => {
const mocked: jest.Mocked<I18nServiceContract> = {
start: jest.fn(),
setup: jest.fn(),
stop: jest.fn(),
};
mocked.start.mockReturnValue(createStartContractMock());
mocked.setup.mockReturnValue(createSetupContractMock());
return mocked;
};
export const i18nServiceMock = {
create: createMock,
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -44,11 +44,11 @@ afterEach(() => {
jest.resetModules();
});
describe('#start()', () => {
describe('#setup()', () => {
it('returns `Context` component', () => {
const i18nService = new I18nService();
const i18n = i18nService.start();
const i18n = i18nService.setup();
expect(shallow(<i18n.Context>content</i18n.Context>)).toMatchSnapshot();
});

View file

@ -27,7 +27,7 @@ import { I18nProvider } from '@kbn/i18n/react';
* Service that is responsible for i18n capabilities.
*/
export class I18nService {
public start() {
public setup() {
const mapping = {
'euiTablePagination.rowsPerPage': i18n.translate('core.euiTablePagination.rowsPerPage', {
defaultMessage: 'Rows per page',
@ -54,4 +54,4 @@ export class I18nService {
}
}
export type I18nStart = ReturnType<I18nService['start']>;
export type I18nSetup = ReturnType<I18nService['setup']>;

View file

@ -17,4 +17,4 @@
* under the License.
*/
export { I18nService, I18nStart } from './i18n_service';
export { I18nService, I18nSetup } from './i18n_service';

View file

@ -17,4 +17,24 @@
* under the License.
*/
import { BasePathSetup } from './base_path';
import { ChromeSetup } from './chrome';
import { FatalErrorsSetup } from './fatal_errors';
import { HttpSetup } from './http';
import { I18nSetup } from './i18n';
import { InjectedMetadataSetup } from './injected_metadata';
import { NotificationsSetup } from './notifications';
import { UiSettingsSetup } from './ui_settings';
export { CoreSystem } from './core_system';
export interface CoreSetup {
i18n: I18nSetup;
injectedMetadata: InjectedMetadataSetup;
fatalErrors: FatalErrorsSetup;
notifications: NotificationsSetup;
http: HttpSetup;
basePath: BasePathSetup;
uiSettings: UiSettingsSetup;
chrome: ChromeSetup;
}

View file

@ -20,5 +20,5 @@
export {
InjectedMetadataService,
InjectedMetadataParams,
InjectedMetadataStart,
InjectedMetadataSetup,
} from './injected_metadata_service';

View file

@ -16,40 +16,42 @@
* specific language governing permissions and limitations
* under the License.
*/
import { InjectedMetadataService, InjectedMetadataStart } from './injected_metadata_service';
import { InjectedMetadataService, InjectedMetadataSetup } from './injected_metadata_service';
const createStartContractMock = () => {
const startContract: jest.Mocked<InjectedMetadataStart> = {
const createSetupContractMock = () => {
const setupContract: jest.Mocked<InjectedMetadataSetup> = {
getBasePath: jest.fn(),
getKibanaVersion: jest.fn(),
getCspConfig: jest.fn(),
getLegacyMetadata: jest.fn(),
getPlugins: jest.fn(),
getInjectedVar: jest.fn(),
getInjectedVars: jest.fn(),
};
startContract.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true });
startContract.getKibanaVersion.mockReturnValue('kibanaVersion');
startContract.getLegacyMetadata.mockReturnValue({
setupContract.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true });
setupContract.getKibanaVersion.mockReturnValue('kibanaVersion');
setupContract.getLegacyMetadata.mockReturnValue({
uiSettings: {
defaults: { legacyInjectedUiSettingDefaults: true },
user: { legacyInjectedUiSettingUserValues: true },
},
} as any);
return startContract;
setupContract.getPlugins.mockReturnValue([]);
return setupContract;
};
type InjectedMetadataServiceContract = PublicMethodsOf<InjectedMetadataService>;
const createMock = () => {
const mocked: jest.Mocked<InjectedMetadataServiceContract> = {
start: jest.fn(),
setup: jest.fn(),
getKibanaVersion: jest.fn(),
getKibanaBuildNumber: jest.fn(),
};
mocked.start.mockReturnValue(createStartContractMock());
mocked.setup.mockReturnValue(createSetupContractMock());
return mocked;
};
export const injectedMetadataServiceMock = {
create: createMock,
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -17,6 +17,7 @@
* under the License.
*/
import { DiscoveredPlugin } from '../../server';
import { InjectedMetadataService } from './injected_metadata_service';
describe('#getKibanaVersion', () => {
@ -43,7 +44,7 @@ describe('#getKibanaBuildNumber', () => {
});
});
describe('start.getCspConfig()', () => {
describe('setup.getCspConfig()', () => {
it('returns injectedMetadata.csp', () => {
const injectedMetadata = new InjectedMetadataService({
injectedMetadata: {
@ -53,7 +54,7 @@ describe('start.getCspConfig()', () => {
},
} as any);
const contract = injectedMetadata.start();
const contract = injectedMetadata.setup();
expect(contract.getCspConfig()).toEqual({
warnLegacyBrowsers: true,
});
@ -68,7 +69,7 @@ describe('start.getCspConfig()', () => {
},
} as any);
const csp = injectedMetadata.start().getCspConfig();
const csp = injectedMetadata.setup().getCspConfig();
expect(() => {
// @ts-ignore TS knows this shouldn't be possible
csp.warnLegacyBrowsers = false;
@ -76,7 +77,44 @@ describe('start.getCspConfig()', () => {
});
});
describe('start.getLegacyMetadata()', () => {
describe('setup.getPlugins()', () => {
it('returns injectedMetadata.uiPlugins', () => {
const injectedMetadata = new InjectedMetadataService({
injectedMetadata: {
uiPlugins: [{ id: 'plugin-1', plugin: {} }, { id: 'plugin-2', plugin: {} }],
},
} as any);
const plugins = injectedMetadata.setup().getPlugins();
expect(plugins).toEqual([{ id: 'plugin-1', plugin: {} }, { id: 'plugin-2', plugin: {} }]);
});
it('returns frozen version of uiPlugins', () => {
const injectedMetadata = new InjectedMetadataService({
injectedMetadata: {
uiPlugins: [{ id: 'plugin-1', plugin: {} }, { id: 'plugin-2', plugin: {} }],
},
} as any);
const plugins = injectedMetadata.setup().getPlugins();
expect(() => {
plugins.pop();
}).toThrowError();
expect(() => {
plugins.push({ id: 'new-plugin', plugin: {} as DiscoveredPlugin });
}).toThrowError();
expect(() => {
// @ts-ignore TS knows this shouldn't be possible
plugins[0].name = 'changed';
}).toThrowError();
expect(() => {
// @ts-ignore TS knows this shouldn't be possible
plugins[0].newProp = 'changed';
}).toThrowError();
});
});
describe('setup.getLegacyMetadata()', () => {
it('returns injectedMetadata.legacyMetadata', () => {
const injectedMetadata = new InjectedMetadataService({
injectedMetadata: {
@ -84,7 +122,7 @@ describe('start.getLegacyMetadata()', () => {
},
} as any);
const contract = injectedMetadata.start();
const contract = injectedMetadata.setup();
expect(contract.getLegacyMetadata()).toBe('foo');
});
@ -97,7 +135,7 @@ describe('start.getLegacyMetadata()', () => {
},
} as any);
const legacyMetadata = injectedMetadata.start().getLegacyMetadata();
const legacyMetadata = injectedMetadata.setup().getLegacyMetadata();
expect(legacyMetadata).toEqual({
foo: true,
});
@ -108,9 +146,9 @@ describe('start.getLegacyMetadata()', () => {
});
});
describe('start.getInjectedVar()', () => {
describe('setup.getInjectedVar()', () => {
it('returns values from injectedMetadata.vars', () => {
const start = new InjectedMetadataService({
const setup = new InjectedMetadataService({
injectedMetadata: {
vars: {
foo: {
@ -121,20 +159,20 @@ describe('start.getInjectedVar()', () => {
},
},
},
} as any).start();
} as any).setup();
expect(start.getInjectedVar('foo')).toEqual({
expect(setup.getInjectedVar('foo')).toEqual({
bar: '1',
});
expect(start.getInjectedVar('foo.bar')).toBe('1');
expect(start.getInjectedVar('baz:box')).toEqual({
expect(setup.getInjectedVar('foo.bar')).toBe('1');
expect(setup.getInjectedVar('baz:box')).toEqual({
foo: 2,
});
expect(start.getInjectedVar('')).toBe(undefined);
expect(setup.getInjectedVar('')).toBe(undefined);
});
it('returns read-only values', () => {
const start = new InjectedMetadataService({
const setup = new InjectedMetadataService({
injectedMetadata: {
vars: {
foo: {
@ -142,9 +180,9 @@ describe('start.getInjectedVar()', () => {
},
},
},
} as any).start();
} as any).setup();
const foo: any = start.getInjectedVar('foo');
const foo: any = setup.getInjectedVar('foo');
expect(() => {
foo.bar = 2;
}).toThrowErrorMatchingInlineSnapshot(
@ -158,9 +196,9 @@ describe('start.getInjectedVar()', () => {
});
});
describe('start.getInjectedVars()', () => {
describe('setup.getInjectedVars()', () => {
it('returns all injected vars, readonly', () => {
const start = new InjectedMetadataService({
const setup = new InjectedMetadataService({
injectedMetadata: {
vars: {
foo: {
@ -168,9 +206,9 @@ describe('start.getInjectedVars()', () => {
},
},
},
} as any).start();
} as any).setup();
const vars: any = start.getInjectedVars();
const vars: any = setup.getInjectedVars();
expect(() => {
vars.foo = 2;
}).toThrowErrorMatchingInlineSnapshot(

View file

@ -18,6 +18,7 @@
*/
import { get } from 'lodash';
import { DiscoveredPlugin, PluginName } from '../../server';
import { UiSettingsState } from '../ui_settings';
import { deepFreeze } from './deep_freeze';
@ -32,6 +33,10 @@ export interface InjectedMetadataParams {
vars: {
[key: string]: unknown;
};
uiPlugins: Array<{
id: PluginName;
plugin: DiscoveredPlugin;
}>;
legacyMetadata: {
app: unknown;
translations: unknown;
@ -59,11 +64,13 @@ export interface InjectedMetadataParams {
* and is read from the DOM in most cases.
*/
export class InjectedMetadataService {
private state = deepFreeze(this.params.injectedMetadata);
private state = deepFreeze(
this.params.injectedMetadata
) as InjectedMetadataParams['injectedMetadata'];
constructor(private readonly params: InjectedMetadataParams) {}
public start() {
public setup() {
return {
getBasePath: () => {
return this.state.basePath;
@ -77,6 +84,10 @@ export class InjectedMetadataService {
return this.state.csp;
},
getPlugins: () => {
return this.state.uiPlugins;
},
getLegacyMetadata: () => {
return this.state.legacyMetadata;
},
@ -100,4 +111,4 @@ export class InjectedMetadataService {
}
}
export type InjectedMetadataStart = ReturnType<InjectedMetadataService['start']>;
export type InjectedMetadataSetup = ReturnType<InjectedMetadataService['setup']>;

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`#start() load order useLegacyTestHarness = false loads ui/modules before ui/chrome, and both before legacy files 1`] = `
exports[`#setup() load order useLegacyTestHarness = false loads ui/modules before ui/chrome, and both before legacy files 1`] = `
Array [
"ui/metadata",
"ui/i18n",
@ -20,7 +20,7 @@ Array [
]
`;
exports[`#start() load order useLegacyTestHarness = true loads ui/modules before ui/test_harness, and both before legacy files 1`] = `
exports[`#setup() load order useLegacyTestHarness = true loads ui/modules before ui/test_harness, and both before legacy files 1`] = `
Array [
"ui/metadata",
"ui/i18n",

View file

@ -21,7 +21,7 @@ import { LegacyPlatformService } from './legacy_service';
type LegacyPlatformServiceContract = PublicMethodsOf<LegacyPlatformService>;
const createMock = () => {
const mocked: jest.Mocked<LegacyPlatformServiceContract> = {
start: jest.fn(),
setup: jest.fn(),
stop: jest.fn(),
};
return mocked;

View file

@ -151,14 +151,14 @@ import { notificationServiceMock } from '../notifications/notifications_service.
import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock';
import { LegacyPlatformService } from './legacy_service';
const basePathStart = basePathServiceMock.createStartContract();
const chromeStart = chromeServiceMock.createStartContract();
const fatalErrorsStart = fatalErrorsServiceMock.createStartContract();
const httpStart = httpServiceMock.createStartContract();
const i18nStart = i18nServiceMock.createStartContract();
const injectedMetadataStart = injectedMetadataServiceMock.createStartContract();
const notificationsStart = notificationServiceMock.createStartContract();
const uiSettingsStart = uiSettingsServiceMock.createStartContract();
const basePathSetup = basePathServiceMock.createSetupContract();
const chromeSetup = chromeServiceMock.createSetupContract();
const fatalErrorsSetup = fatalErrorsServiceMock.createSetupContract();
const httpSetup = httpServiceMock.createSetupContract();
const i18nSetup = i18nServiceMock.createSetupContract();
const injectedMetadataSetup = injectedMetadataServiceMock.createSetupContract();
const notificationsSetup = notificationServiceMock.createSetupContract();
const uiSettingsSetup = uiSettingsServiceMock.createSetupContract();
const defaultParams = {
targetDomElement: document.createElement('div'),
@ -167,35 +167,35 @@ const defaultParams = {
}),
};
const defaultStartDeps = {
i18n: i18nStart,
fatalErrors: fatalErrorsStart,
injectedMetadata: injectedMetadataStart,
notifications: notificationsStart,
http: httpStart,
basePath: basePathStart,
uiSettings: uiSettingsStart,
chrome: chromeStart,
const defaultSetupDeps = {
i18n: i18nSetup,
fatalErrors: fatalErrorsSetup,
injectedMetadata: injectedMetadataSetup,
notifications: notificationsSetup,
http: httpSetup,
basePath: basePathSetup,
uiSettings: uiSettingsSetup,
chrome: chromeSetup,
};
afterEach(() => {
jest.clearAllMocks();
injectedMetadataStart.getLegacyMetadata.mockReset();
injectedMetadataSetup.getLegacyMetadata.mockReset();
jest.resetModules();
mockLoadOrder.length = 0;
});
describe('#start()', () => {
describe('#setup()', () => {
describe('default', () => {
it('passes legacy metadata from injectedVars to ui/metadata', () => {
const legacyMetadata = { isLegacyMetadata: true };
injectedMetadataStart.getLegacyMetadata.mockReturnValue(legacyMetadata as any);
injectedMetadataSetup.getLegacyMetadata.mockReturnValue(legacyMetadata as any);
const legacyPlatform = new LegacyPlatformService({
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockUiMetadataInit).toHaveBeenCalledTimes(1);
expect(mockUiMetadataInit).toHaveBeenCalledWith(legacyMetadata);
@ -206,10 +206,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockI18nContextInit).toHaveBeenCalledTimes(1);
expect(mockI18nContextInit).toHaveBeenCalledWith(i18nStart.Context);
expect(mockI18nContextInit).toHaveBeenCalledWith(i18nSetup.Context);
});
it('passes fatalErrors service to ui/notify/fatal_errors', () => {
@ -217,10 +217,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockFatalErrorInit).toHaveBeenCalledTimes(1);
expect(mockFatalErrorInit).toHaveBeenCalledWith(fatalErrorsStart);
expect(mockFatalErrorInit).toHaveBeenCalledWith(fatalErrorsSetup);
});
it('passes toasts service to ui/notify/toasts', () => {
@ -228,10 +228,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockNotifyToastsInit).toHaveBeenCalledTimes(1);
expect(mockNotifyToastsInit).toHaveBeenCalledWith(notificationsStart.toasts);
expect(mockNotifyToastsInit).toHaveBeenCalledWith(notificationsSetup.toasts);
});
it('passes http service to ui/chrome/api/loading_count', () => {
@ -239,10 +239,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockHttpInit).toHaveBeenCalledTimes(1);
expect(mockHttpInit).toHaveBeenCalledWith(httpStart);
expect(mockHttpInit).toHaveBeenCalledWith(httpSetup);
});
it('passes basePath service to ui/chrome/api/base_path', () => {
@ -250,10 +250,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockBasePathInit).toHaveBeenCalledTimes(1);
expect(mockBasePathInit).toHaveBeenCalledWith(basePathStart);
expect(mockBasePathInit).toHaveBeenCalledWith(basePathSetup);
});
it('passes basePath service to ui/chrome/api/ui_settings', () => {
@ -261,10 +261,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockUiSettingsInit).toHaveBeenCalledTimes(1);
expect(mockUiSettingsInit).toHaveBeenCalledWith(uiSettingsStart);
expect(mockUiSettingsInit).toHaveBeenCalledWith(uiSettingsSetup);
});
it('passes injectedMetadata service to ui/chrome/api/injected_vars', () => {
@ -272,10 +272,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockInjectedVarsInit).toHaveBeenCalledTimes(1);
expect(mockInjectedVarsInit).toHaveBeenCalledWith(injectedMetadataStart);
expect(mockInjectedVarsInit).toHaveBeenCalledWith(injectedMetadataSetup);
});
it('passes chrome service to ui/chrome/api/controls', () => {
@ -283,10 +283,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockChromeControlsInit).toHaveBeenCalledTimes(1);
expect(mockChromeControlsInit).toHaveBeenCalledWith(chromeStart);
expect(mockChromeControlsInit).toHaveBeenCalledWith(chromeSetup);
});
it('passes chrome service to ui/chrome/api/help_extension', () => {
@ -294,10 +294,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockChromeHelpExtensionInit).toHaveBeenCalledTimes(1);
expect(mockChromeHelpExtensionInit).toHaveBeenCalledWith(chromeStart);
expect(mockChromeHelpExtensionInit).toHaveBeenCalledWith(chromeSetup);
});
it('passes chrome service to ui/chrome/api/theme', () => {
@ -305,10 +305,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockChromeThemeInit).toHaveBeenCalledTimes(1);
expect(mockChromeThemeInit).toHaveBeenCalledWith(chromeStart);
expect(mockChromeThemeInit).toHaveBeenCalledWith(chromeSetup);
});
it('passes chrome service to ui/chrome/api/breadcrumbs', () => {
@ -316,10 +316,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockChromeBreadcrumbsInit).toHaveBeenCalledTimes(1);
expect(mockChromeBreadcrumbsInit).toHaveBeenCalledWith(chromeStart);
expect(mockChromeBreadcrumbsInit).toHaveBeenCalledWith(chromeSetup);
});
it('passes chrome service to ui/chrome/api/global_nav_state', () => {
@ -327,10 +327,10 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockGlobalNavStateInit).toHaveBeenCalledTimes(1);
expect(mockGlobalNavStateInit).toHaveBeenCalledWith(chromeStart);
expect(mockGlobalNavStateInit).toHaveBeenCalledWith(chromeSetup);
});
describe('useLegacyTestHarness = false', () => {
@ -339,7 +339,7 @@ describe('#start()', () => {
...defaultParams,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockUiTestHarnessBootstrap).not.toHaveBeenCalled();
expect(mockUiChromeBootstrap).toHaveBeenCalledTimes(1);
@ -354,7 +354,7 @@ describe('#start()', () => {
useLegacyTestHarness: true,
});
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockUiChromeBootstrap).not.toHaveBeenCalled();
expect(mockUiTestHarnessBootstrap).toHaveBeenCalledTimes(1);
@ -372,7 +372,7 @@ describe('#start()', () => {
expect(mockLoadOrder).toEqual([]);
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockLoadOrder).toMatchSnapshot();
});
@ -387,7 +387,7 @@ describe('#start()', () => {
expect(mockLoadOrder).toEqual([]);
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
expect(mockLoadOrder).toMatchSnapshot();
});

View file

@ -18,25 +18,7 @@
*/
import angular from 'angular';
import { BasePathStart } from '../base_path';
import { ChromeStart } from '../chrome';
import { FatalErrorsStart } from '../fatal_errors';
import { HttpStart } from '../http';
import { I18nStart } from '../i18n';
import { InjectedMetadataStart } from '../injected_metadata';
import { NotificationsStart } from '../notifications';
import { UiSettingsClient } from '../ui_settings';
interface Deps {
i18n: I18nStart;
injectedMetadata: InjectedMetadataStart;
fatalErrors: FatalErrorsStart;
notifications: NotificationsStart;
http: HttpStart;
basePath: BasePathStart;
uiSettings: UiSettingsClient;
chrome: ChromeStart;
}
import { CoreSetup } from '../';
export interface LegacyPlatformParams {
targetDomElement: HTMLElement;
@ -49,12 +31,12 @@ export interface LegacyPlatformParams {
* the legacy platform by injecting parts of the new platform
* services into the legacy platform modules, like ui/modules,
* and then bootstrapping the ui/chrome or ui/test_harness to
* start either the app or browser tests.
* setup either the app or browser tests.
*/
export class LegacyPlatformService {
constructor(private readonly params: LegacyPlatformParams) {}
public start(deps: Deps) {
public setup(core: CoreSetup) {
const {
i18n,
injectedMetadata,
@ -64,10 +46,10 @@ export class LegacyPlatformService {
basePath,
uiSettings,
chrome,
} = deps;
} = core;
// Inject parts of the new platform into parts of the legacy platform
// so that legacy APIs/modules can mimic their new platform counterparts
require('ui/new_platform').__newPlatformInit__(deps);
require('ui/new_platform').__newPlatformInit__(core);
require('ui/metadata').__newPlatformInit__(injectedMetadata.getLegacyMetadata());
require('ui/i18n').__newPlatformInit__(i18n.Context);
require('ui/notify/fatal_error').__newPlatformInit__(fatalErrors);

View file

@ -17,5 +17,5 @@
* under the License.
*/
export { Toast, ToastInput, ToastsStart } from './toasts';
export { NotificationsService, NotificationsStart } from './notifications_service';
export { Toast, ToastInput, ToastsSetup } from './toasts';
export { NotificationsService, NotificationsSetup } from './notifications_service';

View file

@ -16,29 +16,29 @@
* specific language governing permissions and limitations
* under the License.
*/
import { NotificationsService, NotificationsStart } from './notifications_service';
import { NotificationsService, NotificationsSetup } from './notifications_service';
import { toastsServiceMock } from './toasts/toasts_service.mock';
import { ToastsStart } from './toasts/toasts_start';
import { ToastsSetup } from './toasts/toasts_start';
const createStartContractMock = () => {
const startContract: jest.Mocked<NotificationsStart> = {
const createSetupContractMock = () => {
const setupContract: jest.Mocked<NotificationsSetup> = {
// we have to suppress type errors until decide how to mock es6 class
toasts: (toastsServiceMock.createStartContract() as unknown) as ToastsStart,
toasts: (toastsServiceMock.createSetupContract() as unknown) as ToastsSetup,
};
return startContract;
return setupContract;
};
type NotificationsServiceContract = PublicMethodsOf<NotificationsService>;
const createMock = () => {
const mocked: jest.Mocked<NotificationsServiceContract> = {
start: jest.fn(),
setup: jest.fn(),
stop: jest.fn(),
};
mocked.start.mockReturnValue(createStartContractMock());
mocked.setup.mockReturnValue(createSetupContractMock());
return mocked;
};
export const notificationServiceMock = {
create: createMock,
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { I18nStart } from '../i18n';
import { I18nSetup } from '../i18n';
import { ToastsService } from './toasts';
interface Params {
@ -25,7 +25,7 @@ interface Params {
}
interface Deps {
i18n: I18nStart;
i18n: I18nSetup;
}
export class NotificationsService {
@ -40,11 +40,11 @@ export class NotificationsService {
});
}
public start({ i18n }: Deps) {
public setup({ i18n }: Deps) {
this.params.targetDomElement.appendChild(this.toastsContainer);
return {
toasts: this.toasts.start({ i18n }),
toasts: this.toasts.setup({ i18n }),
};
}
@ -55,4 +55,4 @@ export class NotificationsService {
}
}
export type NotificationsStart = ReturnType<NotificationsService['start']>;
export type NotificationsSetup = ReturnType<NotificationsService['setup']>;

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`#start() renders the GlobalToastList into the targetDomElement param 1`] = `
exports[`#setup() renders the GlobalToastList into the targetDomElement param 1`] = `
Array [
Array [
<I18nContext>

View file

@ -18,5 +18,5 @@
*/
export { ToastsService } from './toasts_service';
export { ToastsStart, ToastInput } from './toasts_start';
export { ToastsSetup, ToastInput } from './toasts_start';
export { Toast } from '@elastic/eui';

View file

@ -16,10 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
import { ToastsStart } from './toasts_start';
import { ToastsSetup } from './toasts_start';
const createStartContractMock = () => {
const startContract: jest.Mocked<PublicMethodsOf<ToastsStart>> = {
const createSetupContractMock = () => {
const setupContract: jest.Mocked<PublicMethodsOf<ToastsSetup>> = {
get$: jest.fn(),
add: jest.fn(),
remove: jest.fn(),
@ -27,9 +27,9 @@ const createStartContractMock = () => {
addWarning: jest.fn(),
addDanger: jest.fn(),
};
return startContract;
return setupContract;
};
export const toastsServiceMock = {
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -25,7 +25,7 @@ jest.mock('react-dom', () => ({
}));
import { ToastsService } from './toasts_service';
import { ToastsStart } from './toasts_start';
import { ToastsSetup } from './toasts_start';
const mockI18n: any = {
Context: function I18nContext() {
@ -33,23 +33,23 @@ const mockI18n: any = {
},
};
describe('#start()', () => {
describe('#setup()', () => {
it('renders the GlobalToastList into the targetDomElement param', async () => {
const targetDomElement = document.createElement('div');
targetDomElement.setAttribute('test', 'target-dom-element');
const toasts = new ToastsService({ targetDomElement });
expect(mockReactDomRender).not.toHaveBeenCalled();
toasts.start({ i18n: mockI18n });
toasts.setup({ i18n: mockI18n });
expect(mockReactDomRender.mock.calls).toMatchSnapshot();
});
it('returns a ToastsStart', () => {
it('returns a ToastsSetup', () => {
const toasts = new ToastsService({
targetDomElement: document.createElement('div'),
});
expect(toasts.start({ i18n: mockI18n })).toBeInstanceOf(ToastsStart);
expect(toasts.setup({ i18n: mockI18n })).toBeInstanceOf(ToastsSetup);
});
});
@ -59,14 +59,14 @@ describe('#stop()', () => {
targetDomElement.setAttribute('test', 'target-dom-element');
const toasts = new ToastsService({ targetDomElement });
toasts.start({ i18n: mockI18n });
toasts.setup({ i18n: mockI18n });
expect(mockReactDomUnmount).not.toHaveBeenCalled();
toasts.stop();
expect(mockReactDomUnmount.mock.calls).toMatchSnapshot();
});
it('does not fail if start() was never called', () => {
it('does not fail if setup() was never called', () => {
const targetDomElement = document.createElement('div');
targetDomElement.setAttribute('test', 'target-dom-element');
const toasts = new ToastsService({ targetDomElement });

View file

@ -21,23 +21,23 @@ import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { Toast } from '@elastic/eui';
import { I18nStart } from '../../i18n';
import { I18nSetup } from '../../i18n';
import { GlobalToastList } from './global_toast_list';
import { ToastsStart } from './toasts_start';
import { ToastsSetup } from './toasts_start';
interface Params {
targetDomElement: HTMLElement;
}
interface Deps {
i18n: I18nStart;
i18n: I18nSetup;
}
export class ToastsService {
constructor(private readonly params: Params) {}
public start({ i18n }: Deps) {
const toasts = new ToastsStart();
public setup({ i18n }: Deps) {
const toasts = new ToastsSetup();
render(
<i18n.Context>

View file

@ -19,9 +19,9 @@
import { take } from 'rxjs/operators';
import { ToastsStart } from './toasts_start';
import { ToastsSetup } from './toasts_start';
async function getCurrentToasts(toasts: ToastsStart) {
async function getCurrentToasts(toasts: ToastsSetup) {
return await toasts
.get$()
.pipe(take(1))
@ -30,7 +30,7 @@ async function getCurrentToasts(toasts: ToastsStart) {
describe('#get$()', () => {
it('returns observable that emits NEW toast list when something added or removed', () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
const onToasts = jest.fn();
toasts.get$().subscribe(onToasts);
@ -57,7 +57,7 @@ describe('#get$()', () => {
});
it('does not emit a new toast list when unknown toast is passed to remove()', () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
const onToasts = jest.fn();
toasts.get$().subscribe(onToasts);
@ -71,14 +71,14 @@ describe('#get$()', () => {
describe('#add()', () => {
it('returns toast objects with auto assigned id', () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
const toast = toasts.add({ title: 'foo' });
expect(toast).toHaveProperty('id');
expect(toast).toHaveProperty('title', 'foo');
});
it('adds the toast to toasts list', async () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
const toast = toasts.add({});
const currentToasts = await getCurrentToasts(toasts);
@ -87,27 +87,27 @@ describe('#add()', () => {
});
it('increments the toast ID for each additional toast', () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
expect(toasts.add({})).toHaveProperty('id', '0');
expect(toasts.add({})).toHaveProperty('id', '1');
expect(toasts.add({})).toHaveProperty('id', '2');
});
it('accepts a string, uses it as the title', async () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
expect(toasts.add('foo')).toHaveProperty('title', 'foo');
});
});
describe('#remove()', () => {
it('removes a toast', async () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
toasts.remove(toasts.add('Test'));
expect(await getCurrentToasts(toasts)).toHaveLength(0);
});
it('ignores unknown toast', async () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
toasts.add('Test');
toasts.remove({ id: 'foo' });
@ -118,12 +118,12 @@ describe('#remove()', () => {
describe('#addSuccess()', () => {
it('adds a success toast', async () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
expect(toasts.addSuccess({})).toHaveProperty('color', 'success');
});
it('returns the created toast', async () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
const toast = toasts.addSuccess({});
const currentToasts = await getCurrentToasts(toasts);
expect(currentToasts[0]).toBe(toast);
@ -132,12 +132,12 @@ describe('#addSuccess()', () => {
describe('#addWarning()', () => {
it('adds a warning toast', async () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
expect(toasts.addWarning({})).toHaveProperty('color', 'warning');
});
it('returns the created toast', async () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
const toast = toasts.addWarning({});
const currentToasts = await getCurrentToasts(toasts);
expect(currentToasts[0]).toBe(toast);
@ -146,12 +146,12 @@ describe('#addWarning()', () => {
describe('#addDanger()', () => {
it('adds a danger toast', async () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
expect(toasts.addDanger({})).toHaveProperty('color', 'danger');
});
it('returns the created toast', async () => {
const toasts = new ToastsStart();
const toasts = new ToastsSetup();
const toast = toasts.addDanger({});
const currentToasts = await getCurrentToasts(toasts);
expect(currentToasts[0]).toBe(toast);

View file

@ -32,7 +32,7 @@ const normalizeToast = (toastOrTitle: ToastInput) => {
return toastOrTitle;
};
export class ToastsStart {
export class ToastsSetup {
private toasts$ = new Rx.BehaviorSubject<Toast[]>([]);
private idCounter = 0;

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`#start constructs UiSettingsClient and UiSettingsApi: UiSettingsApi args 1`] = `
exports[`#setup constructs UiSettingsClient and UiSettingsApi: UiSettingsApi args 1`] = `
[MockFunction MockUiSettingsApi] {
"calls": Array [
Array [
@ -21,7 +21,7 @@ exports[`#start constructs UiSettingsClient and UiSettingsApi: UiSettingsApi arg
}
`;
exports[`#start constructs UiSettingsClient and UiSettingsApi: UiSettingsClient args 1`] = `
exports[`#setup constructs UiSettingsClient and UiSettingsApi: UiSettingsClient args 1`] = `
[MockFunction MockUiSettingsClient] {
"calls": Array [
Array [
@ -61,7 +61,7 @@ exports[`#start constructs UiSettingsClient and UiSettingsApi: UiSettingsClient
}
`;
exports[`#start passes the uiSettings loading count to the loading count api: http.addLoadingCount calls 1`] = `
exports[`#setup passes the uiSettings loading count to the loading count api: http.addLoadingCount calls 1`] = `
[MockFunction] {
"calls": Array [
Array [

View file

@ -17,6 +17,6 @@
* under the License.
*/
export { UiSettingsService, UiSettingsStart } from './ui_settings_service';
export { UiSettingsService, UiSettingsSetup } from './ui_settings_service';
export { UiSettingsClient } from './ui_settings_client';
export { UiSettingsState } from './types';

View file

@ -26,7 +26,7 @@ import { basePathServiceMock } from '../base_path/base_path_service.mock';
import { UiSettingsApi } from './ui_settings_api';
function setup() {
const basePath = basePathServiceMock.createStartContract();
const basePath = basePathServiceMock.createSetupContract();
basePath.addToPath.mockImplementation(path => `/foo/bar${path}`);
const uiSettingsApi = new UiSettingsApi(basePath, 'v9.9.9');

View file

@ -19,7 +19,7 @@
import { BehaviorSubject } from 'rxjs';
import { BasePathStart } from '../base_path';
import { BasePathSetup } from '../base_path';
import { UiSettingsState } from './types';
export interface UiSettingsApiResponse {
@ -47,7 +47,7 @@ export class UiSettingsApi {
private readonly loadingCount$ = new BehaviorSubject(0);
constructor(private readonly basePath: BasePathStart, private readonly kibanaVersion: string) {}
constructor(private readonly basePath: BasePathSetup, private readonly kibanaVersion: string) {}
/**
* Adds a key+value that will be sent to the server ASAP. If a request is

View file

@ -16,10 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
import { UiSettingsService, UiSettingsStart } from './ui_settings_service';
import { UiSettingsService, UiSettingsSetup } from './ui_settings_service';
const createStartContractMock = () => {
const startContract: jest.Mocked<PublicMethodsOf<UiSettingsStart>> = {
const createSetupContractMock = () => {
const setupContract: jest.Mocked<PublicMethodsOf<UiSettingsSetup>> = {
getAll: jest.fn(),
get: jest.fn(),
get$: jest.fn(),
@ -35,21 +35,21 @@ const createStartContractMock = () => {
stop: jest.fn(),
};
// we have to suppress type errors until decide how to mock es6 class
return (startContract as unknown) as UiSettingsStart;
return (setupContract as unknown) as UiSettingsSetup;
};
type UiSettingsServiceContract = PublicMethodsOf<UiSettingsService>;
const createMock = () => {
const mocked: jest.Mocked<UiSettingsServiceContract> = {
start: jest.fn(),
setup: jest.fn(),
stop: jest.fn(),
};
mocked.start.mockReturnValue(createStartContractMock());
mocked.setup.mockReturnValue(createSetupContractMock());
return mocked;
};
export const uiSettingsServiceMock = {
create: createMock,
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -62,48 +62,48 @@ import { injectedMetadataServiceMock } from '../injected_metadata/injected_metad
import { notificationServiceMock } from '../notifications/notifications_service.mock';
import { UiSettingsService } from './ui_settings_service';
const httpStart = httpServiceMock.createStartContract();
const httpSetup = httpServiceMock.createSetupContract();
const defaultDeps = {
notifications: notificationServiceMock.createStartContract(),
http: httpStart,
injectedMetadata: injectedMetadataServiceMock.createStartContract(),
basePath: basePathServiceMock.createStartContract(),
notifications: notificationServiceMock.createSetupContract(),
http: httpSetup,
injectedMetadata: injectedMetadataServiceMock.createSetupContract(),
basePath: basePathServiceMock.createSetupContract(),
};
afterEach(() => {
jest.clearAllMocks();
});
describe('#start', () => {
describe('#setup', () => {
it('returns an instance of UiSettingsClient', () => {
const start = new UiSettingsService().start(defaultDeps);
expect(start).toBeInstanceOf(MockUiSettingsClient);
const setup = new UiSettingsService().setup(defaultDeps);
expect(setup).toBeInstanceOf(MockUiSettingsClient);
});
it('constructs UiSettingsClient and UiSettingsApi', () => {
new UiSettingsService().start(defaultDeps);
new UiSettingsService().setup(defaultDeps);
expect(MockUiSettingsApi).toMatchSnapshot('UiSettingsApi args');
expect(MockUiSettingsClient).toMatchSnapshot('UiSettingsClient args');
});
it('passes the uiSettings loading count to the loading count api', () => {
new UiSettingsService().start(defaultDeps);
new UiSettingsService().setup(defaultDeps);
expect(httpStart.addLoadingCount).toMatchSnapshot('http.addLoadingCount calls');
expect(httpSetup.addLoadingCount).toMatchSnapshot('http.addLoadingCount calls');
});
});
describe('#stop', () => {
it('runs fine if service never started', () => {
it('runs fine if service never set up', () => {
const service = new UiSettingsService();
expect(() => service.stop()).not.toThrowError();
});
it('stops the uiSettingsClient and uiSettingsApi', () => {
const service = new UiSettingsService();
const client = service.start(defaultDeps);
const client = service.setup(defaultDeps);
const [[{ api }]] = MockUiSettingsClient.mock.calls;
jest.spyOn(client, 'stop');
jest.spyOn(api, 'stop');

View file

@ -17,10 +17,10 @@
* under the License.
*/
import { BasePathStart } from '../base_path';
import { HttpStart } from '../http';
import { InjectedMetadataStart } from '../injected_metadata';
import { NotificationsStart } from '../notifications';
import { BasePathSetup } from '../base_path';
import { HttpSetup } from '../http';
import { InjectedMetadataSetup } from '../injected_metadata';
import { NotificationsSetup } from '../notifications';
import { UiSettingsApi } from './ui_settings_api';
import { UiSettingsClient } from './ui_settings_client';
@ -28,17 +28,17 @@ import { UiSettingsClient } from './ui_settings_client';
import { i18n } from '@kbn/i18n';
interface Deps {
notifications: NotificationsStart;
http: HttpStart;
injectedMetadata: InjectedMetadataStart;
basePath: BasePathStart;
notifications: NotificationsSetup;
http: HttpSetup;
injectedMetadata: InjectedMetadataSetup;
basePath: BasePathSetup;
}
export class UiSettingsService {
private uiSettingsApi?: UiSettingsApi;
private uiSettingsClient?: UiSettingsClient;
public start({ notifications, http, injectedMetadata, basePath }: Deps): UiSettingsStart {
public setup({ notifications, http, injectedMetadata, basePath }: Deps): UiSettingsSetup {
this.uiSettingsApi = new UiSettingsApi(basePath, injectedMetadata.getKibanaVersion());
http.addLoadingCount(this.uiSettingsApi.getLoadingCount$());
@ -73,4 +73,4 @@ export class UiSettingsService {
}
}
export type UiSettingsStart = UiSettingsClient;
export type UiSettingsSetup = UiSettingsClient;

View file

@ -1,10 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`does not fail on "start" if there are unused paths detected: unused paths logs 1`] = `
exports[`does not fail on "setup" if there are unused paths detected: unused paths logs 1`] = `
Object {
"debug": Array [
Array [
"starting server",
"setting up server",
],
],
"error": Array [],

View file

@ -47,6 +47,11 @@ interface BootstrapArgs {
features: KibanaFeatures;
}
/**
* @interal
*
* @param options
*/
export async function bootstrap({
configs,
cliArgs,
@ -78,7 +83,7 @@ export async function bootstrap({
}
try {
await root.start();
await root.setup();
} catch (err) {
await shutdown(err);
}

View file

@ -19,10 +19,10 @@
import { BehaviorSubject } from 'rxjs';
import { ClusterClient } from './cluster_client';
import { ElasticsearchConfig } from './elasticsearch_config';
import { ElasticsearchService, ElasticsearchServiceStart } from './elasticsearch_service';
import { ElasticsearchService, ElasticsearchServiceSetup } from './elasticsearch_service';
const createStartContractMock = () => {
const startContract: ElasticsearchServiceStart = {
const createSetupContractMock = () => {
const setupContract: ElasticsearchServiceSetup = {
legacy: {
config$: new BehaviorSubject({} as ElasticsearchConfig),
},
@ -31,21 +31,21 @@ const createStartContractMock = () => {
adminClient$: new BehaviorSubject({} as ClusterClient),
dataClient$: new BehaviorSubject({} as ClusterClient),
};
return startContract;
return setupContract;
};
type ElasticsearchServiceContract = PublicMethodsOf<ElasticsearchService>;
const createMock = () => {
const mocked: jest.Mocked<ElasticsearchServiceContract> = {
start: jest.fn(),
setup: jest.fn(),
stop: jest.fn(),
};
mocked.start.mockResolvedValue(createStartContractMock());
mocked.setup.mockResolvedValue(createSetupContractMock());
mocked.stop.mockResolvedValue();
return mocked;
};
export const elasticsearchServiceMock = {
create: createMock,
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -54,11 +54,11 @@ beforeEach(() => {
afterEach(() => jest.clearAllMocks());
describe('#start', () => {
describe('#setup', () => {
test('returns legacy Elasticsearch config as a part of the contract', async () => {
const startContract = await elasticsearchService.start();
const setupContract = await elasticsearchService.setup();
await expect(startContract.legacy.config$.pipe(first()).toPromise()).resolves.toBeInstanceOf(
await expect(setupContract.legacy.config$.pipe(first()).toPromise()).resolves.toBeInstanceOf(
ElasticsearchConfig
);
});
@ -70,12 +70,12 @@ describe('#start', () => {
() => mockAdminClusterClientInstance
).mockImplementationOnce(() => mockDataClusterClientInstance);
const startContract = await elasticsearchService.start();
const setupContract = await elasticsearchService.setup();
const [esConfig, adminClient, dataClient] = await combineLatest(
startContract.legacy.config$,
startContract.adminClient$,
startContract.dataClient$
setupContract.legacy.config$,
setupContract.adminClient$,
setupContract.dataClient$
)
.pipe(first())
.toPromise();
@ -100,13 +100,13 @@ describe('#start', () => {
});
test('returns `createClient` as a part of the contract', async () => {
const startContract = await elasticsearchService.start();
const setupContract = await elasticsearchService.setup();
const mockClusterClientInstance = { close: jest.fn() };
MockClusterClient.mockImplementation(() => mockClusterClientInstance);
const mockConfig = { logQueries: true };
const clusterClient = startContract.createClient('some-custom-type', mockConfig as any);
const clusterClient = setupContract.createClient('some-custom-type', mockConfig as any);
expect(clusterClient).toBe(mockClusterClientInstance);
@ -125,7 +125,7 @@ describe('#stop', () => {
() => mockAdminClusterClientInstance
).mockImplementationOnce(() => mockDataClusterClientInstance);
await elasticsearchService.start();
await elasticsearchService.setup();
await elasticsearchService.stop();
expect(mockAdminClusterClientInstance.close).toHaveBeenCalledTimes(1);

View file

@ -32,7 +32,7 @@ interface CoreClusterClients {
dataClient: ClusterClient;
}
export interface ElasticsearchServiceStart {
export interface ElasticsearchServiceSetup {
// Required for the BWC with the legacy Kibana only.
readonly legacy: {
readonly config$: Observable<ElasticsearchConfig>;
@ -44,7 +44,7 @@ export interface ElasticsearchServiceStart {
}
/** @internal */
export class ElasticsearchService implements CoreService<ElasticsearchServiceStart> {
export class ElasticsearchService implements CoreService<ElasticsearchServiceSetup> {
private readonly log: Logger;
private subscription?: Subscription;
@ -52,8 +52,8 @@ export class ElasticsearchService implements CoreService<ElasticsearchServiceSta
this.log = coreContext.logger.get('elasticsearch-service');
}
public async start(): Promise<ElasticsearchServiceStart> {
this.log.debug('Starting elasticsearch service');
public async setup(): Promise<ElasticsearchServiceSetup> {
this.log.debug('Setting up elasticsearch service');
const clients$ = this.coreContext.configService
.atPath('elasticsearch', ElasticsearchConfig)

View file

@ -17,7 +17,7 @@
* under the License.
*/
export { ElasticsearchServiceStart } from './elasticsearch_service';
export { ElasticsearchServiceSetup } from './elasticsearch_service';
export { CallAPIOptions, ClusterClient } from './cluster_client';
import { CoreContext } from '../core_context';

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`logs error if already started 1`] = `
exports[`logs error if already set up 1`] = `
Object {
"debug": Array [],
"error": Array [],
@ -32,7 +32,7 @@ Object {
}
`;
exports[`throws if registering route handler after http server is started 1`] = `
exports[`throws if registering route handler after http server is set up 1`] = `
Object {
"debug": Array [],
"error": Array [

View file

@ -19,27 +19,27 @@
import { Server, ServerOptions } from 'hapi';
import { HttpService } from './http_service';
const createStartContractMock = () => {
const startContract = {
const createSetupContractMock = () => {
const setupContract = {
// we can mock some hapi server method when we need it
server: {} as Server,
options: {} as ServerOptions,
};
return startContract;
return setupContract;
};
type HttpSericeContract = PublicMethodsOf<HttpService>;
type HttpServiceContract = PublicMethodsOf<HttpService>;
const createHttpServiceMock = () => {
const mocked: jest.Mocked<HttpSericeContract> = {
start: jest.fn(),
const mocked: jest.Mocked<HttpServiceContract> = {
setup: jest.fn(),
stop: jest.fn(),
registerRouter: jest.fn(),
};
mocked.start.mockResolvedValue(createStartContractMock());
mocked.setup.mockResolvedValue(createSetupContractMock());
return mocked;
};
export const httpServiceMock = {
create: createHttpServiceMock,
createStartContract: createStartContractMock,
createSetupContract: createSetupContractMock,
};

View file

@ -34,7 +34,7 @@ afterEach(() => {
jest.clearAllMocks();
});
test('creates and starts http server', async () => {
test('creates and sets up http server', async () => {
const config = {
host: 'example.org',
port: 1234,
@ -55,12 +55,12 @@ test('creates and starts http server', async () => {
expect(mockHttpServer.mock.instances.length).toBe(1);
expect(httpServer.start).not.toHaveBeenCalled();
await service.start();
await service.setup();
expect(httpServer.start).toHaveBeenCalledTimes(1);
});
test('logs error if already started', async () => {
test('logs error if already set up', async () => {
const config = { ssl: {} } as HttpConfig;
const config$ = new BehaviorSubject(config);
@ -74,7 +74,7 @@ test('logs error if already started', async () => {
const service = new HttpService(config$.asObservable(), logger);
await service.start();
await service.setup();
expect(loggingServiceMock.collect(logger)).toMatchSnapshot();
});
@ -93,7 +93,7 @@ test('stops http server', async () => {
const service = new HttpService(config$.asObservable(), logger);
await service.start();
await service.setup();
expect(httpServer.stop).toHaveBeenCalledTimes(0);
@ -125,7 +125,7 @@ test('register route handler', () => {
expect(loggingServiceMock.collect(logger)).toMatchSnapshot();
});
test('throws if registering route handler after http server is started', () => {
test('throws if registering route handler after http server is set up', () => {
const config = {} as HttpConfig;
const config$ = new BehaviorSubject(config);
@ -147,7 +147,7 @@ test('throws if registering route handler after http server is started', () => {
expect(loggingServiceMock.collect(logger)).toMatchSnapshot();
});
test('returns http server contract on start', async () => {
test('returns http server contract on setup', async () => {
const httpServer = {
server: {},
options: { someOption: true },
@ -161,5 +161,5 @@ test('returns http server contract on start', async () => {
const service = new HttpService(new BehaviorSubject({ ssl: {} } as HttpConfig), logger);
expect(await service.start()).toBe(httpServer);
expect(await service.setup()).toBe(httpServer);
});

View file

@ -28,10 +28,10 @@ import { HttpsRedirectServer } from './https_redirect_server';
import { Router } from './router';
/** @internal */
export type HttpServiceStart = HttpServerInfo;
export type HttpServiceSetup = HttpServerInfo;
/** @internal */
export class HttpService implements CoreService<HttpServerInfo> {
export class HttpService implements CoreService<HttpServiceSetup> {
private readonly httpServer: HttpServer;
private readonly httpsRedirectServer: HttpsRedirectServer;
private configSubscription?: Subscription;
@ -45,7 +45,7 @@ export class HttpService implements CoreService<HttpServerInfo> {
this.httpsRedirectServer = new HttpsRedirectServer(logger.get('http', 'redirect', 'server'));
}
public async start() {
public async setup() {
this.configSubscription = this.config$.subscribe(() => {
if (this.httpServer.isListening()) {
// If the server is already running we can't make any config changes
@ -64,6 +64,9 @@ export class HttpService implements CoreService<HttpServerInfo> {
await this.httpsRedirectServer.start(config);
}
// The HttpService's setup method calls `start` on HttpServer because it is
// a more appropriate name. In the future, starting the server should be moved
// to the `start` lifecycle handler of HttpService.
return await this.httpServer.start(config);
}

View file

@ -21,11 +21,11 @@ import { Observable } from 'rxjs';
import { LoggerFactory } from '../logging';
import { HttpConfig } from './http_config';
import { HttpService, HttpServiceStart } from './http_service';
import { HttpService, HttpServiceSetup } from './http_service';
import { Router } from './router';
export { Router, KibanaRequest } from './router';
export { HttpService, HttpServiceStart };
export { HttpService, HttpServiceSetup };
export { HttpServerInfo } from './http_server';
export { BasePathProxyServer } from './base_path_proxy_server';

View file

@ -17,67 +17,12 @@
* under the License.
*/
import { PluginsModule } from './plugins';
export { bootstrap } from './bootstrap';
import { first } from 'rxjs/operators';
import { ConfigService, Env } from './config';
import { ElasticsearchModule } from './elasticsearch';
import { HttpConfig, HttpModule, HttpServerInfo } from './http';
import { LegacyCompatModule } from './legacy';
import { Logger, LoggerFactory } from './logging';
export class Server {
private readonly elasticsearch: ElasticsearchModule;
private readonly http: HttpModule;
private readonly plugins: PluginsModule;
private readonly legacy: LegacyCompatModule;
private readonly log: Logger;
constructor(configService: ConfigService, logger: LoggerFactory, private readonly env: Env) {
this.log = logger.get('server');
this.http = new HttpModule(configService.atPath('server', HttpConfig), logger);
const core = { env, configService, logger };
this.plugins = new PluginsModule(core);
this.legacy = new LegacyCompatModule(core);
this.elasticsearch = new ElasticsearchModule(core);
}
public async start() {
this.log.debug('starting server');
// We shouldn't start http service in two cases:
// 1. If `server.autoListen` is explicitly set to `false`.
// 2. When the process is run as dev cluster master in which case cluster manager
// will fork a dedicated process where http service will be started instead.
let httpStart: HttpServerInfo | undefined;
const httpConfig = await this.http.config$.pipe(first()).toPromise();
if (!this.env.isDevClusterMaster && httpConfig.autoListen) {
httpStart = await this.http.service.start();
}
const elasticsearchServiceStart = await this.elasticsearch.service.start();
const pluginsStart = await this.plugins.service.start({
elasticsearch: elasticsearchServiceStart,
});
await this.legacy.service.start({
elasticsearch: elasticsearchServiceStart,
http: httpStart,
plugins: pluginsStart,
});
}
public async stop() {
this.log.debug('stopping server');
await this.legacy.service.stop();
await this.plugins.service.stop();
await this.elasticsearch.service.stop();
await this.http.service.stop();
}
}
export { CallAPIOptions, ClusterClient } from './elasticsearch';
export { Logger, LoggerFactory } from './logging';
export {
DiscoveredPlugin,
PluginInitializerContext,
PluginName,
PluginSetupContext,
} from './plugins';

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`once LegacyService is started in \`devClusterMaster\` mode creates ClusterManager with base path proxy.: cluster manager with base path proxy 1`] = `
exports[`once LegacyService is set up in \`devClusterMaster\` mode creates ClusterManager with base path proxy.: cluster manager with base path proxy 1`] = `
Array [
Array [
Object {
@ -35,7 +35,7 @@ Array [
"debug": [MockFunction] {
"calls": Array [
Array [
"starting legacy service",
"setting up legacy service",
],
],
"results": Array [
@ -57,7 +57,7 @@ Array [
]
`;
exports[`once LegacyService is started in \`devClusterMaster\` mode creates ClusterManager without base path proxy.: cluster manager without base path proxy 1`] = `
exports[`once LegacyService is set up in \`devClusterMaster\` mode creates ClusterManager without base path proxy.: cluster manager without base path proxy 1`] = `
Array [
Array [
Object {
@ -80,9 +80,9 @@ Array [
]
`;
exports[`once LegacyService is started with connection info creates legacy kbnServer and closes it if \`listen\` fails. 1`] = `"something failed"`;
exports[`once LegacyService is set up with connection info creates legacy kbnServer and closes it if \`listen\` fails. 1`] = `"something failed"`;
exports[`once LegacyService is started with connection info proxy route responds with \`503\` if \`kbnServer\` is not ready yet.: 503 response 1`] = `
exports[`once LegacyService is set up with connection info proxy route responds with \`503\` if \`kbnServer\` is not ready yet.: 503 response 1`] = `
Object {
"body": Array [
Array [
@ -103,7 +103,7 @@ Object {
}
`;
exports[`once LegacyService is started with connection info reconfigures logging configuration if new config is received.: applyLoggingConfiguration params 1`] = `
exports[`once LegacyService is set up with connection info reconfigures logging configuration if new config is received.: applyLoggingConfiguration params 1`] = `
Array [
Array [
Object {
@ -115,7 +115,7 @@ Array [
]
`;
exports[`once LegacyService is started with connection info register proxy route.: proxy route options 1`] = `
exports[`once LegacyService is set up with connection info register proxy route.: proxy route options 1`] = `
Array [
Array [
Object {
@ -135,9 +135,9 @@ Array [
]
`;
exports[`once LegacyService is started with connection info throws if fails to retrieve initial config. 1`] = `"something failed"`;
exports[`once LegacyService is set up with connection info throws if fails to retrieve initial config. 1`] = `"something failed"`;
exports[`once LegacyService is started without connection info reconfigures logging configuration if new config is received.: applyLoggingConfiguration params 1`] = `
exports[`once LegacyService is set up without connection info reconfigures logging configuration if new config is received.: applyLoggingConfiguration params 1`] = `
Array [
Array [
Object {

View file

@ -31,9 +31,10 @@ import KbnServer from '../../../legacy/server/kbn_server';
import { Config, Env, ObjectToConfigAdapter } from '../config';
import { getEnvOptions } from '../config/__mocks__/env';
import { configServiceMock } from '../config/config_service.mock';
import { ElasticsearchServiceStart } from '../elasticsearch';
import { ElasticsearchServiceSetup } from '../elasticsearch';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { PluginsServiceStart } from '../plugins/plugins_service';
import { DiscoveredPlugin, DiscoveredPluginInternal } from '../plugins';
import { PluginsServiceSetup } from '../plugins/plugins_service';
import { LegacyPlatformProxy } from './legacy_platform_proxy';
const MockKbnServer: jest.Mock<KbnServer> = KbnServer as any;
@ -42,10 +43,10 @@ const MockLegacyPlatformProxy: jest.Mock<LegacyPlatformProxy> = LegacyPlatformPr
let legacyService: LegacyService;
let env: Env;
let config$: BehaviorSubject<Config>;
let startDeps: {
elasticsearch: ElasticsearchServiceStart;
let setupDeps: {
elasticsearch: ElasticsearchServiceSetup;
http: any;
plugins: PluginsServiceStart;
plugins: PluginsServiceSetup;
};
const logger = loggingServiceMock.create();
let configService: ReturnType<typeof configServiceMock.create>;
@ -56,13 +57,19 @@ beforeEach(() => {
MockKbnServer.prototype.ready = jest.fn().mockReturnValue(Promise.resolve());
startDeps = {
setupDeps = {
elasticsearch: { legacy: {} } as any,
http: {
server: { listener: { addListener: jest.fn() }, route: jest.fn() },
options: { someOption: 'foo', someAnotherOption: 'bar' },
},
plugins: new Map([['plugin-id', 'plugin-value']]),
plugins: {
contracts: new Map([['plugin-id', 'plugin-value']]),
uiPlugins: {
public: new Map([['plugin-id', {} as DiscoveredPlugin]]),
internal: new Map([['plugin-id', {} as DiscoveredPluginInternal]]),
},
},
};
config$ = new BehaviorSubject<Config>(
@ -82,11 +89,11 @@ afterEach(() => {
jest.clearAllMocks();
});
describe('once LegacyService is started with connection info', () => {
describe('once LegacyService is set up with connection info', () => {
test('register proxy route.', async () => {
await legacyService.start(startDeps);
await legacyService.setup(setupDeps);
expect(startDeps.http.server.route.mock.calls).toMatchSnapshot('proxy route options');
expect(setupDeps.http.server.route.mock.calls).toMatchSnapshot('proxy route options');
});
test('proxy route responds with `503` if `kbnServer` is not ready yet.', async () => {
@ -100,7 +107,7 @@ describe('once LegacyService is started with connection info', () => {
// Wait until listen is called and proxy route is registered, but don't allow
// listen to complete and make kbnServer available.
const legacyStartPromise = legacyService.start(startDeps);
const legacySetupPromise = legacyService.setup(setupDeps);
await kbnServerListen$.pipe(first()).toPromise();
const mockResponse: any = {
@ -113,7 +120,7 @@ describe('once LegacyService is started with connection info', () => {
};
const mockRequest = { raw: { req: { a: 1 }, res: { b: 2 } } };
const [[{ handler }]] = startDeps.http.server.route.mock.calls;
const [[{ handler }]] = setupDeps.http.server.route.mock.calls;
const response503 = await handler(mockRequest, mockResponseToolkit);
expect(response503).toBe(mockResponse);
@ -129,7 +136,7 @@ describe('once LegacyService is started with connection info', () => {
// Now wait until kibana is ready and try to request once again.
kbnServerListen$.complete();
await legacyStartPromise;
await legacySetupPromise;
mockResponseToolkit.response.mockClear();
const responseProxy = await handler(mockRequest, mockResponseToolkit);
@ -148,20 +155,20 @@ describe('once LegacyService is started with connection info', () => {
test('creates legacy kbnServer and calls `listen`.', async () => {
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true }));
await legacyService.start(startDeps);
await legacyService.setup(setupDeps);
expect(MockKbnServer).toHaveBeenCalledTimes(1);
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{
elasticsearch: startDeps.elasticsearch,
elasticsearch: setupDeps.elasticsearch,
serverOptions: {
listener: expect.any(LegacyPlatformProxy),
someAnotherOption: 'bar',
someOption: 'foo',
},
handledConfigPaths: ['foo.bar'],
plugins: startDeps.plugins,
plugins: setupDeps.plugins,
}
);
@ -173,20 +180,20 @@ describe('once LegacyService is started with connection info', () => {
test('creates legacy kbnServer but does not call `listen` if `autoListen: false`.', async () => {
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: false }));
await legacyService.start(startDeps);
await legacyService.setup(setupDeps);
expect(MockKbnServer).toHaveBeenCalledTimes(1);
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{
elasticsearch: startDeps.elasticsearch,
elasticsearch: setupDeps.elasticsearch,
serverOptions: {
listener: expect.any(LegacyPlatformProxy),
someAnotherOption: 'bar',
someOption: 'foo',
},
handledConfigPaths: ['foo.bar'],
plugins: startDeps.plugins,
plugins: setupDeps.plugins,
}
);
@ -200,7 +207,7 @@ describe('once LegacyService is started with connection info', () => {
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true }));
MockKbnServer.prototype.listen.mockRejectedValue(new Error('something failed'));
await expect(legacyService.start(startDeps)).rejects.toThrowErrorMatchingSnapshot();
await expect(legacyService.setup(setupDeps)).rejects.toThrowErrorMatchingSnapshot();
const [mockKbnServer] = MockKbnServer.mock.instances;
expect(mockKbnServer.listen).toHaveBeenCalled();
@ -210,14 +217,14 @@ describe('once LegacyService is started with connection info', () => {
test('throws if fails to retrieve initial config.', async () => {
configService.getConfig$.mockReturnValue(throwError(new Error('something failed')));
await expect(legacyService.start(startDeps)).rejects.toThrowErrorMatchingSnapshot();
await expect(legacyService.setup(setupDeps)).rejects.toThrowErrorMatchingSnapshot();
expect(MockKbnServer).not.toHaveBeenCalled();
expect(MockClusterManager).not.toHaveBeenCalled();
});
test('reconfigures logging configuration if new config is received.', async () => {
await legacyService.start(startDeps);
await legacyService.setup(setupDeps);
const [mockKbnServer] = MockKbnServer.mock.instances as Array<jest.Mocked<KbnServer>>;
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
@ -230,7 +237,7 @@ describe('once LegacyService is started with connection info', () => {
});
test('logs error if re-configuring fails.', async () => {
await legacyService.start(startDeps);
await legacyService.setup(setupDeps);
const [mockKbnServer] = MockKbnServer.mock.instances as Array<jest.Mocked<KbnServer>>;
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
@ -247,7 +254,7 @@ describe('once LegacyService is started with connection info', () => {
});
test('logs error if config service fails.', async () => {
await legacyService.start(startDeps);
await legacyService.setup(setupDeps);
const [mockKbnServer] = MockKbnServer.mock.instances;
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
@ -264,9 +271,9 @@ describe('once LegacyService is started with connection info', () => {
const mockResponseToolkit = { response: jest.fn(), abandon: Symbol('abandon') };
const mockRequest = { raw: { req: { a: 1 }, res: { b: 2 } } };
await legacyService.start(startDeps);
await legacyService.setup(setupDeps);
const [[{ handler }]] = startDeps.http.server.route.mock.calls;
const [[{ handler }]] = setupDeps.http.server.route.mock.calls;
const response = await handler(mockRequest, mockResponseToolkit);
expect(response).toBe(mockResponseToolkit.abandon);
@ -283,25 +290,24 @@ describe('once LegacyService is started with connection info', () => {
});
});
describe('once LegacyService is started without connection info', () => {
beforeEach(
async () =>
await legacyService.start({
elasticsearch: startDeps.elasticsearch,
plugins: startDeps.plugins,
})
);
describe('once LegacyService is set up without connection info', () => {
beforeEach(async () => {
await legacyService.setup({
elasticsearch: setupDeps.elasticsearch,
plugins: setupDeps.plugins,
});
});
test('creates legacy kbnServer with `autoListen: false`.', () => {
expect(startDeps.http.server.route).not.toHaveBeenCalled();
expect(setupDeps.http.server.route).not.toHaveBeenCalled();
expect(MockKbnServer).toHaveBeenCalledTimes(1);
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{
elasticsearch: startDeps.elasticsearch,
elasticsearch: setupDeps.elasticsearch,
serverOptions: { autoListen: false },
handledConfigPaths: ['foo.bar'],
plugins: startDeps.plugins,
plugins: setupDeps.plugins,
}
);
});
@ -318,7 +324,7 @@ describe('once LegacyService is started without connection info', () => {
});
});
describe('once LegacyService is started in `devClusterMaster` mode', () => {
describe('once LegacyService is set up in `devClusterMaster` mode', () => {
beforeEach(() => {
configService.atPath.mockImplementation(path => {
return new BehaviorSubject(
@ -339,9 +345,9 @@ describe('once LegacyService is started in `devClusterMaster` mode', () => {
configService: configService as any,
});
await devClusterLegacyService.start({
elasticsearch: startDeps.elasticsearch,
plugins: new Map(),
await devClusterLegacyService.setup({
elasticsearch: setupDeps.elasticsearch,
plugins: { contracts: new Map(), uiPlugins: { public: new Map(), internal: new Map() } },
});
expect(MockClusterManager.create.mock.calls).toMatchSnapshot(
@ -361,9 +367,9 @@ describe('once LegacyService is started in `devClusterMaster` mode', () => {
configService: configService as any,
});
await devClusterLegacyService.start({
elasticsearch: startDeps.elasticsearch,
plugins: new Map(),
await devClusterLegacyService.setup({
elasticsearch: setupDeps.elasticsearch,
plugins: { contracts: new Map(), uiPlugins: { public: new Map(), internal: new Map() } },
});
expect(MockClusterManager.create.mock.calls).toMatchSnapshot(

View file

@ -24,10 +24,10 @@ import { CoreService } from '../../types';
import { Config } from '../config';
import { CoreContext } from '../core_context';
import { DevConfig } from '../dev';
import { ElasticsearchServiceStart } from '../elasticsearch';
import { BasePathProxyServer, HttpConfig, HttpServiceStart } from '../http';
import { ElasticsearchServiceSetup } from '../elasticsearch';
import { BasePathProxyServer, HttpConfig, HttpServiceSetup } from '../http';
import { Logger } from '../logging';
import { PluginsServiceStart } from '../plugins/plugins_service';
import { PluginsServiceSetup } from '../plugins/plugins_service';
import { LegacyPlatformProxy } from './legacy_platform_proxy';
interface LegacyKbnServer {
@ -37,10 +37,10 @@ interface LegacyKbnServer {
close: () => Promise<void>;
}
interface Deps {
elasticsearch: ElasticsearchServiceStart;
http?: HttpServiceStart;
plugins: PluginsServiceStart;
interface SetupDeps {
elasticsearch: ElasticsearchServiceSetup;
http?: HttpServiceSetup;
plugins: PluginsServiceSetup;
}
function getLegacyRawConfig(config: Config) {
@ -65,8 +65,8 @@ export class LegacyService implements CoreService {
this.log = coreContext.logger.get('legacy-service');
}
public async start(deps: Deps) {
this.log.debug('starting legacy service');
public async setup(deps: SetupDeps) {
this.log.debug('setting up legacy service');
const update$ = this.coreContext.configService.getConfig$().pipe(
tap(config => {
@ -131,7 +131,7 @@ export class LegacyService implements CoreService {
);
}
private async createKbnServer(config: Config, { elasticsearch, http, plugins }: Deps) {
private async createKbnServer(config: Config, { elasticsearch, http, plugins }: SetupDeps) {
const KbnServer = require('../../../legacy/server/kbn_server');
const kbnServer: LegacyKbnServer = new KbnServer(getLegacyRawConfig(config), {
// If core HTTP service is run we'll receive internal server reference and

View file

@ -22,8 +22,10 @@ import { PluginsService } from './plugins_service';
/** @internal */
export { isNewPlatformPlugin } from './discovery';
export { PluginInitializerContext, PluginStartContext } from './plugin_context';
/** @internal */
export { DiscoveredPlugin, DiscoveredPluginInternal } from './plugin';
export { PluginName } from './plugin';
export { PluginInitializerContext, PluginSetupContext } from './plugin_context';
/** @internal */
export class PluginsModule {

View file

@ -24,8 +24,9 @@ import { getEnvOptions } from '../config/__mocks__/env';
import { CoreContext } from '../core_context';
import { elasticsearchServiceMock } from '../elasticsearch/elasticsearch_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { Plugin, PluginManifest } from './plugin';
import { createPluginInitializerContext, createPluginStartContext } from './plugin_context';
import { createPluginInitializerContext, createPluginSetupContext } from './plugin_context';
const mockPluginInitializer = jest.fn();
const logger = loggingServiceMock.create();
@ -58,7 +59,7 @@ function createPluginManifest(manifestProps: Partial<PluginManifest> = {}): Plug
let configService: ConfigService;
let env: Env;
let coreContext: CoreContext;
const startDeps = { elasticsearch: elasticsearchServiceMock.createStartContract() };
const setupDeps = { elasticsearch: elasticsearchServiceMock.createSetupContract() };
beforeEach(() => {
env = Env.createDefault(getEnvOptions());
@ -90,7 +91,7 @@ test('`constructor` correctly initializes plugin instance', () => {
expect(plugin.optionalDependencies).toEqual(['some-optional-dep']);
});
test('`start` fails if `plugin` initializer is not exported', async () => {
test('`setup` fails if `plugin` initializer is not exported', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
'plugin-without-initializer-path',
@ -99,13 +100,13 @@ test('`start` fails if `plugin` initializer is not exported', async () => {
);
await expect(
plugin.start(createPluginStartContext(coreContext, startDeps, plugin), {})
plugin.setup(createPluginSetupContext(coreContext, setupDeps, plugin), {})
).rejects.toMatchInlineSnapshot(
`[Error: Plugin "some-plugin-id" does not export "plugin" definition (plugin-without-initializer-path).]`
);
});
test('`start` fails if plugin initializer is not a function', async () => {
test('`setup` fails if plugin initializer is not a function', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
'plugin-with-wrong-initializer-path',
@ -114,13 +115,13 @@ test('`start` fails if plugin initializer is not a function', async () => {
);
await expect(
plugin.start(createPluginStartContext(coreContext, startDeps, plugin), {})
plugin.setup(createPluginSetupContext(coreContext, setupDeps, plugin), {})
).rejects.toMatchInlineSnapshot(
`[Error: Definition of plugin "some-plugin-id" should be a function (plugin-with-wrong-initializer-path).]`
);
});
test('`start` fails if initializer does not return object', async () => {
test('`setup` fails if initializer does not return object', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
'plugin-with-initializer-path',
@ -131,13 +132,13 @@ test('`start` fails if initializer does not return object', async () => {
mockPluginInitializer.mockReturnValue(null);
await expect(
plugin.start(createPluginStartContext(coreContext, startDeps, plugin), {})
plugin.setup(createPluginSetupContext(coreContext, setupDeps, plugin), {})
).rejects.toMatchInlineSnapshot(
`[Error: Initializer for plugin "some-plugin-id" is expected to return plugin instance, but returned "null".]`
);
});
test('`start` fails if object returned from initializer does not define `start` function', async () => {
test('`setup` fails if object returned from initializer does not define `setup` function', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
'plugin-with-initializer-path',
@ -149,32 +150,32 @@ test('`start` fails if object returned from initializer does not define `start`
mockPluginInitializer.mockReturnValue(mockPluginInstance);
await expect(
plugin.start(createPluginStartContext(coreContext, startDeps, plugin), {})
plugin.setup(createPluginSetupContext(coreContext, setupDeps, plugin), {})
).rejects.toMatchInlineSnapshot(
`[Error: Instance of plugin "some-plugin-id" does not define "start" function.]`
`[Error: Instance of plugin "some-plugin-id" does not define "setup" function.]`
);
});
test('`start` initializes plugin and calls appropriate lifecycle hook', async () => {
test('`setup` initializes plugin and calls appropriate lifecycle hook', async () => {
const manifest = createPluginManifest();
const initializerContext = createPluginInitializerContext(coreContext, manifest);
const plugin = new Plugin('plugin-with-initializer-path', manifest, initializerContext);
const mockPluginInstance = { start: jest.fn().mockResolvedValue({ contract: 'yes' }) };
const mockPluginInstance = { setup: jest.fn().mockResolvedValue({ contract: 'yes' }) };
mockPluginInitializer.mockReturnValue(mockPluginInstance);
const startContext = createPluginStartContext(coreContext, startDeps, plugin);
const startDependencies = { 'some-required-dep': { contract: 'no' } };
await expect(plugin.start(startContext, startDependencies)).resolves.toEqual({ contract: 'yes' });
const setupContext = createPluginSetupContext(coreContext, setupDeps, plugin);
const setupDependencies = { 'some-required-dep': { contract: 'no' } };
await expect(plugin.setup(setupContext, setupDependencies)).resolves.toEqual({ contract: 'yes' });
expect(mockPluginInitializer).toHaveBeenCalledTimes(1);
expect(mockPluginInitializer).toHaveBeenCalledWith(initializerContext);
expect(mockPluginInstance.start).toHaveBeenCalledTimes(1);
expect(mockPluginInstance.start).toHaveBeenCalledWith(startContext, startDependencies);
expect(mockPluginInstance.setup).toHaveBeenCalledTimes(1);
expect(mockPluginInstance.setup).toHaveBeenCalledWith(setupContext, setupDependencies);
});
test('`stop` fails if plugin is not started', async () => {
test('`stop` fails if plugin is not set up', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
'plugin-with-initializer-path',
@ -182,11 +183,11 @@ test('`stop` fails if plugin is not started', async () => {
createPluginInitializerContext(coreContext, manifest)
);
const mockPluginInstance = { start: jest.fn(), stop: jest.fn() };
const mockPluginInstance = { setup: jest.fn(), stop: jest.fn() };
mockPluginInitializer.mockReturnValue(mockPluginInstance);
await expect(plugin.stop()).rejects.toMatchInlineSnapshot(
`[Error: Plugin "some-plugin-id" can't be stopped since it isn't started.]`
`[Error: Plugin "some-plugin-id" can't be stopped since it isn't set up.]`
);
expect(mockPluginInstance.stop).not.toHaveBeenCalled();
});
@ -199,8 +200,8 @@ test('`stop` does nothing if plugin does not define `stop` function', async () =
createPluginInitializerContext(coreContext, manifest)
);
mockPluginInitializer.mockReturnValue({ start: jest.fn() });
await plugin.start(createPluginStartContext(coreContext, startDeps, plugin), {});
mockPluginInitializer.mockReturnValue({ setup: jest.fn() });
await plugin.setup(createPluginSetupContext(coreContext, setupDeps, plugin), {});
await expect(plugin.stop()).resolves.toBeUndefined();
});
@ -213,9 +214,9 @@ test('`stop` calls `stop` defined by the plugin instance', async () => {
createPluginInitializerContext(coreContext, manifest)
);
const mockPluginInstance = { start: jest.fn(), stop: jest.fn() };
const mockPluginInstance = { setup: jest.fn(), stop: jest.fn() };
mockPluginInitializer.mockReturnValue(mockPluginInstance);
await plugin.start(createPluginStartContext(coreContext, startDeps, plugin), {});
await plugin.setup(createPluginSetupContext(coreContext, setupDeps, plugin), {});
await expect(plugin.stop()).resolves.toBeUndefined();
expect(mockPluginInstance.stop).toHaveBeenCalledTimes(1);

View file

@ -21,7 +21,7 @@ import { join } from 'path';
import typeDetect from 'type-detect';
import { ConfigPath } from '../config';
import { Logger } from '../logging';
import { PluginInitializerContext, PluginStartContext } from './plugin_context';
import { PluginInitializerContext, PluginSetupContext } from './plugin_context';
/**
* Dedicated type for plugin name/id that is supposed to make Map/Set/Arrays
@ -80,10 +80,55 @@ export interface PluginManifest {
readonly server: boolean;
}
type PluginInitializer<TExposed, TDependencies extends Record<PluginName, unknown>> = (
/**
* Small container object used to expose information about discovered plugins that may
* or may not have been started.
* @internal
*/
export interface DiscoveredPlugin {
/**
* Identifier of the plugin.
*/
readonly id: PluginName;
/**
* Root configuration path used by the plugin, defaults to "id".
*/
readonly configPath: ConfigPath;
/**
* An optional list of the other plugins that **must be** installed and enabled
* for this plugin to function properly.
*/
readonly requiredPlugins: ReadonlyArray<PluginName>;
/**
* An optional list of the other plugins that if installed and enabled **may be**
* leveraged by this plugin for some additional functionality but otherwise are
* not required for this plugin to work properly.
*/
readonly optionalPlugins: ReadonlyArray<PluginName>;
}
/**
* An extended `DiscoveredPlugin` that exposes more sensitive information. Should never
* be exposed to client-side code.
* @internal
*/
export interface DiscoveredPluginInternal extends DiscoveredPlugin {
/**
* Path on the filesystem where plugin was loaded from.
*/
readonly path: string;
}
type PluginInitializer<TExposedSetup, TDependenciesSetup extends Record<PluginName, unknown>> = (
coreContext: PluginInitializerContext
) => {
start: (pluginStartContext: PluginStartContext, dependencies: TDependencies) => TExposed;
setup: (
pluginSetupContext: PluginSetupContext,
dependencies: TDependenciesSetup
) => TExposedSetup;
stop?: () => void;
};
@ -93,8 +138,8 @@ type PluginInitializer<TExposed, TDependencies extends Record<PluginName, unknow
* @internal
*/
export class Plugin<
TStart = unknown,
TDependencies extends Record<PluginName, unknown> = Record<PluginName, unknown>
TSetup = unknown,
TDependenciesSetup extends Record<PluginName, unknown> = Record<PluginName, unknown>
> {
public readonly name: PluginManifest['id'];
public readonly configPath: PluginManifest['configPath'];
@ -105,11 +150,11 @@ export class Plugin<
private readonly log: Logger;
private instance?: ReturnType<PluginInitializer<TStart, TDependencies>>;
private instance?: ReturnType<PluginInitializer<TSetup, TDependenciesSetup>>;
constructor(
public readonly path: string,
private readonly manifest: PluginManifest,
public readonly manifest: PluginManifest,
private readonly initializerContext: PluginInitializerContext
) {
this.log = initializerContext.logger.get();
@ -122,18 +167,18 @@ export class Plugin<
}
/**
* Instantiates plugin and calls `start` function exposed by the plugin initializer.
* @param startContext Context that consists of various core services tailored specifically
* for the `start` lifecycle event.
* Instantiates plugin and calls `setup` function exposed by the plugin initializer.
* @param setupContext Context that consists of various core services tailored specifically
* for the `setup` lifecycle event.
* @param dependencies The dictionary where the key is the dependency name and the value
* is the contract returned by the dependency's `start` function.
* is the contract returned by the dependency's `setup` function.
*/
public async start(startContext: PluginStartContext, dependencies: TDependencies) {
public async setup(setupContext: PluginSetupContext, dependencies: TDependenciesSetup) {
this.instance = this.createPluginInstance();
this.log.info('Starting plugin');
this.log.info('Setting up plugin');
return await this.instance.start(startContext, dependencies);
return await this.instance.setup(setupContext, dependencies);
}
/**
@ -141,7 +186,7 @@ export class Plugin<
*/
public async stop() {
if (this.instance === undefined) {
throw new Error(`Plugin "${this.name}" can't be stopped since it isn't started.`);
throw new Error(`Plugin "${this.name}" can't be stopped since it isn't set up.`);
}
this.log.info('Stopping plugin');
@ -162,7 +207,7 @@ export class Plugin<
}
const { plugin: initializer } = pluginDefinition as {
plugin: PluginInitializer<TStart, TDependencies>;
plugin: PluginInitializer<TSetup, TDependenciesSetup>;
};
if (!initializer || typeof initializer !== 'function') {
throw new Error(`Definition of plugin "${this.name}" should be a function (${this.path}).`);
@ -177,8 +222,8 @@ export class Plugin<
);
}
if (typeof instance.start !== 'function') {
throw new Error(`Instance of plugin "${this.name}" does not define "start" function.`);
if (typeof instance.setup !== 'function') {
throw new Error(`Instance of plugin "${this.name}" does not define "setup" function.`);
}
return instance;

View file

@ -24,7 +24,7 @@ import { CoreContext } from '../core_context';
import { ClusterClient } from '../elasticsearch';
import { LoggerFactory } from '../logging';
import { Plugin, PluginManifest } from './plugin';
import { PluginsServiceStartDeps } from './plugins_service';
import { PluginsServiceSetupDeps } from './plugins_service';
/**
* Context that's available to plugins during initialization stage.
@ -43,9 +43,9 @@ export interface PluginInitializerContext {
}
/**
* Context passed to the plugins `start` method.
* Context passed to the plugins `setup` method.
*/
export interface PluginStartContext {
export interface PluginSetupContext {
elasticsearch: {
adminClient$: Observable<ClusterClient>;
dataClient$: Observable<ClusterClient>;
@ -106,24 +106,24 @@ export function createPluginInitializerContext(
}
/**
* This returns a facade for `CoreContext` that will be exposed to the plugin `start` method.
* This facade should be safe to use only within `start` itself.
* This returns a facade for `CoreContext` that will be exposed to the plugin `setup` method.
* This facade should be safe to use only within `setup` itself.
*
* This is called for each plugin when it's started, so each plugin gets its own
* This is called for each plugin when it's set up, so each plugin gets its own
* version of these values.
*
* We should aim to be restrictive and specific in the APIs that we expose.
*
* @param coreContext Kibana core context
* @param plugin The plugin we're building these values for.
* @param deps Dependencies that Plugins services gets during start.
* @param deps Dependencies that Plugins services gets during setup.
* @internal
*/
export function createPluginStartContext<TPlugin, TPluginDependencies>(
export function createPluginSetupContext<TPlugin, TPluginDependencies>(
coreContext: CoreContext,
deps: PluginsServiceStartDeps,
deps: PluginsServiceSetupDeps,
plugin: Plugin<TPlugin, TPluginDependencies>
): PluginStartContext {
): PluginSetupContext {
return {
elasticsearch: {
adminClient$: deps.elasticsearch.adminClient$,

View file

@ -43,7 +43,7 @@ let pluginsService: PluginsService;
let configService: ConfigService;
let env: Env;
let mockPluginSystem: jest.Mocked<PluginsSystem>;
const startDeps = { elasticsearch: elasticsearchServiceMock.createStartContract() };
const setupDeps = { elasticsearch: elasticsearchServiceMock.createSetupContract() };
const logger = loggingServiceMock.create();
beforeEach(() => {
mockPackage.raw = {
@ -72,13 +72,13 @@ afterEach(() => {
jest.clearAllMocks();
});
test('`start` throws if plugin has an invalid manifest', async () => {
test('`setup` throws if plugin has an invalid manifest', async () => {
mockDiscover.mockReturnValue({
error$: from([PluginDiscoveryError.invalidManifest('path-1', new Error('Invalid JSON'))]),
plugin$: from([]),
});
await expect(pluginsService.start(startDeps)).rejects.toMatchInlineSnapshot(`
await expect(pluginsService.setup(setupDeps)).rejects.toMatchInlineSnapshot(`
[Error: Failed to initialize plugins:
Invalid JSON (invalid-manifest, path-1)]
`);
@ -91,7 +91,7 @@ Array [
`);
});
test('`start` throws if plugin required Kibana version is incompatible with the current version', async () => {
test('`setup` throws if plugin required Kibana version is incompatible with the current version', async () => {
mockDiscover.mockReturnValue({
error$: from([
PluginDiscoveryError.incompatibleVersion('path-3', new Error('Incompatible version')),
@ -99,7 +99,7 @@ test('`start` throws if plugin required Kibana version is incompatible with the
plugin$: from([]),
});
await expect(pluginsService.start(startDeps)).rejects.toMatchInlineSnapshot(`
await expect(pluginsService.setup(setupDeps)).rejects.toMatchInlineSnapshot(`
[Error: Failed to initialize plugins:
Incompatible version (incompatible-version, path-3)]
`);
@ -112,7 +112,7 @@ Array [
`);
});
test('`start` throws if discovered plugins with conflicting names', async () => {
test('`setup` throws if discovered plugins with conflicting names', async () => {
mockDiscover.mockReturnValue({
error$: from([]),
plugin$: from([
@ -147,20 +147,21 @@ test('`start` throws if discovered plugins with conflicting names', async () =>
]),
});
await expect(pluginsService.start(startDeps)).rejects.toMatchInlineSnapshot(
await expect(pluginsService.setup(setupDeps)).rejects.toMatchInlineSnapshot(
`[Error: Plugin with id "conflicting-id" is already registered!]`
);
expect(mockPluginSystem.addPlugin).not.toHaveBeenCalled();
expect(mockPluginSystem.startPlugins).not.toHaveBeenCalled();
expect(mockPluginSystem.setupPlugins).not.toHaveBeenCalled();
});
test('`start` properly detects plugins that should be disabled.', async () => {
test('`setup` properly detects plugins that should be disabled.', async () => {
jest
.spyOn(configService, 'isEnabledAtPath')
.mockImplementation(path => Promise.resolve(!path.includes('disabled')));
mockPluginSystem.startPlugins.mockResolvedValue(new Map());
mockPluginSystem.setupPlugins.mockResolvedValue(new Map());
mockPluginSystem.uiPlugins.mockReturnValue({ public: new Map(), internal: new Map() });
mockDiscover.mockReturnValue({
error$: from([]),
@ -224,10 +225,14 @@ test('`start` properly detects plugins that should be disabled.', async () => {
]),
});
expect(await pluginsService.start(startDeps)).toBeInstanceOf(Map);
const start = await pluginsService.setup(setupDeps);
expect(start.contracts).toBeInstanceOf(Map);
expect(start.uiPlugins.public).toBeInstanceOf(Map);
expect(start.uiPlugins.internal).toBeInstanceOf(Map);
expect(mockPluginSystem.addPlugin).not.toHaveBeenCalled();
expect(mockPluginSystem.startPlugins).toHaveBeenCalledTimes(1);
expect(mockPluginSystem.startPlugins).toHaveBeenCalledWith(startDeps);
expect(mockPluginSystem.setupPlugins).toHaveBeenCalledTimes(1);
expect(mockPluginSystem.setupPlugins).toHaveBeenCalledWith(setupDeps);
expect(loggingServiceMock.collect(logger).info).toMatchInlineSnapshot(`
Array [
@ -247,7 +252,7 @@ Array [
`);
});
test('`start` properly invokes `discover` and ignores non-critical errors.', async () => {
test('`setup` properly invokes `discover` and ignores non-critical errors.', async () => {
const firstPlugin = new Plugin(
'path-1',
{
@ -287,12 +292,15 @@ test('`start` properly invokes `discover` and ignores non-critical errors.', asy
plugin$: from([firstPlugin, secondPlugin]),
});
const pluginsStart = new Map();
mockPluginSystem.startPlugins.mockResolvedValue(pluginsStart);
const contracts = new Map();
const discoveredPlugins = { public: new Map(), internal: new Map() };
mockPluginSystem.setupPlugins.mockResolvedValue(contracts);
mockPluginSystem.uiPlugins.mockReturnValue(discoveredPlugins);
const start = await pluginsService.start(startDeps);
const setup = await pluginsService.setup(setupDeps);
expect(start).toBe(pluginsStart);
expect(setup.contracts).toBe(contracts);
expect(setup.uiPlugins).toBe(discoveredPlugins);
expect(mockPluginSystem.addPlugin).toHaveBeenCalledTimes(2);
expect(mockPluginSystem.addPlugin).toHaveBeenCalledWith(firstPlugin);
expect(mockPluginSystem.addPlugin).toHaveBeenCalledWith(secondPlugin);

View file

@ -21,23 +21,29 @@ import { Observable } from 'rxjs';
import { filter, first, mergeMap, tap, toArray } from 'rxjs/operators';
import { CoreService } from '../../types';
import { CoreContext } from '../core_context';
import { ElasticsearchServiceStart } from '../elasticsearch';
import { ElasticsearchServiceSetup } from '../elasticsearch/elasticsearch_service';
import { Logger } from '../logging';
import { discover, PluginDiscoveryError, PluginDiscoveryErrorType } from './discovery';
import { Plugin, PluginName } from './plugin';
import { DiscoveredPlugin, DiscoveredPluginInternal, Plugin, PluginName } from './plugin';
import { PluginsConfig } from './plugins_config';
import { PluginsSystem } from './plugins_system';
/** @internal */
export type PluginsServiceStart = Map<PluginName, unknown>;
/** @internal */
export interface PluginsServiceStartDeps {
elasticsearch: ElasticsearchServiceStart;
export interface PluginsServiceSetup {
contracts: Map<PluginName, unknown>;
uiPlugins: {
public: Map<PluginName, DiscoveredPlugin>;
internal: Map<PluginName, DiscoveredPluginInternal>;
};
}
/** @internal */
export class PluginsService implements CoreService<PluginsServiceStart> {
export interface PluginsServiceSetupDeps {
elasticsearch: ElasticsearchServiceSetup;
}
/** @internal */
export class PluginsService implements CoreService<PluginsServiceSetup> {
private readonly log: Logger;
private readonly pluginsSystem: PluginsSystem;
@ -46,8 +52,8 @@ export class PluginsService implements CoreService<PluginsServiceStart> {
this.pluginsSystem = new PluginsSystem(coreContext);
}
public async start(deps: PluginsServiceStartDeps) {
this.log.debug('Starting plugins service');
public async setup(deps: PluginsServiceSetupDeps) {
this.log.debug('Setting up plugins service');
const config = await this.coreContext.configService
.atPath('plugins', PluginsConfig)
@ -60,10 +66,16 @@ export class PluginsService implements CoreService<PluginsServiceStart> {
if (!config.initialize || this.coreContext.env.isDevClusterMaster) {
this.log.info('Plugin initialization disabled.');
return new Map();
return {
contracts: new Map(),
uiPlugins: this.pluginsSystem.uiPlugins(),
};
}
return await this.pluginsSystem.startPlugins(deps);
return {
contracts: await this.pluginsSystem.setupPlugins(deps),
uiPlugins: this.pluginsSystem.uiPlugins(),
};
}
public async stop() {
@ -105,10 +117,7 @@ export class PluginsService implements CoreService<PluginsServiceStart> {
throw new Error(`Plugin with id "${plugin.name}" is already registered!`);
}
pluginEnableStatuses.set(plugin.name, {
plugin,
isEnabled,
});
pluginEnableStatuses.set(plugin.name, { plugin, isEnabled });
})
)
.toPromise();

View file

@ -19,9 +19,9 @@
import { CoreContext } from '../core_context';
const mockCreatePluginStartContext = jest.fn();
const mockCreatePluginSetupContext = jest.fn();
jest.mock('./plugin_context', () => ({
createPluginStartContext: mockCreatePluginStartContext,
createPluginSetupContext: mockCreatePluginSetupContext,
}));
import { BehaviorSubject } from 'rxjs';
@ -61,7 +61,7 @@ let pluginsSystem: PluginsSystem;
let configService: ConfigService;
let env: Env;
let coreContext: CoreContext;
const startDeps = { elasticsearch: elasticsearchServiceMock.createStartContract() };
const setupDeps = { elasticsearch: elasticsearchServiceMock.createSetupContract() };
beforeEach(() => {
env = Env.createDefault(getEnvOptions());
@ -80,48 +80,48 @@ afterEach(() => {
jest.clearAllMocks();
});
test('can be started even without plugins', async () => {
const pluginsStart = await pluginsSystem.startPlugins(startDeps);
test('can be setup even without plugins', async () => {
const pluginsSetup = await pluginsSystem.setupPlugins(setupDeps);
expect(pluginsStart).toBeInstanceOf(Map);
expect(pluginsStart.size).toBe(0);
expect(pluginsSetup).toBeInstanceOf(Map);
expect(pluginsSetup.size).toBe(0);
});
test('`startPlugins` throws plugin has missing required dependency', async () => {
test('`setupPlugins` throws plugin has missing required dependency', async () => {
pluginsSystem.addPlugin(createPlugin('some-id', { required: ['missing-dep'] }));
await expect(pluginsSystem.startPlugins(startDeps)).rejects.toMatchInlineSnapshot(
await expect(pluginsSystem.setupPlugins(setupDeps)).rejects.toMatchInlineSnapshot(
`[Error: Topological ordering of plugins did not complete, these edges could not be ordered: [["some-id",{}]]]`
);
});
test('`startPlugins` throws if plugins have circular required dependency', async () => {
test('`setupPlugins` throws if plugins have circular required dependency', async () => {
pluginsSystem.addPlugin(createPlugin('no-dep'));
pluginsSystem.addPlugin(createPlugin('depends-on-1', { required: ['depends-on-2'] }));
pluginsSystem.addPlugin(createPlugin('depends-on-2', { required: ['depends-on-1'] }));
await expect(pluginsSystem.startPlugins(startDeps)).rejects.toMatchInlineSnapshot(
await expect(pluginsSystem.setupPlugins(setupDeps)).rejects.toMatchInlineSnapshot(
`[Error: Topological ordering of plugins did not complete, these edges could not be ordered: [["depends-on-1",{}],["depends-on-2",{}]]]`
);
});
test('`startPlugins` throws if plugins have circular optional dependency', async () => {
test('`setupPlugins` throws if plugins have circular optional dependency', async () => {
pluginsSystem.addPlugin(createPlugin('no-dep'));
pluginsSystem.addPlugin(createPlugin('depends-on-1', { optional: ['depends-on-2'] }));
pluginsSystem.addPlugin(createPlugin('depends-on-2', { optional: ['depends-on-1'] }));
await expect(pluginsSystem.startPlugins(startDeps)).rejects.toMatchInlineSnapshot(
await expect(pluginsSystem.setupPlugins(setupDeps)).rejects.toMatchInlineSnapshot(
`[Error: Topological ordering of plugins did not complete, these edges could not be ordered: [["depends-on-1",{}],["depends-on-2",{}]]]`
);
});
test('`startPlugins` ignores missing optional dependency', async () => {
test('`setupPlugins` ignores missing optional dependency', async () => {
const plugin = createPlugin('some-id', { optional: ['missing-dep'] });
jest.spyOn(plugin, 'start').mockResolvedValue('test');
jest.spyOn(plugin, 'setup').mockResolvedValue('test');
pluginsSystem.addPlugin(plugin);
expect([...(await pluginsSystem.startPlugins(startDeps))]).toMatchInlineSnapshot(`
expect([...(await pluginsSystem.setupPlugins(setupDeps))]).toMatchInlineSnapshot(`
Array [
Array [
"some-id",
@ -131,7 +131,7 @@ Array [
`);
});
test('`startPlugins` correctly orders plugins and returns exposed values', async () => {
test('`setupPlugins` correctly orders plugins and returns exposed values', async () => {
const plugins = new Map([
[createPlugin('order-4', { required: ['order-2'] }), { 'order-2': 'added-as-2' }],
[createPlugin('order-0'), {}],
@ -146,21 +146,21 @@ test('`startPlugins` correctly orders plugins and returns exposed values', async
],
] as Array<[Plugin, Record<PluginName, unknown>]>);
const startContextMap = new Map();
const setupContextMap = new Map();
[...plugins.keys()].forEach((plugin, index) => {
jest.spyOn(plugin, 'start').mockResolvedValue(`added-as-${index}`);
jest.spyOn(plugin, 'setup').mockResolvedValue(`added-as-${index}`);
startContextMap.set(plugin.name, `start-for-${plugin.name}`);
setupContextMap.set(plugin.name, `setup-for-${plugin.name}`);
pluginsSystem.addPlugin(plugin);
});
mockCreatePluginStartContext.mockImplementation((context, deps, plugin) =>
startContextMap.get(plugin.name)
mockCreatePluginSetupContext.mockImplementation((context, deps, plugin) =>
setupContextMap.get(plugin.name)
);
expect([...(await pluginsSystem.startPlugins(startDeps))]).toMatchInlineSnapshot(`
expect([...(await pluginsSystem.setupPlugins(setupDeps))]).toMatchInlineSnapshot(`
Array [
Array [
"order-0",
@ -186,24 +186,24 @@ Array [
`);
for (const [plugin, deps] of plugins) {
expect(mockCreatePluginStartContext).toHaveBeenCalledWith(coreContext, startDeps, plugin);
expect(plugin.start).toHaveBeenCalledTimes(1);
expect(plugin.start).toHaveBeenCalledWith(startContextMap.get(plugin.name), deps);
expect(mockCreatePluginSetupContext).toHaveBeenCalledWith(coreContext, setupDeps, plugin);
expect(plugin.setup).toHaveBeenCalledTimes(1);
expect(plugin.setup).toHaveBeenCalledWith(setupContextMap.get(plugin.name), deps);
}
});
test('`startPlugins` only starts plugins that have server side', async () => {
test('`setupPlugins` only setups plugins that have server side', async () => {
const firstPluginToRun = createPlugin('order-0');
const secondPluginNotToRun = createPlugin('order-not-run', { server: false });
const thirdPluginToRun = createPlugin('order-1');
[firstPluginToRun, secondPluginNotToRun, thirdPluginToRun].forEach((plugin, index) => {
jest.spyOn(plugin, 'start').mockResolvedValue(`added-as-${index}`);
jest.spyOn(plugin, 'setup').mockResolvedValue(`added-as-${index}`);
pluginsSystem.addPlugin(plugin);
});
expect([...(await pluginsSystem.startPlugins(startDeps))]).toMatchInlineSnapshot(`
expect([...(await pluginsSystem.setupPlugins(setupDeps))]).toMatchInlineSnapshot(`
Array [
Array [
"order-1",
@ -216,19 +216,58 @@ Array [
]
`);
expect(mockCreatePluginStartContext).toHaveBeenCalledWith(
expect(mockCreatePluginSetupContext).toHaveBeenCalledWith(
coreContext,
startDeps,
setupDeps,
firstPluginToRun
);
expect(mockCreatePluginStartContext).not.toHaveBeenCalledWith(coreContext, secondPluginNotToRun);
expect(mockCreatePluginStartContext).toHaveBeenCalledWith(
expect(mockCreatePluginSetupContext).not.toHaveBeenCalledWith(coreContext, secondPluginNotToRun);
expect(mockCreatePluginSetupContext).toHaveBeenCalledWith(
coreContext,
startDeps,
setupDeps,
thirdPluginToRun
);
expect(firstPluginToRun.start).toHaveBeenCalledTimes(1);
expect(secondPluginNotToRun.start).not.toHaveBeenCalled();
expect(thirdPluginToRun.start).toHaveBeenCalledTimes(1);
expect(firstPluginToRun.setup).toHaveBeenCalledTimes(1);
expect(secondPluginNotToRun.setup).not.toHaveBeenCalled();
expect(thirdPluginToRun.setup).toHaveBeenCalledTimes(1);
});
test('`uiPlugins` returns empty Maps before plugins are added', async () => {
expect(pluginsSystem.uiPlugins()).toMatchInlineSnapshot(`
Object {
"internal": Map {},
"public": Map {},
}
`);
});
test('`uiPlugins` returns ordered Maps of all plugin manifests', async () => {
const plugins = new Map([
[createPlugin('order-4', { required: ['order-2'] }), { 'order-2': 'added-as-2' }],
[createPlugin('order-0'), {}],
[
createPlugin('order-2', { required: ['order-1'], optional: ['order-0'] }),
{ 'order-1': 'added-as-3', 'order-0': 'added-as-1' },
],
[createPlugin('order-1', { required: ['order-0'] }), { 'order-0': 'added-as-1' }],
[
createPlugin('order-3', { required: ['order-2'], optional: ['missing-dep'] }),
{ 'order-2': 'added-as-2' },
],
] as Array<[Plugin, Record<PluginName, unknown>]>);
[...plugins.keys()].forEach(plugin => {
pluginsSystem.addPlugin(plugin);
});
expect([...pluginsSystem.uiPlugins().internal.keys()]).toMatchInlineSnapshot(`
Array [
"order-0",
"order-1",
"order-2",
"order-3",
"order-4",
]
`);
});

View file

@ -17,17 +17,20 @@
* under the License.
*/
import { pick } from 'lodash';
import { CoreContext } from '../core_context';
import { Logger } from '../logging';
import { Plugin, PluginName } from './plugin';
import { createPluginStartContext } from './plugin_context';
import { PluginsServiceStartDeps } from './plugins_service';
import { DiscoveredPlugin, DiscoveredPluginInternal, Plugin, PluginName } from './plugin';
import { createPluginSetupContext } from './plugin_context';
import { PluginsServiceSetupDeps } from './plugins_service';
/** @internal */
export class PluginsSystem {
private readonly plugins = new Map<PluginName, Plugin>();
private readonly log: Logger;
private readonly startedPlugins: PluginName[] = [];
// `satup`, the past-tense version of the noun `setup`.
private readonly satupPlugins: PluginName[] = [];
constructor(private readonly coreContext: CoreContext) {
this.log = coreContext.logger.get('plugins-system');
@ -37,14 +40,14 @@ export class PluginsSystem {
this.plugins.set(plugin.name, plugin);
}
public async startPlugins(deps: PluginsServiceStartDeps) {
public async setupPlugins(deps: PluginsServiceSetupDeps) {
const exposedValues = new Map<PluginName, unknown>();
if (this.plugins.size === 0) {
return exposedValues;
}
const sortedPlugins = this.getTopologicallySortedPluginNames();
this.log.info(`Starting [${this.plugins.size}] plugins: [${[...sortedPlugins]}]`);
this.log.info(`Setting up [${this.plugins.size}] plugins: [${[...sortedPlugins]}]`);
for (const pluginName of sortedPlugins) {
const plugin = this.plugins.get(pluginName)!;
@ -52,7 +55,7 @@ export class PluginsSystem {
continue;
}
this.log.debug(`Starting plugin "${pluginName}"...`);
this.log.debug(`Setting up plugin "${pluginName}"...`);
const exposedDependencyValues = [
...plugin.requiredDependencies,
@ -67,34 +70,69 @@ export class PluginsSystem {
exposedValues.set(
pluginName,
await plugin.start(
createPluginStartContext(this.coreContext, deps, plugin),
await plugin.setup(
createPluginSetupContext(this.coreContext, deps, plugin),
exposedDependencyValues
)
);
this.startedPlugins.push(pluginName);
this.satupPlugins.push(pluginName);
}
return exposedValues;
}
public async stopPlugins() {
if (this.plugins.size === 0 || this.startedPlugins.length === 0) {
if (this.plugins.size === 0 || this.satupPlugins.length === 0) {
return;
}
this.log.info(`Stopping all plugins.`);
// Stop plugins in the reverse order of when they were started.
while (this.startedPlugins.length > 0) {
const pluginName = this.startedPlugins.pop()!;
// Stop plugins in the reverse order of when they were set up.
while (this.satupPlugins.length > 0) {
const pluginName = this.satupPlugins.pop()!;
this.log.debug(`Stopping plugin "${pluginName}"...`);
await this.plugins.get(pluginName)!.stop();
}
}
/**
* Get a Map of all discovered UI plugins in topological order.
*/
public uiPlugins() {
const internal = new Map<PluginName, DiscoveredPluginInternal>(
[...this.getTopologicallySortedPluginNames().keys()]
.filter(pluginName => this.plugins.get(pluginName)!.includesUiPlugin)
.map(pluginName => {
const plugin = this.plugins.get(pluginName)!;
return [
pluginName,
{
id: pluginName,
path: plugin.path,
configPath: plugin.manifest.configPath,
requiredPlugins: plugin.manifest.requiredPlugins,
optionalPlugins: plugin.manifest.optionalPlugins,
},
] as [PluginName, DiscoveredPluginInternal];
})
);
const publicPlugins = new Map<PluginName, DiscoveredPlugin>(
[...internal.entries()].map(
([pluginName, plugin]) =>
[
pluginName,
pick(plugin, ['id', 'configPath', 'requiredPlugins', 'optionalPlugins']),
] as [PluginName, DiscoveredPlugin]
)
);
return { public: publicPlugins, internal };
}
/**
* Gets topologically sorted plugin names that are registered with the plugin system.
* Ordering is possible if and only if the plugins graph has no directed cycles,

View file

@ -29,8 +29,8 @@ jest.mock('../config/config_service', () => ({
ConfigService: jest.fn(() => configService),
}));
const mockServer = { start: jest.fn(), stop: jest.fn() };
jest.mock('../', () => ({ Server: jest.fn(() => mockServer) }));
const mockServer = { setup: jest.fn(), stop: jest.fn() };
jest.mock('../server', () => ({ Server: jest.fn(() => mockServer) }));
import { BehaviorSubject } from 'rxjs';
import { filter, first } from 'rxjs/operators';
@ -57,29 +57,29 @@ afterEach(() => {
logger.upgrade.mockReset();
configService.atPath.mockReset();
mockServer.start.mockReset();
mockServer.setup.mockReset();
mockServer.stop.mockReset();
});
test('starts services on "start"', async () => {
test('sets up services on "setup"', async () => {
const root = new Root(config$, env);
expect(logger.upgrade).not.toHaveBeenCalled();
expect(mockServer.start).not.toHaveBeenCalled();
expect(mockServer.setup).not.toHaveBeenCalled();
await root.start();
await root.setup();
expect(logger.upgrade).toHaveBeenCalledTimes(1);
expect(logger.upgrade).toHaveBeenLastCalledWith({ someValue: 'foo' });
expect(mockServer.start).toHaveBeenCalledTimes(1);
expect(mockServer.setup).toHaveBeenCalledTimes(1);
});
test('upgrades logging configuration after start', async () => {
test('upgrades logging configuration after setup', async () => {
const mockLoggingConfig$ = new BehaviorSubject({ someValue: 'foo' });
configService.atPath.mockReturnValue(mockLoggingConfig$);
const root = new Root(config$, env);
await root.start();
await root.setup();
expect(logger.upgrade).toHaveBeenCalledTimes(1);
expect(logger.upgrade).toHaveBeenLastCalledWith({ someValue: 'foo' });
@ -95,7 +95,7 @@ test('stops services on "shutdown"', async () => {
const mockOnShutdown = jest.fn();
const root = new Root(config$, env, mockOnShutdown);
await root.start();
await root.setup();
expect(mockOnShutdown).not.toHaveBeenCalled();
expect(logger.stop).not.toHaveBeenCalled();
@ -113,7 +113,7 @@ test('stops services on "shutdown" an calls `onShutdown` with error passed to `s
const mockOnShutdown = jest.fn();
const root = new Root(config$, env, mockOnShutdown);
await root.start();
await root.setup();
expect(mockOnShutdown).not.toHaveBeenCalled();
expect(logger.stop).not.toHaveBeenCalled();
@ -128,18 +128,18 @@ test('stops services on "shutdown" an calls `onShutdown` with error passed to `s
expect(mockServer.stop).toHaveBeenCalledTimes(1);
});
test('fails and stops services if server fails to start', async () => {
test('fails and stops services if server setup fails', async () => {
const mockOnShutdown = jest.fn();
const root = new Root(config$, env, mockOnShutdown);
const serverError = new Error('server failed');
mockServer.start.mockRejectedValue(serverError);
mockServer.setup.mockRejectedValue(serverError);
expect(mockOnShutdown).not.toHaveBeenCalled();
expect(logger.stop).not.toHaveBeenCalled();
expect(mockServer.stop).not.toHaveBeenCalled();
await expect(root.start()).rejects.toThrowError('server failed');
await expect(root.setup()).rejects.toThrowError('server failed');
expect(mockOnShutdown).toHaveBeenCalledTimes(1);
expect(mockOnShutdown).toHaveBeenCalledWith(serverError);
@ -158,13 +158,13 @@ test('fails and stops services if initial logger upgrade fails', async () => {
expect(mockOnShutdown).not.toHaveBeenCalled();
expect(logger.stop).not.toHaveBeenCalled();
expect(mockServer.start).not.toHaveBeenCalled();
expect(mockServer.setup).not.toHaveBeenCalled();
await expect(root.start()).rejects.toThrowError('logging config upgrade failed');
await expect(root.setup()).rejects.toThrowError('logging config upgrade failed');
expect(mockOnShutdown).toHaveBeenCalledTimes(1);
expect(mockOnShutdown).toHaveBeenCalledWith(loggingUpgradeError);
expect(mockServer.start).not.toHaveBeenCalled();
expect(mockServer.setup).not.toHaveBeenCalled();
expect(logger.stop).toHaveBeenCalledTimes(1);
expect(mockConsoleError.mock.calls).toMatchSnapshot();
@ -181,7 +181,7 @@ test('stops services if consequent logger upgrade fails', async () => {
configService.atPath.mockReturnValue(mockLoggingConfig$);
const root = new Root(config$, env, mockOnShutdown);
await root.start();
await root.setup();
expect(mockOnShutdown).not.toHaveBeenCalled();
expect(logger.stop).not.toHaveBeenCalled();

View file

@ -20,9 +20,9 @@
import { ConnectableObservable, Observable, Subscription } from 'rxjs';
import { first, map, publishReplay, switchMap, tap } from 'rxjs/operators';
import { Server } from '..';
import { Config, ConfigService, Env } from '../config';
import { Logger, LoggerFactory, LoggingConfig, LoggingService } from '../logging';
import { Server } from '../server';
/**
* Top-level entry point to kick off the app and start the Kibana server.
@ -48,12 +48,12 @@ export class Root {
this.server = new Server(this.configService, this.logger, this.env);
}
public async start() {
this.log.debug('starting root');
public async setup() {
this.log.debug('setting up root');
try {
await this.setupLogging();
await this.server.start();
await this.server.setup();
} catch (e) {
await this.shutdown(e);
throw e;

View file

@ -22,7 +22,7 @@ jest.mock('./http/http_service', () => ({
HttpService: jest.fn(() => httpService),
}));
const mockPluginsService = { start: jest.fn(), stop: jest.fn() };
const mockPluginsService = { setup: jest.fn(), stop: jest.fn() };
jest.mock('./plugins/plugins_service', () => ({
PluginsService: jest.fn(() => mockPluginsService),
}));
@ -33,18 +33,18 @@ jest.mock('./elasticsearch/elasticsearch_service', () => ({
ElasticsearchService: jest.fn(() => elasticsearchService),
}));
const mockLegacyService = { start: jest.fn(), stop: jest.fn() };
const mockLegacyService = { setup: jest.fn(), stop: jest.fn() };
jest.mock('./legacy/legacy_service', () => ({
LegacyService: jest.fn(() => mockLegacyService),
}));
import { BehaviorSubject } from 'rxjs';
import { Server } from '.';
import { Env } from './config';
import { getEnvOptions } from './config/__mocks__/env';
import { loggingServiceMock } from './logging/logging_service.mock';
import { Server } from './server';
import { getEnvOptions } from './config/__mocks__/env';
import { configServiceMock } from './config/config_service.mock';
import { loggingServiceMock } from './logging/logging_service.mock';
const configService = configServiceMock.create();
const env = new Env('.', getEnvOptions());
@ -58,77 +58,77 @@ afterEach(() => {
jest.clearAllMocks();
configService.atPath.mockReset();
httpService.start.mockReset();
httpService.setup.mockReset();
httpService.stop.mockReset();
elasticsearchService.start.mockReset();
elasticsearchService.setup.mockReset();
elasticsearchService.stop.mockReset();
mockPluginsService.start.mockReset();
mockPluginsService.setup.mockReset();
mockPluginsService.stop.mockReset();
mockLegacyService.start.mockReset();
mockLegacyService.setup.mockReset();
mockLegacyService.stop.mockReset();
});
test('starts services on "start"', async () => {
const mockPluginsServiceStart = new Map([['some-plugin', 'some-value']]);
mockPluginsService.start.mockReturnValue(Promise.resolve(mockPluginsServiceStart));
test('sets up services on "setup"', async () => {
const mockPluginsServiceSetup = new Map([['some-plugin', 'some-value']]);
mockPluginsService.setup.mockReturnValue(Promise.resolve(mockPluginsServiceSetup));
const server = new Server(configService as any, logger, env);
expect(httpService.start).not.toHaveBeenCalled();
expect(elasticsearchService.start).not.toHaveBeenCalled();
expect(mockPluginsService.start).not.toHaveBeenCalled();
expect(mockLegacyService.start).not.toHaveBeenCalled();
expect(httpService.setup).not.toHaveBeenCalled();
expect(elasticsearchService.setup).not.toHaveBeenCalled();
expect(mockPluginsService.setup).not.toHaveBeenCalled();
expect(mockLegacyService.setup).not.toHaveBeenCalled();
await server.start();
await server.setup();
expect(httpService.start).toHaveBeenCalledTimes(1);
expect(elasticsearchService.start).toHaveBeenCalledTimes(1);
expect(mockPluginsService.start).toHaveBeenCalledTimes(1);
expect(mockLegacyService.start).toHaveBeenCalledTimes(1);
expect(httpService.setup).toHaveBeenCalledTimes(1);
expect(elasticsearchService.setup).toHaveBeenCalledTimes(1);
expect(mockPluginsService.setup).toHaveBeenCalledTimes(1);
expect(mockLegacyService.setup).toHaveBeenCalledTimes(1);
});
test('does not fail on "start" if there are unused paths detected', async () => {
test('does not fail on "setup" if there are unused paths detected', async () => {
configService.getUnusedPaths.mockResolvedValue(['some.path', 'another.path']);
const server = new Server(configService as any, logger, env);
await expect(server.start()).resolves.toBeUndefined();
await expect(server.setup()).resolves.toBeUndefined();
expect(loggingServiceMock.collect(logger)).toMatchSnapshot('unused paths logs');
});
test('does not start http service is `autoListen:false`', async () => {
test('does not setup http service is `autoListen:false`', async () => {
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: false }));
const server = new Server(configService as any, logger, env);
expect(mockLegacyService.start).not.toHaveBeenCalled();
expect(mockLegacyService.setup).not.toHaveBeenCalled();
await server.start();
await server.setup();
expect(httpService.start).not.toHaveBeenCalled();
expect(mockLegacyService.start).toHaveBeenCalledTimes(1);
expect(mockLegacyService.start).toHaveBeenCalledWith({});
expect(httpService.setup).not.toHaveBeenCalled();
expect(mockLegacyService.setup).toHaveBeenCalledTimes(1);
expect(mockLegacyService.setup).toHaveBeenCalledWith({});
});
test('does not start http service if process is dev cluster master', async () => {
test('does not setup http service if process is dev cluster master', async () => {
const server = new Server(
configService as any,
logger,
new Env('.', getEnvOptions({ isDevClusterMaster: true }))
);
expect(mockLegacyService.start).not.toHaveBeenCalled();
expect(mockLegacyService.setup).not.toHaveBeenCalled();
await server.start();
await server.setup();
expect(httpService.start).not.toHaveBeenCalled();
expect(mockLegacyService.start).toHaveBeenCalledTimes(1);
expect(mockLegacyService.start).toHaveBeenCalledWith({});
expect(httpService.setup).not.toHaveBeenCalled();
expect(mockLegacyService.setup).toHaveBeenCalledTimes(1);
expect(mockLegacyService.setup).toHaveBeenCalledWith({});
});
test('stops services on "stop"', async () => {
const server = new Server(configService as any, logger, env);
await server.start();
await server.setup();
expect(httpService.stop).not.toHaveBeenCalled();
expect(elasticsearchService.stop).not.toHaveBeenCalled();

80
src/core/server/server.ts Normal file
View file

@ -0,0 +1,80 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { first } from 'rxjs/operators';
import { ConfigService, Env } from './config';
import { ElasticsearchModule } from './elasticsearch';
import { HttpConfig, HttpModule, HttpServiceSetup } from './http';
import { LegacyCompatModule } from './legacy';
import { Logger, LoggerFactory } from './logging';
import { PluginsModule } from './plugins';
export class Server {
private readonly elasticsearch: ElasticsearchModule;
private readonly http: HttpModule;
private readonly plugins: PluginsModule;
private readonly legacy: LegacyCompatModule;
private readonly log: Logger;
constructor(configService: ConfigService, logger: LoggerFactory, private readonly env: Env) {
this.log = logger.get('server');
this.http = new HttpModule(configService.atPath('server', HttpConfig), logger);
const core = { env, configService, logger };
this.plugins = new PluginsModule(core);
this.legacy = new LegacyCompatModule(core);
this.elasticsearch = new ElasticsearchModule(core);
}
public async setup() {
this.log.debug('setting up server');
// We shouldn't set up http service in two cases:
// 1. If `server.autoListen` is explicitly set to `false`.
// 2. When the process is run as dev cluster master in which case cluster manager
// will fork a dedicated process where http service will be set up instead.
let httpSetup: HttpServiceSetup | undefined;
const httpConfig = await this.http.config$.pipe(first()).toPromise();
if (!this.env.isDevClusterMaster && httpConfig.autoListen) {
httpSetup = await this.http.service.setup();
}
const elasticsearchServiceSetup = await this.elasticsearch.service.setup();
const pluginsSetup = await this.plugins.service.setup({
elasticsearch: elasticsearchServiceSetup,
});
await this.legacy.service.setup({
elasticsearch: elasticsearchServiceSetup,
http: httpSetup,
plugins: pluginsSetup,
});
}
public async stop() {
this.log.debug('stopping server');
await this.legacy.service.stop();
await this.plugins.service.stop();
await this.elasticsearch.service.stop();
await this.http.service.stop();
}
}

View file

@ -18,7 +18,7 @@
*/
/** @internal */
export interface CoreService<TStart = void> {
start(...params: any[]): Promise<TStart>;
export interface CoreService<TSetup = void> {
setup(...params: any[]): Promise<TSetup>;
stop(): Promise<void>;
}

View file

@ -20,8 +20,5 @@
/**
* Use * syntax so that these exports do not break when internal
* types are stripped.
*
* No imports in this directory can import from ./server or ./public
* or else builds will not work correctly for both NodeJS and Webpack.
*/
export * from './core_service';

View file

@ -24,10 +24,6 @@ export const TranspileTypescriptTask = {
description: 'Transpiling sources with typescript compiler',
async run(config, log, build) {
// the types project is built inside the repo so x-pack can use it for it's in-repo build.
const typesProjectRepo = new Project(config.resolveFromRepo('tsconfig.types.json'));
const typesProjectBuild = new Project(build.resolvePath('tsconfig.types.json'));
// these projects are built in the build folder
const defaultProject = new Project(build.resolvePath('tsconfig.json'));
const browserProject = new Project(build.resolvePath('tsconfig.browser.json'));
@ -52,8 +48,6 @@ export const TranspileTypescriptTask = {
}));
const projects = [
typesProjectRepo.tsConfigPath,
typesProjectBuild.tsConfigPath,
// Browser needs to be compiled before server code so that any shared code
// is compiled to the lowest common denominator (server's CommonJS format)
// which can be supported by both environments.

View file

@ -49,5 +49,5 @@ export function lintFiles(log, files) {
}
log.error(cli.getFormatter()(report.results));
throw createFailError(`[eslint] ${failTypes.join(' & ')}`, 1);
throw createFailError(`[eslint] ${failTypes.join(' & ')}`);
}

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