Expose core api types in /public and /server (#32746)

* Expose core api types in /public and /server

* Export CoreStart from core/public

* Export Server and Public from 'kibana'

* Cast frozen object type back to original

The exported type `InjectedMetadataStart` derives it's type from the returned values.
Since it's internal state is frozen the type changes to `ReadOnly<`. However, consumers
of the API shouldn't have to know or care about this type.

* Be more selective with what gets exported

* Fix type imports in tests

* Fix type errors

* Remove src/type_exports.ts

* More remove src/type_exports.ts

* Remove build:types

* Fix bootstrap import

* Expose internal API's at the top level

Exposing the internal API's at the top level of core/public and core/server
makes it obvious that these API's are consumed outside these modules. Marking
these @internal ensures they don't get exported as part of the documentation.

* Fix tests

* Put core/{public/server} in their own namespaces
This commit is contained in:
Rudolf Meijering 2019-03-19 08:35:19 +01:00 committed by GitHub
parent effebd8c05
commit 1ffb261788
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 152 additions and 180 deletions

3
kibana.d.ts vendored
View file

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

View file

@ -67,8 +67,7 @@
"uiFramework:createComponent": "cd packages/kbn-ui-framework && yarn createComponent",
"uiFramework:documentComponent": "cd packages/kbn-ui-framework && yarn documentComponent",
"kbn:watch": "node scripts/kibana --dev --logging.json=false",
"build:types": "tsc --p tsconfig.types.json",
"kbn:bootstrap": "yarn build:types && node scripts/register_git_hook"
"kbn:bootstrap": "node scripts/register_git_hook"
},
"repository": {
"type": "git",

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

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

View file

@ -42,6 +42,8 @@ interface Params {
* 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;

View file

@ -17,4 +17,24 @@
* under the License.
*/
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';
export { CoreSystem } from './core_system';
export interface CoreStart {
i18n: I18nStart;
injectedMetadata: InjectedMetadataStart;
fatalErrors: FatalErrorsStart;
notifications: NotificationsStart;
http: HttpStart;
basePath: BasePathStart;
uiSettings: UiSettingsClient;
chrome: ChromeStart;
}

View file

@ -59,7 +59,9 @@ 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) {}

View file

@ -18,25 +18,7 @@
*/
import angular from 'angular';
import { BasePathStart } from '../base_path';
import { ChromeStart } from '../chrome';
import { FatalErrorsStart } from '../fatal_errors';
import { HttpStart } from '../http';
import { I18nStart } from '../i18n';
import { InjectedMetadataStart } from '../injected_metadata';
import { NotificationsStart } from '../notifications';
import { UiSettingsClient } from '../ui_settings';
interface Deps {
i18n: I18nStart;
injectedMetadata: InjectedMetadataStart;
fatalErrors: FatalErrorsStart;
notifications: NotificationsStart;
http: HttpStart;
basePath: BasePathStart;
uiSettings: UiSettingsClient;
chrome: ChromeStart;
}
import { CoreStart } from '../';
export interface LegacyPlatformParams {
targetDomElement: HTMLElement;
@ -54,7 +36,7 @@ export interface LegacyPlatformParams {
export class LegacyPlatformService {
constructor(private readonly params: LegacyPlatformParams) {}
public start(deps: Deps) {
public start(core: CoreStart) {
const {
i18n,
injectedMetadata,
@ -64,10 +46,10 @@ export class LegacyPlatformService {
basePath,
uiSettings,
chrome,
} = deps;
} = core;
// Inject parts of the new platform into parts of the legacy platform
// so that legacy APIs/modules can mimic their new platform counterparts
require('ui/new_platform').__newPlatformInit__(deps);
require('ui/new_platform').__newPlatformInit__(core);
require('ui/metadata').__newPlatformInit__(injectedMetadata.getLegacyMetadata());
require('ui/i18n').__newPlatformInit__(i18n.Context);
require('ui/notify/fatal_error').__newPlatformInit__(fatalErrors);

View file

@ -47,6 +47,11 @@ interface BootstrapArgs {
features: KibanaFeatures;
}
/**
* @interal
*
* @param options
*/
export async function bootstrap({
configs,
cliArgs,

View file

@ -17,67 +17,7 @@
* 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 { PluginInitializerContext, PluginName, PluginStartContext } from './plugins';

View file

@ -30,7 +30,7 @@ jest.mock('../config/config_service', () => ({
}));
const mockServer = { start: jest.fn(), stop: jest.fn() };
jest.mock('../', () => ({ Server: jest.fn(() => mockServer) }));
jest.mock('../server', () => ({ Server: jest.fn(() => mockServer) }));
import { BehaviorSubject } from 'rxjs';
import { filter, first } from 'rxjs/operators';

View file

@ -20,9 +20,9 @@
import { ConnectableObservable, Observable, Subscription } from 'rxjs';
import { first, map, publishReplay, switchMap, tap } from 'rxjs/operators';
import { Server } from '..';
import { Config, ConfigService, Env } from '../config';
import { Logger, LoggerFactory, LoggingConfig, LoggingService } from '../logging';
import { Server } from '../server';
/**
* Top-level entry point to kick off the app and start the Kibana server.

View file

@ -39,12 +39,12 @@ jest.mock('./legacy/legacy_service', () => ({
}));
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());

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

@ -0,0 +1,80 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { first } from 'rxjs/operators';
import { ConfigService, Env } from './config';
import { ElasticsearchModule } from './elasticsearch';
import { HttpConfig, HttpModule, HttpServerInfo } 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 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();
}
}

View file

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

View file

@ -18,7 +18,7 @@
*/
import { errors } from 'elasticsearch';
import { CallAPIOptions, ClusterClient } from 'kibana';
import { CallAPIOptions, ClusterClient } from 'kibana/server';
export class Cluster {
public readonly errors = errors;

View file

@ -19,7 +19,7 @@
import { map, mergeMap } from 'rxjs/operators';
import { Logger, PluginInitializerContext, PluginName, PluginStartContext } from 'kibana';
import { Logger, PluginInitializerContext, PluginName, PluginStartContext } from 'kibana/server';
import { TestBedConfig } from './config';
class Plugin {

View file

@ -1,65 +0,0 @@
/*
* 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.
*/
/**
* This file re-exports only those Kibana types that we'd like plugins to have access to.
*
* Generated types are referenced from the `types` field of the Kibana's `package.json`, so
* that plugins can just reference Kibana root folder to access all required types.
*
* Here is an example of how plugin can use these types assuming it is located
* in one of the known plugin locations (kibana/plugins/* or kibana-extra/*):
*
* ```ts
* import { Logger, PluginInitializerContext, PluginStartContext } from '../../kibana';
*
* export interface SomePlugin {
* setValue: (val: string) => void;
* }
*
* class Plugin {
* private readonly log: Logger;
*
* constructor(private readonly initializerContext: PluginInitializerContext) {
* this.log = initializerContext.logger.get();
* }
*
* start(startContext: PluginStartContext, deps: Record<string, any>) {
* this.log.info('Hello from plugin!');
*
* let value = 'Hello World!';
*
* const router = startContext.http.createAndRegisterRouter('/some-path');
* router.get('/some-value', (req, res) => res.ok(value));
*
* return { setValue: (val: string) => { value = val; } };
* }
* }
*
* export plugin = (initializerContext: PluginInitializerContext) => new Plugin(initializerContext));
* ```
*
* **NOTE:** If the code is not needed in plugins, we can add a `at_internal` JSDoc
* annotation to that code. And since we've specified the `stripInternal` compiler
* option TypeScript will not emit declarations for this code.
*/
export { CallAPIOptions, ClusterClient } from './core/server/elasticsearch';
export { Logger, LoggerFactory } from './core/server/logging';
export { PluginInitializerContext, PluginName, PluginStartContext } from './core/server/plugins';

View file

@ -4,6 +4,8 @@
"paths": {
// Allows for importing from `kibana` package for the exported types.
"kibana": ["./kibana"],
"kibana/public": ["./src/core/public"],
"kibana/server": ["./src/core/server"],
"ui/*": [
"src/legacy/ui/public/*"
],

View file

@ -1,13 +0,0 @@
{
"extends": "./tsconfig",
"compilerOptions": {
"declaration": true,
"declarationDir": "./target/types",
"stripInternal": true,
"emitDeclarationOnly": true,
"declarationMap": true
},
"include": [
"./src/type_exports.ts"
]
}