[7.x] [NP] Expose global config to the plugins (#51478) (#52263)

* [NP] Expose global config to the plugins (#51478)

* [NP] Expose global config to the plugins

* globalConfig in Plugin context: expose read-only methods only

* SharedGlobalConfig rework + Moving pkg, fromRoot & path utils from legacy to NP

* Updated API docs

* Fix test references to the moved utils

* Replace zip with combineLatest

* Change tests to describe/it + remove "(deprecated)" from the test description

* Moving path files to a folder + exposing the config path in the contract

* deepFreeze the globalConfig in the pluginContext

* Fix types in tests with new path.config

* Move fromRoot and package_json utils to core/server/utils

* Rename globalConfig to legacy.globalConfig$

* path.config renamed to path.configDir (not renaming path.data because it might be a breaking change)

* Change configDir in mocker as well

* Fix test after config renamed to configDir

* Fix API docs conflicts

* Rename the path properties when exposing them

* path.configDir removed from the path config-schema

* Remove path.configDir. It is already in env.configs

* Add Migration documentation and examples

* Fix 'kibana/server' imports in the MIGRATION docs

* Disable eslint complain for the legacy config/schema.js import
This commit is contained in:
Alejandro Fernández Haro 2019-12-05 14:04:47 +00:00 committed by GitHub
parent 8695f6a1b0
commit ea3af98e65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 887 additions and 143 deletions

View file

@ -9,5 +9,5 @@ Search for objects
<b>Signature:</b>
```typescript
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "page" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>>;
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "page" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>>;
```

View file

@ -20,7 +20,7 @@ export declare class SavedObjectsClient
| [bulkGet](./kibana-plugin-public.savedobjectsclient.bulkget.md) | | <code>(objects?: {</code><br/><code> id: string;</code><br/><code> type: string;</code><br/><code> }[]) =&gt; Promise&lt;SavedObjectsBatchResponse&lt;SavedObjectAttributes&gt;&gt;</code> | Returns an array of objects by id |
| [create](./kibana-plugin-public.savedobjectsclient.create.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(type: string, attributes: T, options?: SavedObjectsCreateOptions) =&gt; Promise&lt;SimpleSavedObject&lt;T&gt;&gt;</code> | Persists an object |
| [delete](./kibana-plugin-public.savedobjectsclient.delete.md) | | <code>(type: string, id: string) =&gt; Promise&lt;{}&gt;</code> | Deletes an object |
| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(options: Pick&lt;SavedObjectFindOptionsServer, &quot;search&quot; &#124; &quot;filter&quot; &#124; &quot;type&quot; &#124; &quot;fields&quot; &#124; &quot;searchFields&quot; &#124; &quot;defaultSearchOperator&quot; &#124; &quot;hasReference&quot; &#124; &quot;sortField&quot; &#124; &quot;page&quot; &#124; &quot;perPage&quot;&gt;) =&gt; Promise&lt;SavedObjectsFindResponsePublic&lt;T&gt;&gt;</code> | Search for objects |
| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(options: Pick&lt;SavedObjectFindOptionsServer, &quot;search&quot; &#124; &quot;filter&quot; &#124; &quot;type&quot; &#124; &quot;page&quot; &#124; &quot;fields&quot; &#124; &quot;searchFields&quot; &#124; &quot;defaultSearchOperator&quot; &#124; &quot;hasReference&quot; &#124; &quot;sortField&quot; &#124; &quot;perPage&quot;&gt;) =&gt; Promise&lt;SavedObjectsFindResponsePublic&lt;T&gt;&gt;</code> | Search for objects |
| [get](./kibana-plugin-public.savedobjectsclient.get.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(type: string, id: string) =&gt; Promise&lt;SimpleSavedObject&lt;T&gt;&gt;</code> | Fetches a single object |
## Methods

View file

@ -8,6 +8,9 @@
```typescript
config: {
legacy: {
globalConfig$: Observable<SharedGlobalConfig>;
};
create: <T = ConfigSchema>() => Observable<T>;
createIfExists: <T = ConfigSchema>() => Observable<T | undefined>;
};

View file

@ -16,7 +16,7 @@ export interface PluginInitializerContext<ConfigSchema = unknown>
| Property | Type | Description |
| --- | --- | --- |
| [config](./kibana-plugin-server.plugininitializercontext.config.md) | <code>{</code><br/><code> create: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T&gt;;</code><br/><code> createIfExists: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T &#124; undefined&gt;;</code><br/><code> }</code> | |
| [config](./kibana-plugin-server.plugininitializercontext.config.md) | <code>{</code><br/><code> legacy: {</code><br/><code> globalConfig$: Observable&lt;SharedGlobalConfig&gt;;</code><br/><code> };</code><br/><code> create: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T&gt;;</code><br/><code> createIfExists: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T &#124; undefined&gt;;</code><br/><code> }</code> | |
| [env](./kibana-plugin-server.plugininitializercontext.env.md) | <code>{</code><br/><code> mode: EnvironmentMode;</code><br/><code> packageInfo: Readonly&lt;PackageInfo&gt;;</code><br/><code> }</code> | |
| [logger](./kibana-plugin-server.plugininitializercontext.logger.md) | <code>LoggerFactory</code> | |
| [opaqueId](./kibana-plugin-server.plugininitializercontext.opaqueid.md) | <code>PluginOpaqueId</code> | |

View file

@ -18,7 +18,7 @@
*/
import _ from 'lodash';
import { pkg } from '../legacy/utils';
import { pkg } from '../core/server/utils';
import Command from './command';
import serveCommand from './serve/serve';

View file

@ -167,7 +167,7 @@ export default class ClusterManager {
setupWatching(extraPaths, pluginInternalDirsIgnore) {
const chokidar = require('chokidar');
const { fromRoot } = require('../../legacy/utils');
const { fromRoot } = require('../../core/server/utils');
const watchPaths = [
fromRoot('src/core'),

View file

@ -21,7 +21,8 @@ import _ from 'lodash';
import cluster from 'cluster';
import { EventEmitter } from 'events';
import { BinderFor, fromRoot } from '../../legacy/utils';
import { BinderFor } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
const cliPath = fromRoot('src/cli');
const baseArgs = _.difference(process.argv.slice(2), ['--no-watch']);

View file

@ -21,9 +21,9 @@ import path from 'path';
import { set } from 'lodash';
import { Keystore } from '../../legacy/server/keystore';
import { getData } from '../../legacy/server/path';
import { getDataPath } from '../../core/server/path';
export function readKeystore(dataPath = getData()) {
export function readKeystore(dataPath = getDataPath()) {
const keystore = new Keystore(path.join(dataPath, 'kibana.keystore'));
keystore.load();

View file

@ -22,8 +22,9 @@ import { statSync } from 'fs';
import { resolve } from 'path';
import url from 'url';
import { fromRoot, IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
import { getConfig } from '../../legacy/server/path';
import { IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import { getConfigPath } from '../../core/server/path';
import { bootstrap } from '../../core/server';
import { readKeystore } from './read_keystore';
@ -166,7 +167,7 @@ export default function (program) {
'-c, --config <path>',
'Path to the config file, use multiple --config args to include multiple config files',
configPathCollector,
[ getConfig() ]
[ getConfigPath() ]
)
.option('-p, --port <port>', 'The port to bind to', parseInt)
.option('-q, --quiet', 'Prevent all logging except errors')

View file

@ -19,12 +19,12 @@
import { join } from 'path';
import { pkg } from '../legacy/utils';
import { pkg } from '../core/server/utils';
import Command from '../cli/command';
import { getData } from '../legacy/server/path';
import { getDataPath } from '../core/server/path';
import { Keystore } from '../legacy/server/keystore';
const path = join(getData(), 'kibana.keystore');
const path = join(getDataPath(), 'kibana.keystore');
const keystore = new Keystore(path);
import { createCli } from './create';

View file

@ -18,7 +18,7 @@
*/
import _ from 'lodash';
import { pkg } from '../legacy/utils';
import { pkg } from '../core/server/utils';
import Command from '../cli/command';
import listCommand from './list';
import installCommand from './install';

View file

@ -17,10 +17,10 @@
* under the License.
*/
import { fromRoot, pkg } from '../../legacy/utils';
import { fromRoot, pkg } from '../../core/server/utils';
import install from './install';
import Logger from '../lib/logger';
import { getConfig } from '../../legacy/server/path';
import { getConfigPath } from '../../core/server/path';
import { parse, parseMilliseconds } from './settings';
import logWarnings from '../lib/log_warnings';
import { warnIfUsingPluginDirOption } from '../lib/warn_if_plugin_dir_option';
@ -50,7 +50,7 @@ export default function pluginInstall(program) {
.option(
'-c, --config <path>',
'path to the config file',
getConfig()
getConfigPath()
)
.option(
'-t, --timeout <duration>',

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import { resolve } from 'path';
import { parseMilliseconds, parse } from './settings';

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import list from './list';
import Logger from '../lib/logger';
import { parse } from './settings';

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import { parse } from './settings';
describe('kibana cli', function () {

View file

@ -17,11 +17,11 @@
* under the License.
*/
import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import remove from './remove';
import Logger from '../lib/logger';
import { parse } from './settings';
import { getConfig } from '../../legacy/server/path';
import { getConfigPath } from '../../core/server/path';
import logWarnings from '../lib/log_warnings';
import { warnIfUsingPluginDirOption } from '../lib/warn_if_plugin_dir_option';
@ -50,7 +50,7 @@ export default function pluginRemove(program) {
.option(
'-c, --config <path>',
'path to the config file',
getConfig()
getConfigPath()
)
.option(
'-d, --plugin-dir <path>',

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import { parse } from './settings';
describe('kibana cli', function () {

View file

@ -18,15 +18,15 @@
- [Switch to new platform services](#switch-to-new-platform-services)
- [Migrate to the new plugin system](#migrate-to-the-new-plugin-system)
- [Browser-side plan of action](#browser-side-plan-of-action)
- [1. Create a plugin definition file](#1-create-a-plugin-definition-file)
- [2. Export all static code and types from `public/index.ts`](#2-export-all-static-code-and-types-from-publicindexts)
- [3. Export your runtime contract](#3-export-your-runtime-contract)
- [4. Move "owned" UI modules into your plugin and expose them from your public contract](#4-move-owned-ui-modules-into-your-plugin-and-expose-them-from-your-public-contract)
- [5. Provide plugin extension points decoupled from angular.js](#5-provide-plugin-extension-points-decoupled-from-angularjs)
- [6. Move all webpack alias imports into uiExport entry files](#6-move-all-webpack-alias-imports-into-uiexport-entry-files)
- [7. Switch to new platform services](#7-switch-to-new-platform-services)
- [8. Migrate to the new plugin system](#8-migrate-to-the-new-plugin-system)
- [Bonus: Tips for complex migration scenarios](#bonus-tips-for-complex-migration-scenarios)
- [1. Create a plugin definition file](#1-create-a-plugin-definition-file)
- [2. Export all static code and types from `public/index.ts`](#2-export-all-static-code-and-types-from-publicindexts)
- [3. Export your runtime contract](#3-export-your-runtime-contract)
- [4. Move "owned" UI modules into your plugin and expose them from your public contract](#4-move-owned-ui-modules-into-your-plugin-and-expose-them-from-your-public-contract)
- [5. Provide plugin extension points decoupled from angular.js](#5-provide-plugin-extension-points-decoupled-from-angularjs)
- [6. Move all webpack alias imports into uiExport entry files](#6-move-all-webpack-alias-imports-into-uiexport-entry-files)
- [7. Switch to new platform services](#7-switch-to-new-platform-services)
- [8. Migrate to the new plugin system](#8-migrate-to-the-new-plugin-system)
- [Bonus: Tips for complex migration scenarios](#bonus-tips-for-complex-migration-scenarios)
- [Frequently asked questions](#frequently-asked-questions)
- [Is migrating a plugin an all-or-nothing thing?](#is-migrating-a-plugin-an-all-or-nothing-thing)
- [Do plugins need to be converted to TypeScript?](#do-plugins-need-to-be-converted-to-typescript)
@ -73,7 +73,7 @@ Plugins are defined as classes and exposed to the platform itself through a simp
The basic file structure of a new platform plugin named "demo" that had both client-side and server-side code would be:
```
```tree
src/plugins
demo
kibana.json [1]
@ -85,7 +85,7 @@ src/plugins
plugin.ts [5]
```
**[1] `kibana.json`** is a static manifest file that is used to identify the plugin and to determine what kind of code the platform should execute from the plugin:
**[1] `kibana.json`** is a [static manifest](../../docs/development/core/server/kibana-plugin-server.pluginmanifest.md) file that is used to identify the plugin and to determine what kind of code the platform should execute from the plugin:
```json
{
@ -102,7 +102,7 @@ Note that `package.json` files are irrelevant to and ignored by the new platform
**[2] `public/index.ts`** is the entry point into the client-side code of this plugin. It must export a function named `plugin`, which will receive a standard set of core capabilities as an argument (e.g. logger). It should return an instance of its plugin definition for the platform to register at load time.
```ts
import { PluginInitializerContext } from '../../../core/public';
import { PluginInitializerContext } from 'kibana/server';
import { Plugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
@ -113,7 +113,7 @@ export function plugin(initializerContext: PluginInitializerContext) {
**[3] `public/plugin.ts`** is the client-side plugin definition itself. Technically speaking it does not need to be a class or even a separate file from the entry point, but _all plugins at Elastic_ should be consistent in this way.
```ts
import { PluginInitializerContext, CoreSetup, CoreStart } from '../../../core/public';
import { PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server';
export class Plugin {
constructor(initializerContext: PluginInitializerContext) {
@ -136,7 +136,7 @@ export class Plugin {
**[4] `server/index.ts`** is the entry-point into the server-side code of this plugin. It is identical in almost every way to the client-side entry-point:
```ts
import { PluginInitializerContext } from '../../../core/server';
import { PluginInitializerContext } from 'kibana/server';
import { Plugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
@ -147,7 +147,7 @@ export function plugin(initializerContext: PluginInitializerContext) {
**[5] `server/plugin.ts`** is the server-side plugin definition. The _shape_ of this plugin is the same as it's client-side counter-part:
```ts
import { PluginInitializerContext, CoreSetup, CoreStart } from '../../../core/server';
import { PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server';
export class Plugin {
constructor(initializerContext: PluginInitializerContext) {
@ -188,7 +188,7 @@ There is no equivalent behavior to `start` or `stop` in legacy plugins, so this
The lifecycle-specific contracts exposed by core services are always passed as the first argument to the equivalent lifecycle function in a plugin. For example, the core `UiSettings` service exposes a function `get` to all plugin `setup` functions. To use this function to retrieve a specific UI setting, a plugin just accesses it off of the first argument:
```ts
import { CoreSetup } from '../../../core/public';
import { CoreSetup } from 'kibana/server';
export class Plugin {
public setup(core: CoreSetup) {
@ -203,6 +203,15 @@ For example, the `stop` function in the browser gets invoked as part of the `win
Core services that expose functionality to plugins always have their `setup` function ran before any plugins.
These are the contracts exposed by the core services for each lifecycle event:
| lifecycle event | contract |
| --------------- | --------------------------------------------------------------------------------------------------------------- |
| *contructor* | [PluginInitializerContext](../../docs/development/core/server/kibana-plugin-server.plugininitializercontext.md) |
| *setup* | [CoreSetup](../../docs/development/core/server/kibana-plugin-server.coresetup.md) |
| *start* | [CoreStart](../../docs/development/core/server/kibana-plugin-server.corestart.md) |
| *stop* | |
### Integrating with other plugins
Plugins can expose public interfaces for other plugins to consume. Like `core`, those interfaces are bound to the lifecycle functions `setup` and/or `start`.
@ -360,6 +369,7 @@ npStart.plugins.data.fieldFormats.getType(myFieldFormatId);
Legacy server-side plugins access functionality from core and other plugins at runtime via function arguments, which is similar to how they must be architected to use the new plugin system. This greatly simplifies the plan of action for migrating server-side plugins.
Here is the high-level for migrating a server-side plugin:
- De-couple from hapi.js server and request objects
- Introduce a new plugin definition shim
- Replace legacy services in shim with new platform services
@ -518,7 +528,7 @@ interface FooSetup {
}
// We inject the miminal legacy dependencies into our plugin including dependencies on other legacy
// plugins. Take care to only expose the legacy functionality you need e.g. don't inject the whole
// plugins. Take care to only expose the legacy functionality you need e.g. don't inject the whole
// `Legacy.Server` if you only depend on `Legacy.Server['route']`.
interface LegacySetup {
route: Legacy.Server['route']
@ -542,7 +552,7 @@ export class DemoPlugin implements Plugin<DemoSetup, DemoStart, DemoSetupDeps, D
public setup(core: CoreSetup, plugins: PluginsSetup, __LEGACY: LegacySetup): DemoSetup {
// We're still using the legacy Elasticsearch and http router here, but we're now accessing
// these services in the same way a NP plugin would: injected into the setup function. It's
// also obvious that these dependencies needs to be removed by migrating over to the New
// also obvious that these dependencies needs to be removed by migrating over to the New
// Platform services exposed through core.
const serverFacade: ServerFacade = {
plugins: {
@ -606,6 +616,7 @@ export default (kibana) => {
});
}
```
> Note: An equally valid approach is to extend `CoreSetup` with a `__legacy`
> property instead of introducing a third parameter to your plugins lifecycle
> function. The important thing is that you reduce the legacy API surface that
@ -660,7 +671,7 @@ the legacy core.
A similar approach can be taken for your plugin dependencies. To start
consuming an API from a New Platform plugin access these from
`server.newPlatform.setup.plugins` and inject it into your plugin's setup
function.
function.
```ts
init(server) {
@ -692,7 +703,7 @@ entirely powered by the New Platform and New Platform plugins.
> Note: All New Platform plugins are exposed to legacy plugins via
> `server.newPlatform.setup.plugins`. Once you move your plugin over to the
> New Platform you will have to explicitly declare your dependencies on other
> plugins in your `kibana.json` manifest file.
> plugins in your `kibana.json` manifest file.
At this point, your legacy server-side plugin logic is no longer coupled to legacy plugins.
@ -705,6 +716,7 @@ Many plugins will copy and paste all of their plugin code into a new plugin dire
With the previous steps resolved, this final step should be easy, but the exact process may vary plugin by plugin, so when you're at this point talk to the platform team to figure out the exact changes you need.
Other plugins may want to move subsystems over individually. For instance, you can move routes over to the New Platform in groups rather than all at once. Other examples that could be broken up:
- Configuration schema ([see example](./MIGRATION_EXAMPLES.md#declaring-config-schema))
- HTTP route registration ([see example](./MIGRATION_EXAMPLES.md#http-routes))
- Polling mechanisms (eg. job worker)
@ -729,7 +741,7 @@ This definition isn't going to do much for us just yet, but as we get further in
```ts
// public/plugin.ts
import { CoreSetup, CoreStart, Plugin } from '../../../../core/public';
import { CoreSetup, CoreStart, Plugin } from 'kibana/server';
import { FooSetup, FooStart } from '../../../../legacy/core_plugins/foo/public';
/**
@ -797,7 +809,7 @@ While you're at it, you can also add your plugin initializer to this file:
```ts
// public/index.ts
import { PluginInitializer, PluginInitializerContext } from '../../../../core/public';
import { PluginInitializer, PluginInitializerContext } from 'kibana/server';
import { DemoSetup, DemoStart, DemoSetupDeps, DemoStartDeps, DemoPlugin } from './plugin';
// Core will be looking for this when loading our plugin in the new platform
@ -831,7 +843,7 @@ So we will take a similar approach to what was described above in the server sec
```ts
// public/legacy.ts
import { PluginInitializerContext } from '../../../../core/public';
import { PluginInitializerContext } from 'kibana/server';
import { npSetup, npStart } from 'ui/new_platform';
import { plugin } from '.';
@ -861,10 +873,10 @@ The point is that, over time, this becomes the one file in our plugin containing
Everything inside of the `ui/public` directory is going to be dealt with in one of the following ways:
* Deleted because it doesn't need to be used anymore
* Moved to or replaced by something in core that isn't coupled to angular
* Moved to or replaced by an extension point in a specific plugin that "owns" that functionality
* Copied into each plugin that depends on it and becomes an implementation detail there
- Deleted because it doesn't need to be used anymore
- Moved to or replaced by something in core that isn't coupled to angular
- Moved to or replaced by an extension point in a specific plugin that "owns" that functionality
- Copied into each plugin that depends on it and becomes an implementation detail there
To rapidly define ownership and determine interdependencies, UI modules should move to the most appropriate plugins to own them. Modules that are considered "core" can remain in the ui directory as the platform team works to move them out.
@ -876,12 +888,12 @@ If it is determined that your plugin is going to own any UI modules that other p
Depending on the module's level of complexity and the number of other places in Kibana that rely on it, there are a number of strategies you could use for this:
* **Do it all at once.** Move the code, expose it from your plugin, and update all imports across Kibana.
- **Do it all at once.** Move the code, expose it from your plugin, and update all imports across Kibana.
- This works best for small pieces of code that aren't widely used.
* **Shim first, move later.** Expose the code from your plugin by importing it in your shim and then re-exporting it from your plugin first, then gradually update imports to pull from the new location, leaving the actual moving of the code as a final step.
- **Shim first, move later.** Expose the code from your plugin by importing it in your shim and then re-exporting it from your plugin first, then gradually update imports to pull from the new location, leaving the actual moving of the code as a final step.
- This works best for the largest, most widely used modules that would otherwise result in huge, hard-to-review PRs.
- It makes things easier by splitting the process into small, incremental PRs, but is probably overkill for things with a small surface area.
* **Hybrid approach.** As a middle ground, you can also move the code to your plugin immediately, and then re-export your plugin code from the original `ui/public` directory.
- **Hybrid approach.** As a middle ground, you can also move the code to your plugin immediately, and then re-export your plugin code from the original `ui/public` directory.
- This eliminates any concerns about backwards compatibility by allowing you to update the imports across Kibana later.
- Works best when the size of the PR is such that moving the code can be done without much refactoring.
@ -943,6 +955,7 @@ Many plugins at this point will copy over their plugin definition class & the co
With the previous steps resolved, this final step should be easy, but the exact process may vary plugin by plugin, so when you're at this point talk to the platform team to figure out the exact changes you need.
Other plugins may want to move subsystems over individually. Examples of pieces that could be broken up:
- Registration logic (eg. viz types, embeddables, chrome nav controls)
- Application mounting
- Polling mechanisms (eg. job worker)
@ -980,6 +993,7 @@ At the very least, any plugin exposing an extension point should do so with firs
Legacy Kibana has never run as a single page application. Each plugin has it's own entry point and gets "ownership" of every module it imports when it is loaded into the browser. This has allowed stateful modules to work without breaking other plugins because each time the user navigates to a new plugin, the browser reloads with a different entry bundle, clearing the state of the previous plugin.
Because of this "feature" many undesirable things developed in the legacy platform:
- We had to invent an unconventional and fragile way of allowing plugins to integrate and communicate with one another, `uiExports`.
- It has never mattered if shared modules in `ui/public` were stateful or cleaned up after themselves, so many of them behave like global singletons. These modules could never work in single-page application because of this state.
- We've had to ship Webpack with Kibana in production so plugins could be disabled or installed and still have access to all the "platform" features of `ui/public` modules and all the `uiExports` would be present for any enabled plugins.
@ -997,7 +1011,6 @@ One goal of a stable Kibana core API is to allow Kibana instances to run plugins
This method of building and installing plugins comes with side effects which are important to be aware of when developing a plugin.
- **Any code you export to other plugins will get copied into their bundles.** If a plugin is built for 8.1 and is running on Kibana 8.2, any modules it imported that changed will not be updated in that plugin.
- **When a plugin is disabled, other plugins can still import its static exports.** This can make code difficult to reason about and result in poor user experience. For example, users generally expect that all of a plugins features will be disabled when the plugin is disabled. If another plugin imports a disabled plugins feature and exposes it to the user, then users will be confused about whether that plugin really is disabled or not.
- **Plugins cannot share state by importing each others modules.** Sharing state via imports does not work because exported modules will be copied into plugins that import them. Lets say your plugin exports a module thats imported by other plugins. If your plugin populates state into this module, a natural expectation would be that the other plugins now have access to this state. However, because those plugins have copies of the exported module, this assumption will be incorrect.
@ -1007,16 +1020,19 @@ This method of building and installing plugins comes with side effects which are
The general rule of thumb here is: any module that is not purely functional should not be shared statically, and instead should be exposed at runtime via the plugin's `setup` and/or `start` contracts.
Ask yourself these questions when deciding to share code through static exports or plugin contracts:
- Is its behavior dependent on any state populated from my plugin?
- If a plugin uses an old copy (from an older version of Kibana) of this module, will it still break?
If you answered yes to any of the above questions, you probably have an impure module that cannot be shared across plugins. Another way to think about this: if someone literally copied and pasted your exported module into their plugin, would it break if:
- Your original module changed in a future version and the copy was the old version; or
- If your plugin doesnt have access to the copied version in the other plugin (because it doesn't know about it).
If your module were to break for either of these reasons, it should not be exported statically. This can be more easily illustrated by examples of what can and cannot be exported statically.
Examples of code that could be shared statically:
- Constants. Strings and numbers that do not ever change (even between Kibana versions)
- If constants do change between Kibana versions, then they should only be exported statically if the old value would not _break_ if it is still used. For instance, exporting a constant like `VALID_INDEX_NAME_CHARACTERS` would be fine, but exporting a constant like `API_BASE_PATH` would not because if this changed, old bundles using the previous value would break.
- React components that do not depend on module state.
@ -1025,25 +1041,33 @@ Examples of code that could be shared statically:
- Pure computation functions, for example lodash-like functions like `mapValues`.
Examples of code that could **not** be shared statically and how to fix it:
- A function that calls a Core service, but does not take that service as a parameter.
- If the function does not take a client as an argument, it must have an instance of the client in its internal state, populated by your plugin. This would not work across plugin boundaries because your plugin would not be able to call `setClient` in the copy of this module in other plugins:
```js
let esClient;
export const setClient = (client) => esClient = client;
export const query = (params) => esClient.search(params);
```
- This could be fixed by requiring the calling code to provide the client:
```js
export const query = (esClient, params) => esClient.search(params);
```
- A function that allows other plugins to register values that get pushed into an array defined internally to the module.
- The values registered would only be visible to the plugin that imported it. Each plugin would essentially have their own registry of visTypes that is not visible to any other plugins.
```js
const visTypes = [];
export const registerVisType = (visType) => visTypes.push(visType);
export const getVisTypes = () => visTypes;
```
- For state that does need to be shared across plugins, you will need to expose methods in your plugin's `setup` and `start` contracts.
```js
class MyPlugin {
constructor() { this.visTypes = [] }
@ -1087,6 +1111,7 @@ If you have code that should be available to other plugins on both the client an
### How can I avoid passing Core services deeply within my UI component tree?
There are some Core services that are purely presentational, for example `core.overlays.openModal()` or `core.application.createLink()` where UI code does need access to these deeply within your application. However, passing these services down as props throughout your application leads to lots of boilerplate. To avoid this, you have three options:
1. Use an abstraction layer, like Redux, to decouple your UI code from core (**this is the highly preferred option**); or
- [redux-thunk](https://github.com/reduxjs/redux-thunk#injecting-a-custom-argument) and [redux-saga](https://redux-saga.js.org/docs/api/#createsagamiddlewareoptions) already have ways to do this.
2. Use React Context to provide these services to large parts of your React tree; or
@ -1124,12 +1149,12 @@ The packages directory should have the least amount of code in Kibana. Just beca
Many of the utilities you're using to build your plugins are available in the New Platform or in New Platform plugins. To help you build the shim for these new services, use the tables below to find where the New Platform equivalent lives.
#### Client-side
TODO: add links to API docs on items in "New Platform" column.
##### Core services
In client code, `core` can be imported in legacy plugins via the `ui/new_platform` module.
```ts
@ -1157,6 +1182,7 @@ import { npStart: { core } } from 'ui/new_platform';
_See also: [Public's CoreStart API Docs](/docs/development/core/public/kibana-plugin-public.corestart.md)_
##### Plugins for shared application services
In client code, we have a series of plugins which house shared application services that are being built in the shape of the new platform, but for the time being, are only available in legacy. So if your plugin depends on any of the APIs below, you'll need build your plugin as a legacy plugin that shims the new platform. Once these API's have been moved to the new platform you can migrate your plugin and declare a dependency on the plugin that owns the API's you require.
The contracts for these plugins are exposed for you to consume in your own plugin; we have created dedicated exports for the `setup` and `start` contracts in a file called `legacy`. By passing these contracts to your plugin's `setup` and `start` methods, you can mimic the functionality that will eventually be provided in the new platform.
@ -1191,6 +1217,7 @@ import { setup, start } from '../core_plugins/visualizations/public/legacy';
#### Server-side
##### Core services
In server code, `core` can be accessed from either `server.newPlatform` or `kbnServer.newPlatform`. There are not currently very many services available on the server-side:
| Legacy Platform | New Platform | Notes |
@ -1253,6 +1280,7 @@ Examples:
- **uiSettingDefaults**
Before:
```js
uiExports: {
uiSettingDefaults: {
@ -1265,7 +1293,9 @@ uiExports: {
}
}
```
After:
```ts
// src/plugins/my-plugin/server/plugin.ts
setup(core: CoreSetup){
@ -1283,6 +1313,7 @@ setup(core: CoreSetup){
## How to
### Configure plugin
Kibana provides ConfigService if a plugin developer may want to support adjustable runtime behavior for their plugins. Access to Kibana config in New platform has been subject to significant refactoring.
Config service does not provide access to the whole config anymore. New platform plugin cannot read configuration parameters of the core services nor other plugins directly. Use plugin contract to provide data.
@ -1296,8 +1327,10 @@ const basePath = core.http.basePath.get(request);
```
In order to have access to your plugin config, you *should*:
- Declare plugin specific "configPath" (will fallback to plugin "id" if not specified) in `kibana.json` file.
- Export schema validation for config from plugin's main file. Schema is mandatory. If a plugin reads from the config without schema declaration, ConfigService will throw an error.
```typescript
// my_plugin/server/index.ts
import { schema, TypeOf } from '@kbn/config-schema';
@ -1307,7 +1340,9 @@ export const config = {
};
export type MyPluginConfigType = TypeOf<typeof config.schema>;
```
- Read config value exposed via initializerContext. No config path is required.
```typescript
class MyPlugin {
constructor(initializerContext: PluginInitializerContext) {
@ -1318,6 +1353,7 @@ class MyPlugin {
```
If your plugin also have a client-side part, you can also expose configuration properties to it using a whitelisting mechanism with the configuration `exposeToBrowser` property.
```typescript
// my_plugin/server/index.ts
import { schema, TypeOf } from '@kbn/config-schema';
@ -1339,6 +1375,7 @@ export const config: PluginConfigDescriptor<ConfigType> = {
```
Configuration containing only the exposed properties will be then available on the client-side using the plugin's `initializerContext`:
```typescript
// my_plugin/public/index.ts
interface ClientConfigType {
@ -1364,7 +1401,9 @@ export const config = {
### Mock new platform services in tests
#### Writing mocks for your plugin
Core services already provide mocks to simplify testing and make sure plugins always rely on valid public contracts:
```typescript
// my_plugin/server/plugin.test.ts
import { configServiceMock } from 'src/core/server/mocks';
@ -1376,6 +1415,7 @@ const plugin = new MyPlugin({ configService }, …);
```
Or if you need to get the whole core `setup` or `start` contracts:
```typescript
// my_plugin/public/plugin.test.ts
import { coreMock } from 'src/core/public/mocks';
@ -1388,8 +1428,8 @@ coreSetup.uiSettings.get.mockImplementation((key: string) => {
const plugin = new MyPlugin(coreSetup, ...);
```
Although it isn't mandatory, we strongly recommended you export your plugin mocks as well, in order for dependent plugins to use them in tests. Your plugin mocks should be exported from the root `/server` and `/public` directories in your plugin:
```typescript
// my_plugin/server/mocks.ts or my_plugin/public/mocks.ts
const createSetupContractMock = () => {
@ -1406,26 +1446,31 @@ export const myPluginMocks = {
createStart: …
}
```
Plugin mocks should consist of mocks for *public APIs only*: setup/start/stop contracts. Mocks aren't necessary for pure functions as other plugins can call the original implementation in tests.
#### Using mocks in your tests
During the migration process, it is likely you are preparing your plugin by shimming in new platform-ready dependencies via the legacy `ui/new_platform` module:
```typescript
import { npSetup, npStart } from 'ui/new_platform';
```
If you are using this approach, the easiest way to mock core and new platform-ready plugins in your legacy tests is to mock the `ui/new_platform` module:
```typescript
jest.mock('ui/new_platform');
```
This will automatically mock the services in `ui/new_platform` thanks to the [helpers that have been added](https://github.com/elastic/kibana/blob/master/src/legacy/ui/public/new_platform/__mocks__/helpers.ts) to that module.
This will automatically mock the services in `ui/new_platform` thanks to the [helpers that have been added](../../src/legacy/ui/public/new_platform/__mocks__/helpers.ts) to that module.
If others are consuming your plugin's new platform contracts via the `ui/new_platform` module, you'll want to update the helpers as well to ensure your contracts are properly mocked.
> Note: The `ui/new_platform` mock is only designed for use by old Jest tests. If you are writing new tests, you should structure your code and tests such that you don't need this mock. Instead, you should import the `core` mock directly and instantiate it.
#### What about karma tests?
While our plan is to only provide first-class mocks for Jest tests, there are many legacy karma tests that cannot be quickly or easily converted to Jest -- particularly those which are still relying on mocking Angular services via `ngMock`.
For these tests, we are maintaining a separate set of mocks. Files with a `.karma_mock.{js|ts|tsx}` extension will be loaded _globally_ before karma tests are run.
@ -1433,11 +1478,15 @@ For these tests, we are maintaining a separate set of mocks. Files with a `.karm
It is important to note that this behavior is different from `jest.mock('ui/new_platform')`, which only mocks tests on an individual basis. If you encounter any failures in karma tests as a result of new platform migration efforts, you may need to add a `.karma_mock.js` file for the affected services, or add to the existing karma mock we are maintaining in `ui/new_platform`.
### Provide Legacy Platform API to the New platform plugin
#### On the server side
During migration, you can face a problem that not all API is available in the New platform yet. You can work around this by extending your
new platform plugin with Legacy API:
- create New platform plugin
- New platform plugin should expose a method `registerLegacyAPI` that allows passing API from the Legacy platform and store it in the NP plugin instance
```js
class MyPlugin {
public async setup(core){
@ -1447,7 +1496,9 @@ class MyPlugin {
}
}
```
- The legacy plugin provides API calling `registerLegacyAPI`
```js
new kibana.Plugin({
init(server){
@ -1459,7 +1510,9 @@ new kibana.Plugin({
}
})
```
- The new platform plugin access stored Legacy platform API via `getLegacyAPI` getter. Getter function must have name indicating thats API provided from the Legacy platform.
```js
class MyPlugin {
private getLegacyAPI(){
@ -1478,7 +1531,8 @@ class MyPlugin {
```
#### On the client side
It's not currently possible to use a similar pattern on the client-side.
Because Legacy platform plugins heavily rely on global angular modules, which aren't available on the new platform.
It's not currently possible to use a similar pattern on the client-side.
Because Legacy platform plugins heavily rely on global angular modules, which aren't available on the new platform.
So you can utilize the same approach for only *stateless Angular components*, as long as they are not consumed by a New Platform application. When New Platform applications are on the page, no legacy code is executed, so the `registerLegacyAPI` function would not be called.
>>>>>>> 9e168391af... Move apply filters popover ⇒ NP (#51566)

View file

@ -0,0 +1,468 @@
# Migration Examples
This document is a list of examples of how to migrate plugin code from legacy
APIs to their New Platform equivalents.
- [Migration Examples](#migration-examples)
- [Configuration](#configuration)
- [Declaring config schema](#declaring-config-schema)
- [Using New Platform config from a Legacy plugin](#using-new-platform-config-from-a-legacy-plugin)
- [Create a New Platform plugin](#create-a-new-platform-plugin)
- [HTTP Routes](#http-routes)
- [1. Legacy route registration](#1-legacy-route-registration)
- [2. New Platform shim using legacy router](#2-new-platform-shim-using-legacy-router)
- [3. New Platform shim using New Platform router](#3-new-platform-shim-using-new-platform-router)
- [4. New Platform plugin](#4-new-platform-plugin)
- [Accessing Services](#accessing-services)
- [Chrome](#chrome)
## Configuration
### Declaring config schema
Declaring the schema of your configuration fields is similar to the Legacy Platform but uses the `@kbn/config-schema` package instead of Joi. This package has full TypeScript support, but may be missing some features you need. Let the Platform team know by opening an issue and we'll add what you're missing.
```ts
// Legacy config schema
import Joi from 'joi';
new kibana.Plugin({
config() {
return Joi.object({
enabled: Joi.boolean().default(true),
defaultAppId: Joi.string().default('home'),
index: Joi.string().default('.kibana'),
disableWelcomeScreen: Joi.boolean().default(false),
autocompleteTerminateAfter: Joi.number().integer().min(1).default(100000),
})
}
});
// New Platform equivalent
import { schema, TypeOf } from '@kbn/config-schema';
export const config = {
schema: schema.object({
enabled: schema.boolean({ defaultValue: true }),
defaultAppId: schema.string({ defaultValue: true }),
index: schema.string({ defaultValue: '.kibana' }),
disableWelcomeScreen: schema.boolean({ defaultValue: false }),
autocompleteTerminateAfter: schema.duration({ min: 1, defaultValue: 100000 }),
})
};
// @kbn/config-schema is written in TypeScript, so you can use your schema
// definition to create a type to use in your plugin code.
export type MyPluginConfig = TypeOf<typeof config.schema>;
```
### Using New Platform config in a new plugin
After setting the config schema for your plugin, you might want to reach the configuration in the plugin.
It is provided as part of the [PluginInitializerContext](../../docs/development/core/server/kibana-plugin-server.plugininitializercontext.md)
in the *constructor* of the plugin:
```ts
// myPlugin/(public|server)/index.ts
import { PluginInitializerContext } from 'kibana/server';
import { MyPlugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
return new MyPlugin(initializerContext);
}
```
```ts
// myPlugin/(public|server)/plugin.ts
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import { CoreSetup, Logger, Plugin, PluginInitializerContext, PluginName } from 'kibana/server';
import { MyPlugin } from './plugin';
export class MyPlugin implements Plugin {
private readonly config$: Observable<MyPluginConfig>;
private readonly log: Logger;
constructor(private readonly initializerContext: PluginInitializerContext) {
this.log = initializerContext.logger.get();
this.config$ = initializerContext.config.create();
}
public async setup(core: CoreSetup, deps: Record<PluginName, unknown>) {
const isEnabled = await this.config$.pipe(first()).toPromise();
...
}
...
}
}
```
Additionally, some plugins need to read other plugins' config to act accordingly (like timing out a request, matching ElasticSearch's timeout). For those use cases, the plugin can rely on the *globalConfig* and *env* properties in the context:
```ts
export class MyPlugin implements Plugin {
...
public async setup(core: CoreSetup, deps: Record<PluginName, unknown>) {
const { mode: { dev }, packageInfo: { version } } = this.initializerContext.env;
const { elasticsearch: { shardTimeout }, path: { data } } = await this.initializerContext.config.legacy.globalConfig$
.pipe(first()).toPromise();
...
}
```
### Using New Platform config from a Legacy plugin
During the migration process, you'll want to migrate your schema to the new
format. However, legacy plugins cannot directly get access to New Platform's
config service due to the way that config is tied to the `kibana.json` file
(which does not exist for legacy plugins).
There is a workaround though:
- Create a New Platform plugin that contains your plugin's config schema in the new format
- Expose the config from the New Platform plugin in its setup contract
- Read the config from the setup contract in your legacy plugin
#### Create a New Platform plugin
For example, if wanted to move the legacy `timelion` plugin's configuration to
the New Platform, we could create a NP plugin with the same name in
`src/plugins/timelion` with the following files:
```json5
// src/plugins/timelion/kibana.json
{
"id": "timelion",
"server": true
}
```
```ts
// src/plugins/timelion/server/index.ts
import { schema, TypeOf } from '@kbn/config-schema';
import { PluginInitializerContext } from 'src/core/server';
import { TimelionPlugin } from './plugin';
export const config = {
schema: schema.object({
enabled: schema.boolean({ defaultValue: true }),
});
}
export const plugin = (initContext: PluginInitializerContext) => new TimelionPlugin(initContext);
export type TimelionConfig = TypeOf<typeof config.schema>;
export { TimelionSetup } from './plugin';
```
```ts
// src/plugins/timelion/server/plugin.ts
import { PluginInitializerContext, Plugin, CoreSetup } from '../../core/server';
import { TimelionConfig } from '.';
export class TimelionPlugin implements Plugin<TimelionSetup> {
constructor(private readonly initContext: PluginInitializerContext) {}
public setup(core: CoreSetup) {
return {
__legacy: {
config$: this.initContext.config.create<TimelionConfig>(),
},
};
}
public start() {}
public stop() {}
}
export interface TimelionSetup {
/** @deprecated */
__legacy: {
config$: Observable<TimelionConfig>;
};
}
```
With the New Platform plugin in place, you can then read this `config$`
Observable from your legacy plugin:
```ts
import { take } from 'rxjs/operators';
new kibana.Plugin({
async init(server) {
const { config$ } = server.newPlatform.setup.plugins.timelion;
const currentConfig = await config$.pipe(take(1)).toPromise();
}
});
```
## HTTP Routes
In the legacy platform, plugins have direct access to the Hapi `server` object
which gives full access to all of Hapi's API. In the New Platform, plugins have
access to the
[HttpServiceSetup](/docs/development/core/server/kibana-plugin-server.httpservicesetup.md)
interface, which is exposed via the
[CoreSetup](/docs/development/core/server/kibana-plugin-server.coresetup.md)
object injected into the `setup` method of server-side plugins.
This interface has a different API with slightly different behaviors.
- All input (body, query parameters, and URL parameters) must be validated using
the `@kbn/config-schema` package. If no validation schema is provided, these
values will be empty objects.
- All exceptions thrown by handlers result in 500 errors. If you need a specific
HTTP error code, catch any exceptions in your handler and construct the
appropriate response using the provided response factory. While you can
continue using the `boom` module internally in your plugin, the framework does
not have native support for converting Boom exceptions into HTTP responses.
Because of the incompatibility between the legacy and New Platform HTTP Route
API's it might be helpful to break up your migration work into several stages.
### 1. Legacy route registration
```ts
// legacy/plugins/myplugin/index.ts
import Joi from 'joi';
new kibana.Plugin({
init(server) {
server.route({
path: '/api/demoplugin/search',
method: 'POST',
options: {
validate: {
payload: Joi.object({
field1: Joi.string().required(),
}),
}
},
handler(req, h) {
return { message: `Received field1: ${req.payload.field1}` };
}
});
}
});
```
### 2. New Platform shim using legacy router
Create a New Platform shim and inject the legacy `server.route` into your
plugin's setup function.
```ts
// legacy/plugins/demoplugin/index.ts
import { Plugin, LegacySetup } from './server/plugin';
export default (kibana) => {
return new kibana.Plugin({
id: 'demo_plugin',
init(server) {
// core shim
const coreSetup: server.newPlatform.setup.core;
const pluginSetup = {};
const legacySetup: LegacySetup = {
route: server.route
};
new Plugin().setup(coreSetup, pluginSetup, legacySetup);
}
}
}
```
```ts
// legacy/plugins/demoplugin/server/plugin.ts
import { CoreSetup } from 'src/core/server';
import { Legacy } from 'kibana';
export interface LegacySetup {
route: Legacy.Server['route'];
};
export interface DemoPluginsSetup {};
export class Plugin {
public setup(core: CoreSetup, plugins: DemoPluginsSetup, __LEGACY: LegacySetup) {
__LEGACY.route({
path: '/api/demoplugin/search',
method: 'POST',
options: {
validate: {
payload: Joi.object({
field1: Joi.string().required(),
}),
}
},
async handler(req) {
return { message: `Received field1: ${req.payload.field1}` };
},
});
}
}
```
### 3. New Platform shim using New Platform router
We now switch the shim to use the real New Platform HTTP API's in `coreSetup`
instead of relying on the legacy `server.route`. Since our plugin is now using
the New Platform API's we are guaranteed that our HTTP route handling is 100%
compatible with the New Platform. As a result, we will also have to adapt our
route registration accordingly.
```ts
// legacy/plugins/demoplugin/index.ts
import { Plugin } from './server/plugin';
export default (kibana) => {
return new kibana.Plugin({
id: 'demo_plugin',
init(server) {
// core shim
const coreSetup = server.newPlatform.setup.core;
const pluginSetup = {};
new Plugin().setup(coreSetup, pluginSetup);
}
}
}
```
```ts
// legacy/plugins/demoplugin/server/plugin.ts
import { schema } from '@kbn/config-schema';
import { CoreSetup } from 'src/core/server';
export interface DemoPluginsSetup {};
class Plugin {
public setup(core: CoreSetup, pluginSetup: DemoPluginSetup) {
const router = core.http.createRouter();
router.post(
{
path: '/api/demoplugin/search',
validate: {
body: schema.object({
field1: schema.string(),
}),
}
},
(context, req, res) => {
return res.ok({
body: {
message: `Received field1: ${req.body.field1}`
}
});
}
)
}
}
```
If your plugin still relies on throwing Boom errors from routes, you can use the `router.handleLegacyErrors`
as a temporary solution until error migration is complete:
```ts
// legacy/plugins/demoplugin/server/plugin.ts
import { schema } from '@kbn/config-schema';
import { CoreSetup } from 'src/core/server';
export interface DemoPluginsSetup {};
class Plugin {
public setup(core: CoreSetup, pluginSetup: DemoPluginSetup) {
const router = core.http.createRouter();
router.post(
{
path: '/api/demoplugin/search',
validate: {
body: schema.object({
field1: schema.string(),
}),
}
},
router.wrapErrors((context, req, res) => {
throw Boom.notFound('not there'); // will be converted into proper New Platform error
})
)
}
}
```
#### 4. New Platform plugin
As the final step we delete the shim and move all our code into a New Platform
plugin. Since we were already consuming the New Platform API's no code changes
are necessary inside `plugin.ts`.
```ts
// Move legacy/plugins/demoplugin/server/plugin.ts -> plugins/demoplugin/server/plugin.ts
```
### Accessing Services
Services in the Legacy Platform were typically available via methods on either
`server.plugins.*`, `server.*`, or `req.*`. In the New Platform, all services
are available via the `context` argument to the route handler. The type of this
argument is the
[RequestHandlerContext](/docs/development/core/server/kibana-plugin-server.requesthandlercontext.md).
The APIs available here will include all Core services and any services
registered by plugins this plugin depends on.
```ts
new kibana.Plugin({
init(server) {
const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
server.route({
path: '/api/my-plugin/my-route',
method: 'POST',
async handler(req, h) {
const results = await callWithRequest(req, 'search', query);
return { results };
}
});
}
});
class Plugin {
public setup(core) {
const router = core.http.createRouter();
router.post(
{
path: '/api/my-plugin/my-route',
},
async (context, req, res) => {
const results = await context.elasticsearch.dataClient.callAsCurrentUser('search', query);
return res.ok({
body: { results }
});
}
)
}
}
```
## Chrome
In the Legacy Platform, the `ui/chrome` import contained APIs for a very wide
range of features. In the New Platform, some of these APIs have changed or moved
elsewhere.
| Legacy Platform | New Platform | Notes |
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `chrome.addBasePath` | [`core.http.basePath.prepend`](/docs/development/core/public/kibana-plugin-public.httpservicebase.basepath.md) | |
| `chrome.breadcrumbs.set` | [`core.chrome.setBreadcrumbs`](/docs/development/core/public/kibana-plugin-public.chromestart.setbreadcrumbs.md) | |
| `chrome.getUiSettingsClient` | [`core.uiSettings`](/docs/development/core/public/kibana-plugin-public.uisettingsclient.md) | |
| `chrome.helpExtension.set` | [`core.chrome.setHelpExtension`](/docs/development/core/public/kibana-plugin-public.chromestart.sethelpextension.md) | |
| `chrome.setVisible` | [`core.chrome.setIsVisible`](/docs/development/core/public/kibana-plugin-public.chromestart.setisvisible.md) | |
| `chrome.getInjected` | [`core.injectedMetadata.getInjected`](/docs/development/core/public/kibana-plugin-public.coresetup.injectedmetadata.md) (temporary) | A temporary API is available to read injected vars provided by legacy plugins. This will be removed after [#41990](https://github.com/elastic/kibana/issues/41990) is completed. |
| `chrome.setRootTemplate` / `chrome.setRootController` | -- | Use application mounting via `core.application.register` (not currently avaiable to legacy plugins). |
In most cases, the most convenient way to access these APIs will be via the
[AppMountContext](/docs/development/core/public/kibana-plugin-public.appmountcontext.md)
object passed to your application when your app is mounted on the page.

View file

@ -871,7 +871,7 @@ export class SavedObjectsClient {
bulkUpdate<T extends SavedObjectAttributes>(objects?: SavedObjectsBulkUpdateObject[]): Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>;
create: <T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>>;
delete: (type: string, id: string) => Promise<{}>;
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectsFindOptions, "search" | "filter" | "type" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "page" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>>;
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectsFindOptions, "search" | "filter" | "type" | "page" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>>;
get: <T extends SavedObjectAttributes>(type: string, id: string) => Promise<SimpleSavedObject<T>>;
update<T extends SavedObjectAttributes>(type: string, id: string, attributes: T, { version, migrationVersion, references }?: SavedObjectsUpdateOptions): Promise<SimpleSavedObject<T>>;
}

View file

@ -7,6 +7,7 @@ Array [
"logging": Object {
"verbose": true,
},
"path": Object {},
},
],
]
@ -19,6 +20,7 @@ Array [
"logging": Object {
"verbose": true,
},
"path": Object {},
},
],
]

View file

@ -133,8 +133,8 @@ describe('once LegacyService is set up with connection info', () => {
expect(MockKbnServer).toHaveBeenCalledTimes(1);
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{ server: { autoListen: true } },
{ path: { autoListen: true }, server: { autoListen: true } },
{ path: { autoListen: true }, server: { autoListen: true } }, // Because of the mock, path also gets the value
expect.any(Object),
{ disabledPluginSpecs: [], pluginSpecs: [], uiExports: [] }
);
@ -159,8 +159,8 @@ describe('once LegacyService is set up with connection info', () => {
expect(MockKbnServer).toHaveBeenCalledTimes(1);
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{ server: { autoListen: true } },
{ path: { autoListen: false }, server: { autoListen: true } },
{ path: { autoListen: false }, server: { autoListen: true } },
expect.any(Object),
{ disabledPluginSpecs: [], pluginSpecs: [], uiExports: [] }
);
@ -296,8 +296,8 @@ describe('once LegacyService is set up without connection info', () => {
test('creates legacy kbnServer with `autoListen: false`.', () => {
expect(MockKbnServer).toHaveBeenCalledTimes(1);
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{ server: { autoListen: true } },
{ path: {}, server: { autoListen: true } },
{ path: {}, server: { autoListen: true } },
expect.any(Object),
{ disabledPluginSpecs: [], pluginSpecs: [], uiExports: [] }
);

View file

@ -31,6 +31,7 @@ import { Logger } from '../logging';
import { PluginsServiceSetup, PluginsServiceStart } from '../plugins';
import { findLegacyPluginSpecs } from './plugins';
import { LegacyPluginSpec } from './plugins/find_legacy_plugin_specs';
import { PathConfigType } from '../path';
import { LegacyConfig } from './config';
interface LegacyKbnServer {
@ -40,7 +41,7 @@ interface LegacyKbnServer {
close: () => Promise<void>;
}
function getLegacyRawConfig(config: Config) {
function getLegacyRawConfig(config: Config, pathConfig: PathConfigType) {
const rawConfig = config.toRaw();
// Elasticsearch config is solely handled by the core and legacy platform
@ -49,7 +50,10 @@ function getLegacyRawConfig(config: Config) {
delete rawConfig.elasticsearch;
}
return rawConfig;
return {
...rawConfig,
path: pathConfig, // We rely heavily in the default value of 'path.data' in the legacy world and, since it has been moved to NP, it won't show up in RawConfig
};
}
/**
@ -96,7 +100,7 @@ export class LegacyService implements CoreService {
private kbnServer?: LegacyKbnServer;
private configSubscription?: Subscription;
private setupDeps?: LegacyServiceSetupDeps;
private update$: ConnectableObservable<Config> | undefined;
private update$: ConnectableObservable<[Config, PathConfigType]> | undefined;
private legacyRawConfig: LegacyConfig | undefined;
private legacyPlugins:
| {
@ -118,22 +122,25 @@ export class LegacyService implements CoreService {
}
public async discoverPlugins(): Promise<LegacyServiceDiscoverPlugins> {
this.update$ = this.coreContext.configService.getConfig$().pipe(
tap(config => {
this.update$ = combineLatest(
this.coreContext.configService.getConfig$(),
this.coreContext.configService.atPath<PathConfigType>('path')
).pipe(
tap(([config, pathConfig]) => {
if (this.kbnServer !== undefined) {
this.kbnServer.applyLoggingConfiguration(getLegacyRawConfig(config));
this.kbnServer.applyLoggingConfiguration(getLegacyRawConfig(config, pathConfig));
}
}),
tap({ error: err => this.log.error(err) }),
publishReplay(1)
) as ConnectableObservable<Config>;
) as ConnectableObservable<[Config, PathConfigType]>;
this.configSubscription = this.update$.connect();
this.settings = await this.update$
.pipe(
first(),
map(config => getLegacyRawConfig(config))
map(([config, pathConfig]) => getLegacyRawConfig(config, pathConfig))
)
.toPromise();

View file

@ -17,6 +17,7 @@
* under the License.
*/
import { of } from 'rxjs';
import { duration } from 'moment';
import { PluginInitializerContext, CoreSetup, CoreStart } from '.';
import { loggingServiceMock } from './logging/logging_service.mock';
import { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock';
@ -24,6 +25,7 @@ import { httpServiceMock } from './http/http_service.mock';
import { contextServiceMock } from './context/context_service.mock';
import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock';
import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
import { SharedGlobalConfig } from './plugins';
import { InternalCoreSetup, InternalCoreStart } from './internal_types';
import { capabilitiesServiceMock } from './capabilities/capabilities_service.mock';
@ -37,7 +39,19 @@ export { savedObjectsClientMock } from './saved_objects/service/saved_objects_cl
export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
export function pluginInitializerContextConfigMock<T>(config: T) {
const globalConfig: SharedGlobalConfig = {
kibana: { defaultAppId: 'home-mocks', index: '.kibana-tests' },
elasticsearch: {
shardTimeout: duration('30s'),
requestTimeout: duration('30s'),
pingTimeout: duration('30s'),
startupTimeout: duration('30s'),
},
path: { data: '/tmp' },
};
const mock: jest.Mocked<PluginInitializerContext<T>['config']> = {
legacy: { globalConfig$: of(globalConfig) },
create: jest.fn().mockReturnValue(of(config)),
createIfExists: jest.fn().mockReturnValue(of(config)),
};

View file

@ -17,17 +17,17 @@
* under the License.
*/
import { getConfig, getData } from './';
import { accessSync, R_OK } from 'fs';
import { accessSync, constants } from 'fs';
import { getConfigPath, getDataPath } from './';
describe('Default path finder', function () {
describe('Default path finder', () => {
it('should find a kibana.yml', () => {
const configPath = getConfig();
expect(() => accessSync(configPath, R_OK)).not.toThrow();
const configPath = getConfigPath();
expect(() => accessSync(configPath, constants.R_OK)).not.toThrow();
});
it('should find a data directory', () => {
const dataPath = getData();
expect(() => accessSync(dataPath, R_OK)).not.toThrow();
const dataPath = getDataPath();
expect(() => accessSync(dataPath, constants.R_OK)).not.toThrow();
});
});

View file

@ -18,34 +18,53 @@
*/
import { join } from 'path';
import { accessSync, R_OK } from 'fs';
import { find } from 'lodash';
import { fromRoot } from '../../utils';
import { accessSync, constants } from 'fs';
import { TypeOf, schema } from '@kbn/config-schema';
import { fromRoot } from '../utils';
const isString = (v: any): v is string => typeof v === 'string';
const CONFIG_PATHS = [
process.env.KIBANA_PATH_CONF && join(process.env.KIBANA_PATH_CONF, 'kibana.yml'),
process.env.CONFIG_PATH, //deprecated
process.env.CONFIG_PATH, // deprecated
fromRoot('config/kibana.yml'),
'/etc/kibana/kibana.yml'
].filter(Boolean);
'/etc/kibana/kibana.yml',
].filter(isString);
const DATA_PATHS = [
process.env.DATA_PATH, //deprecated
process.env.DATA_PATH, // deprecated
fromRoot('data'),
'/var/lib/kibana'
].filter(Boolean);
'/var/lib/kibana',
].filter(isString);
function findFile(paths) {
const availablePath = find(paths, configPath => {
function findFile(paths: string[]) {
const availablePath = paths.find(configPath => {
try {
accessSync(configPath, R_OK);
accessSync(configPath, constants.R_OK);
return true;
} catch (e) {
//Check the next path
// Check the next path
}
});
return availablePath || paths[0];
}
export const getConfig = () => findFile(CONFIG_PATHS);
export const getData = () => findFile(DATA_PATHS);
/**
* Get the path where the config files are stored
* @internal
*/
export const getConfigPath = () => findFile(CONFIG_PATHS);
/**
* Get the path where the data can be stored
* @internal
*/
export const getDataPath = () => findFile(DATA_PATHS);
export type PathConfigType = TypeOf<typeof config.schema>;
export const config = {
path: 'path',
schema: schema.object({
data: schema.string({ defaultValue: () => getDataPath() }),
}),
};

View file

@ -0,0 +1,88 @@
/*
* 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 { duration } from 'moment';
import { BehaviorSubject } from 'rxjs';
import { first } from 'rxjs/operators';
import { createPluginInitializerContext } from './plugin_context';
import { CoreContext } from '../core_context';
import { Env, ObjectToConfigAdapter } from '../config';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { getEnvOptions } from '../config/__mocks__/env';
import { PluginManifest } from './types';
import { Server } from '../server';
import { fromRoot } from '../utils';
const logger = loggingServiceMock.create();
let coreId: symbol;
let env: Env;
let coreContext: CoreContext;
let server: Server;
function createPluginManifest(manifestProps: Partial<PluginManifest> = {}): PluginManifest {
return {
id: 'some-plugin-id',
version: 'some-version',
configPath: 'path',
kibanaVersion: '7.0.0',
requiredPlugins: ['some-required-dep'],
optionalPlugins: ['some-optional-dep'],
server: true,
ui: true,
...manifestProps,
};
}
describe('Plugin Context', () => {
beforeEach(async () => {
coreId = Symbol('core');
env = Env.createDefault(getEnvOptions());
const config$ = new BehaviorSubject(new ObjectToConfigAdapter({}));
server = new Server(config$, env, logger);
await server.setupConfigSchemas();
coreContext = { coreId, env, logger, configService: server.configService };
});
it('should return a globalConfig handler in the context', async () => {
const manifest = createPluginManifest();
const opaqueId = Symbol();
const pluginInitializerContext = createPluginInitializerContext(
coreContext,
opaqueId,
manifest
);
expect(pluginInitializerContext.config.legacy.globalConfig$).toBeDefined();
const configObject = await pluginInitializerContext.config.legacy.globalConfig$
.pipe(first())
.toPromise();
expect(configObject).toStrictEqual({
kibana: { defaultAppId: 'home', index: '.kibana' },
elasticsearch: {
shardTimeout: duration(30, 's'),
requestTimeout: duration(30, 's'),
pingTimeout: duration(30, 's'),
startupTimeout: duration(5, 's'),
},
path: { data: fromRoot('data') },
});
});
});

View file

@ -17,10 +17,24 @@
* under the License.
*/
import { map } from 'rxjs/operators';
import { combineLatest } from 'rxjs';
import { CoreContext } from '../core_context';
import { PluginWrapper } from './plugin';
import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service';
import { PluginInitializerContext, PluginManifest, PluginOpaqueId } from './types';
import {
PluginInitializerContext,
PluginManifest,
PluginOpaqueId,
SharedGlobalConfigKeys,
} from './types';
import { PathConfigType, config as pathConfig } from '../path';
import { KibanaConfigType, config as kibanaConfig } from '../kibana_config';
import {
ElasticsearchConfigType,
config as elasticsearchConfig,
} from '../elasticsearch/elasticsearch_config';
import { pick, deepFreeze } from '../../utils';
import { CoreSetup, CoreStart } from '..';
/**
@ -65,6 +79,27 @@ export function createPluginInitializerContext(
* Core configuration functionality, enables fetching a subset of the config.
*/
config: {
legacy: {
/**
* Global configuration
* Note: naming not final here, it will be renamed in a near future (https://github.com/elastic/kibana/issues/46240)
* @deprecated
*/
globalConfig$: combineLatest(
coreContext.configService.atPath<KibanaConfigType>(kibanaConfig.path),
coreContext.configService.atPath<ElasticsearchConfigType>(elasticsearchConfig.path),
coreContext.configService.atPath<PathConfigType>(pathConfig.path)
).pipe(
map(([kibana, elasticsearch, path]) =>
deepFreeze({
kibana: pick(kibana, SharedGlobalConfigKeys.kibana),
elasticsearch: pick(elasticsearch, SharedGlobalConfigKeys.elasticsearch),
path: pick(path, SharedGlobalConfigKeys.path),
})
)
),
},
/**
* Reads the subset of the config at the `configPath` defined in the plugin
* manifest and validates it against the schema in the static `schema` on

View file

@ -17,8 +17,11 @@
* under the License.
*/
export const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] });
jest.mock('../../../legacy/utils/package_json', () => ({ pkg: mockPackage }));
export const mockPackage = new Proxy(
{ raw: { __dirname: '/tmp' } as any },
{ get: (obj, prop) => obj.raw[prop] }
);
jest.mock('../../../core/server/utils/package_json', () => ({ pkg: mockPackage }));
export const mockDiscover = jest.fn();
jest.mock('./discovery/plugins_discovery', () => ({ discover: mockDiscover }));

View file

@ -20,8 +20,12 @@
import { Observable } from 'rxjs';
import { Type } from '@kbn/config-schema';
import { RecursiveReadonly } from 'kibana/public';
import { ConfigPath, EnvironmentMode, PackageInfo } from '../config';
import { LoggerFactory } from '../logging';
import { KibanaConfigType } from '../kibana_config';
import { ElasticsearchConfigType } from '../elasticsearch/elasticsearch_config';
import { PathConfigType } from '../path';
import { CoreSetup, CoreStart } from '..';
/**
@ -195,6 +199,19 @@ export interface Plugin<
stop?(): void;
}
export const SharedGlobalConfigKeys = {
// We can add more if really needed
kibana: ['defaultAppId', 'index'] as const,
elasticsearch: ['shardTimeout', 'requestTimeout', 'pingTimeout', 'startupTimeout'] as const,
path: ['data'] as const,
};
export type SharedGlobalConfig = RecursiveReadonly<{
kibana: Pick<KibanaConfigType, typeof SharedGlobalConfigKeys.kibana[number]>;
elasticsearch: Pick<ElasticsearchConfigType, typeof SharedGlobalConfigKeys.elasticsearch[number]>;
path: Pick<PathConfigType, typeof SharedGlobalConfigKeys.path[number]>;
}>;
/**
* Context that's available to plugins during initialization stage.
*
@ -208,6 +225,7 @@ export interface PluginInitializerContext<ConfigSchema = unknown> {
};
logger: LoggerFactory;
config: {
legacy: { globalConfig$: Observable<SharedGlobalConfig> };
create: <T = ConfigSchema>() => Observable<T>;
createIfExists: <T = ConfigSchema>() => Observable<T | undefined>;
};

View file

@ -108,6 +108,7 @@ import { PingParams } from 'elasticsearch';
import { PutScriptParams } from 'elasticsearch';
import { PutTemplateParams } from 'elasticsearch';
import { Readable } from 'stream';
import { RecursiveReadonly as RecursiveReadonly_2 } from 'kibana/public';
import { ReindexParams } from 'elasticsearch';
import { ReindexRethrottleParams } from 'elasticsearch';
import { RenderSearchTemplateParams } from 'elasticsearch';
@ -1017,6 +1018,9 @@ export type PluginInitializer<TSetup, TStart, TPluginsSetup extends object = obj
export interface PluginInitializerContext<ConfigSchema = unknown> {
// (undocumented)
config: {
legacy: {
globalConfig$: Observable<SharedGlobalConfig>;
};
create: <T = ConfigSchema>() => Observable<T>;
createIfExists: <T = ConfigSchema>() => Observable<T | undefined>;
};
@ -1759,5 +1763,6 @@ export const validBodyOutput: readonly ["data", "stream"];
//
// src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/plugins_service.ts:43:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:228:15 - (ae-forgotten-export) The symbol "SharedGlobalConfig" needs to be exported by the entry point index.d.ts
```

View file

@ -33,6 +33,7 @@ import { config as elasticsearchConfig } from './elasticsearch';
import { config as httpConfig } from './http';
import { config as loggingConfig } from './logging';
import { config as devConfig } from './dev';
import { config as pathConfig } from './path';
import { config as kibanaConfig } from './kibana_config';
import { config as savedObjectsConfig } from './saved_objects';
import { config as uiSettingsConfig } from './ui_settings';
@ -208,6 +209,7 @@ export class Server {
public async setupConfigSchemas() {
const schemas: Array<[ConfigPath, Type<unknown>]> = [
[pathConfig.path, pathConfig.schema],
[elasticsearchConfig.path, elasticsearchConfig.schema],
[loggingConfig.path, loggingConfig.schema],
[httpConfig.path, httpConfig.schema],

View file

@ -0,0 +1,21 @@
/*
* 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.
*/
export * from './from_root';
export * from './package_json';

View file

@ -20,8 +20,8 @@
import { dirname } from 'path';
export const pkg = {
__filename: require.resolve('../../../package.json'),
__dirname: dirname(require.resolve('../../../package.json')),
__filename: require.resolve('../../../../package.json'),
__dirname: dirname(require.resolve('../../../../package.json')),
// eslint-disable no-var-requires
...require('../../../package.json'),
...require('../../../../package.json'),
};

View file

@ -17,7 +17,7 @@
* under the License.
*/
export function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
export function pick<T extends object, K extends keyof T>(obj: T, keys: readonly K[]): Pick<T, K> {
return keys.reduce((acc, key) => {
if (obj.hasOwnProperty(key)) {
acc[key] = obj[key];

View file

@ -23,6 +23,8 @@ import JoiNamespace from 'joi';
import { Server } from 'hapi';
import { CoreSetup, PluginInitializerContext } from 'src/core/server';
import { i18n } from '@kbn/i18n';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getConfigPath } from '../../../core/server/path';
// @ts-ignore
import mappings from './mappings.json';
import { CONFIG_TELEMETRY, getConfigTelemetryDesc } from './common/constants';
@ -47,7 +49,7 @@ const telemetry = (kibana: any) => {
otherwise: Joi.boolean().default(true),
}),
// `config` is used internally and not intended to be set
config: Joi.string().default(Joi.ref('$defaultConfigPath')),
config: Joi.string().default(getConfigPath()),
banner: Joi.boolean().default(true),
url: Joi.when('$dev', {
is: true,

View file

@ -18,7 +18,8 @@
*/
import { fromRoot } from '../../../legacy/utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { fromRoot } from '../../../core/server/utils';
import { chain } from 'lodash';
import { resolve } from 'path';
import { fromNode } from 'bluebird';

View file

@ -23,7 +23,8 @@ import globby from 'globby';
import MultiStream from 'multistream';
import webpackMerge from 'webpack-merge';
import { fromRoot } from '../../../legacy/utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { fromRoot } from '../../../core/server/utils';
import { replacePlaceholder } from '../../../optimize/public_path_placeholder';
import findSourceFiles from './find_source_files';
import { createTestEntryTemplate } from './tests_entry_template';

View file

@ -21,9 +21,9 @@ import Joi from 'joi';
import _ from 'lodash';
import override from './override';
import createDefaultSchema from './schema';
import { getConfig } from '../path';
import { pkg, unset, deepCloneWithBuffers as clone, IS_KIBANA_DISTRIBUTABLE } from '../../utils';
import { unset, deepCloneWithBuffers as clone, IS_KIBANA_DISTRIBUTABLE } from '../../utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { pkg } from '../../../core/server/utils';
const schema = Symbol('Joi Schema');
const schemaExts = Symbol('Schema Extensions');
const vals = Symbol('config values');
@ -111,7 +111,6 @@ export class Config {
buildNum: IS_KIBANA_DISTRIBUTABLE ? pkg.build.number : Number.MAX_SAFE_INTEGER,
buildSha: IS_KIBANA_DISTRIBUTABLE ? pkg.build.sha : 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
dist: IS_KIBANA_DISTRIBUTABLE,
defaultConfigPath: getConfig(),
};
if (!context.dev && !context.prod) {

View file

@ -20,12 +20,8 @@
import Joi from 'joi';
import os from 'os';
import {
fromRoot
} from '../../utils';
import {
getData
} from '../path';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { fromRoot } from '../../../core/server/utils';
import {
DEFAULT_CSP_RULES,
DEFAULT_CSP_STRICT,
@ -152,9 +148,7 @@ export default () => Joi.object({
initialize: Joi.boolean().default(true)
}).default(),
path: Joi.object({
data: Joi.string().default(getData())
}).default(),
path: HANDLED_IN_NEW_PLATFORM,
stats: Joi.object({
maximumWaitTimeForAllCollectorsInS: Joi.number().default(60)

View file

@ -19,7 +19,8 @@
import { i18n, i18nLoader } from '@kbn/i18n';
import { basename } from 'path';
import { fromRoot } from '../../utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { fromRoot } from '../../../core/server/utils';
import { getTranslationPaths } from './get_translations_path';
import { I18N_RC } from './constants';

View file

@ -21,7 +21,8 @@ import { constant, once, compact, flatten } from 'lodash';
import { isWorker } from 'cluster';
import { fromRoot, pkg } from '../utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { fromRoot, pkg } from '../../core/server/utils';
import { Config } from './config';
import loggingConfiguration from './logging/configuration';
import httpMixin from './http';

View file

@ -17,7 +17,9 @@
* under the License.
*/
import { IS_KIBANA_DISTRIBUTABLE, fromRoot } from '../../utils';
import { IS_KIBANA_DISTRIBUTABLE } from '../../utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { fromRoot } from '../../../core/server/utils';
export async function sassMixin(kbnServer, server, config) {
if (process.env.kbnWorkerType === 'optmzr') {

View file

@ -21,7 +21,8 @@ import _ from 'lodash';
import * as states from './states';
import Status from './status';
import { pkg } from '../../utils/package_json';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { pkg } from '../../../core/server/utils';
export default class ServerStatus {
constructor(server) {

View file

@ -21,7 +21,8 @@ import expect from '@kbn/expect';
import sinon from 'sinon';
import { initChromeXsrfApi } from '../xsrf';
import { version } from '../../../../../utils/package_json';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { version } from '../../../../../../core/server/utils/package_json';
describe('chrome xsrf apis', function () {
const sandbox = sinon.createSandbox();

View file

@ -23,7 +23,8 @@ import sinon from 'sinon';
import ngMock from 'ng_mock';
import { $setupXsrfRequestInterceptor } from '../angular_config';
import { version } from '../../../../utils/package_json';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { version } from '../../../../../core/server/utils/package_json';
const xsrfHeader = 'kbn-version';
const newPlatform = {

View file

@ -26,8 +26,9 @@ import del from 'del';
import { makeRe } from 'minimatch';
import jsonStableStringify from 'json-stable-stringify';
import { IS_KIBANA_DISTRIBUTABLE, fromRoot } from '../../utils';
import { IS_KIBANA_DISTRIBUTABLE } from '../../utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { fromRoot } from '../../../core/server/utils';
import { UiBundle } from './ui_bundle';
import { appEntryTemplate } from './app_entry_template';

View file

@ -26,7 +26,8 @@ import { get } from 'lodash';
import { i18n } from '@kbn/i18n';
import { AppBootstrap } from './bootstrap';
import { mergeVariables } from './lib';
import { fromRoot } from '../../utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { fromRoot } from '../../../core/server/utils';
import { createCSPRuleString } from '../../server/csp';
export function uiRenderMixin(kbnServer, server, config) {

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { pkg } from './package_json';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { pkg } from '../../core/server/utils';
export const IS_KIBANA_DISTRIBUTABLE = pkg.build && pkg.build.distributable === true;
export const IS_KIBANA_RELEASE = pkg.build && pkg.build.release === true;

View file

@ -20,8 +20,6 @@
export { BinderBase } from './binder';
export { BinderFor } from './binder_for';
export { deepCloneWithBuffers } from './deep_clone_with_buffers';
export { fromRoot } from './from_root';
export { pkg } from './package_json';
export { unset } from './unset';
export { encodeQueryComponent } from './encode_query_component';
export { getFlattenedObject } from './get_flattened_object';

View file

@ -30,8 +30,8 @@ import { DynamicDllPlugin } from './dynamic_dll_plugin';
import { defaults } from 'lodash';
import { IS_KIBANA_DISTRIBUTABLE, fromRoot } from '../legacy/utils';
import { IS_KIBANA_DISTRIBUTABLE } from '../legacy/utils';
import { fromRoot } from '../core/server/utils';
import { PUBLIC_PATH_PLACEHOLDER } from './public_path_placeholder';
const POSTCSS_CONFIG_PATH = require.resolve('./postcss.config');

View file

@ -19,7 +19,7 @@
import { configModel } from './dll_config_model';
import { notInNodeModulesOrWebpackShims, notInNodeModules, inDllPluginPublic } from './dll_allowed_modules';
import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import { PUBLIC_PATH_PLACEHOLDER } from '../public_path_placeholder';
import fs from 'fs';
import webpack from 'webpack';

View file

@ -17,7 +17,8 @@
* under the License.
*/
import { fromRoot, IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
import { IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import webpack from 'webpack';
import webpackMerge from 'webpack-merge';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';

View file

@ -20,9 +20,7 @@
import FsOptimizer from './fs_optimizer';
import { createBundlesRoute } from './bundles_route';
import { DllCompiler } from './dynamic_dll_plugin';
import { fromRoot } from '../legacy/utils';
export default async (kbnServer, server, config) => {
import { fromRoot } from '../core/server/utils'; export default async (kbnServer, server, config) => {
if (!config.get('optimize.enabled')) return;
// the watch optimizer sets up two threads, one is the server listening

View file

@ -20,7 +20,7 @@
import BaseOptimizer from '../base_optimizer';
import { createBundlesRoute } from '../bundles_route';
import { DllCompiler } from '../dynamic_dll_plugin';
import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import * as Rx from 'rxjs';
import { mergeMap, take } from 'rxjs/operators';

View file

@ -23,7 +23,7 @@ import _ from 'lodash';
import testSubjSelector from '@kbn/test-subj-selector';
import { pkg } from '../../../../src/legacy/utils/package_json';
import { pkg } from '../../../../src/core/server/utils';
import { FtrProviderContext } from '../../ftr_provider_context';
// @ts-ignore internal js that is passed to the browser as is