mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Merge remote-tracking branch 'origin/master' into feature/merge-code
This commit is contained in:
commit
046c922d2c
377 changed files with 7730 additions and 4890 deletions
|
@ -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',
|
||||
],
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[[canvas-workpad]]
|
||||
=== Using the workpad
|
||||
|
||||
beta[]Now that you have a workpad with sample data that you can mess with, let’s mess with it.
|
||||
Now that you have a workpad with sample data that you can mess with, let’s mess with it.
|
||||
We’ll 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).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
3
kibana.d.ts
vendored
|
@ -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).
|
||||
*/
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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'));
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
23
src/core/index.ts
Normal 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 };
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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']>;
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { BasePathService, BasePathStart } from './base_path_service';
|
||||
export { BasePathService, BasePathSetup } from './base_path_service';
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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']>;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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']>;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { FatalErrorsStart, FatalErrorsService } from './fatal_errors_service';
|
||||
export { FatalErrorsSetup, FatalErrorsService } from './fatal_errors_service';
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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']>;
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { HttpService, HttpStart } from './http_service';
|
||||
export { HttpService, HttpSetup } from './http_service';
|
||||
|
|
|
@ -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={
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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']>;
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { I18nService, I18nStart } from './i18n_service';
|
||||
export { I18nService, I18nSetup } from './i18n_service';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -20,5 +20,5 @@
|
|||
export {
|
||||
InjectedMetadataService,
|
||||
InjectedMetadataParams,
|
||||
InjectedMetadataStart,
|
||||
InjectedMetadataSetup,
|
||||
} from './injected_metadata_service';
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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']>;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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']>;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 [
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 [],
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 [
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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$,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
80
src/core/server/server.ts
Normal 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();
|
||||
}
|
||||
}
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue