Merge branch 'master' into core-explicit-setup-types

This commit is contained in:
Rudolf Meijering 2019-04-15 10:32:40 +02:00
commit b2ea641b56
1127 changed files with 54460 additions and 16640 deletions

View file

@ -33,5 +33,8 @@ bower_components
/packages/kbn-interpreter/src/common/lib/grammar.js
/x-pack/plugins/canvas/canvas_plugin
/x-pack/plugins/canvas/canvas_plugin_src/lib/flot-charts
/x-pack/plugins/infra/common/graphql/types.ts
/x-pack/plugins/infra/public/graphql/types.ts
/x-pack/plugins/infra/server/graphql/types.ts
**/*.js.snap
!/.eslintrc.js

View file

@ -132,11 +132,13 @@ module.exports = {
],
from: [
'src/core/public/**/*',
'!src/core/public/index*',
'!src/core/public/index.ts',
'!src/core/public/mocks.ts',
'!src/core/public/utils/**/*',
'src/core/server/**/*',
'!src/core/server/index*',
'!src/core/server/index.ts',
'!src/core/server/mocks.ts',
'src/plugins/**/public/**/*',
'!src/plugins/**/public/index*',

3
.github/CODEOWNERS vendored
View file

@ -36,3 +36,6 @@
/x-pack/plugins/rollup/ @elastic/es-ui
/x-pack/plugins/searchprofiler/ @elastic/es-ui
/x-pack/plugins/watcher/ @elastic/es-ui
# Kibana TSVB external contractors
/src/legacy/core_plugins/metrics/ @elastic/kibana-tsvb-external

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [Capabilities](./kibana-plugin-public.capabilities.md) &gt; [catalogue](./kibana-plugin-public.capabilities.catalogue.md)
## Capabilities.catalogue property
Catalogue capabilities. Catalogue entries drive the visibility of the Kibana homepage options.
<b>Signature:</b>
```typescript
catalogue: Record<string, boolean>;
```

View file

@ -0,0 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [Capabilities](./kibana-plugin-public.capabilities.md) &gt; [management](./kibana-plugin-public.capabilities.management.md)
## Capabilities.management property
Management section capabilities.
<b>Signature:</b>
```typescript
management: {
[sectionId: string]: Record<string, boolean>;
};
```

View file

@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [Capabilities](./kibana-plugin-public.capabilities.md)
## Capabilities interface
The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled.
<b>Signature:</b>
```typescript
export interface Capabilities
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [catalogue](./kibana-plugin-public.capabilities.catalogue.md) | <code>Record&lt;string, boolean&gt;</code> | Catalogue capabilities. Catalogue entries drive the visibility of the Kibana homepage options. |
| [management](./kibana-plugin-public.capabilities.management.md) | <code>{`<p/>` [sectionId: string]: Record&lt;string, boolean&gt;;`<p/>` }</code> | Management section capabilities. |
| [navLinks](./kibana-plugin-public.capabilities.navlinks.md) | <code>Record&lt;string, boolean&gt;</code> | Navigation link capabilities. |

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [Capabilities](./kibana-plugin-public.capabilities.md) &gt; [navLinks](./kibana-plugin-public.capabilities.navlinks.md)
## Capabilities.navLinks property
Navigation link capabilities.
<b>Signature:</b>
```typescript
navLinks: Record<string, boolean>;
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [CapabilitiesSetup](./kibana-plugin-public.capabilitiessetup.md) &gt; [getCapabilities](./kibana-plugin-public.capabilitiessetup.getcapabilities.md)
## CapabilitiesSetup.getCapabilities property
Gets the read-only capabilities.
<b>Signature:</b>
```typescript
getCapabilities: () => Capabilities;
```

View file

@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [CapabilitiesSetup](./kibana-plugin-public.capabilitiessetup.md)
## CapabilitiesSetup interface
Capabilities Setup.
<b>Signature:</b>
```typescript
export interface CapabilitiesSetup
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [getCapabilities](./kibana-plugin-public.capabilitiessetup.getcapabilities.md) | <code>() =&gt; Capabilities</code> | Gets the read-only capabilities. |

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [CoreSetup](./kibana-plugin-public.coresetup.md) &gt; [capabilities](./kibana-plugin-public.coresetup.capabilities.md)
## CoreSetup.capabilities property
<b>Signature:</b>
```typescript
capabilities: CapabilitiesSetup;
```

View file

@ -17,6 +17,7 @@ export interface CoreSetup
| Property | Type | Description |
| --- | --- | --- |
| [basePath](./kibana-plugin-public.coresetup.basepath.md) | <code>BasePathSetup</code> | |
| [capabilities](./kibana-plugin-public.coresetup.capabilities.md) | <code>CapabilitiesSetup</code> | |
| [chrome](./kibana-plugin-public.coresetup.chrome.md) | <code>ChromeSetup</code> | |
| [fatalErrors](./kibana-plugin-public.coresetup.fatalerrors.md) | <code>FatalErrorsSetup</code> | |
| [http](./kibana-plugin-public.coresetup.http.md) | <code>HttpSetup</code> | |

View file

@ -17,6 +17,8 @@
| Interface | Description |
| --- | --- |
| [BasePathSetup](./kibana-plugin-public.basepathsetup.md) | Provides access to the 'server.basePath' configuration option in kibana.yml |
| [Capabilities](./kibana-plugin-public.capabilities.md) | The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled. |
| [CapabilitiesSetup](./kibana-plugin-public.capabilitiessetup.md) | Capabilities Setup. |
| [ChromeBrand](./kibana-plugin-public.chromebrand.md) | |
| [ChromeBreadcrumb](./kibana-plugin-public.chromebreadcrumb.md) | |
| [CoreSetup](./kibana-plugin-public.coresetup.md) | Core services exposed to the start lifecycle |

View file

@ -9,13 +9,13 @@ The interface that should be returned by a `PluginInitializer`<!-- -->.
<b>Signature:</b>
```typescript
export interface Plugin<TSetup, TDependencies extends Record<string, unknown> =
export interface Plugin<TSetup, TPluginsSetup extends Record<string, unknown> =
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [setup](./kibana-plugin-public.plugin.setup.md) | <code>(core: PluginSetupContext, dependencies: TDependencies) =&gt; TSetup &#124; Promise&lt;TSetup&gt;</code> | |
| [setup](./kibana-plugin-public.plugin.setup.md) | <code>(core: PluginSetupContext, plugins: TPluginsSetup) =&gt; TSetup &#124; Promise&lt;TSetup&gt;</code> | |
| [stop](./kibana-plugin-public.plugin.stop.md) | <code>() =&gt; void</code> | |

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
setup: (core: PluginSetupContext, dependencies: TDependencies) => TSetup | Promise<TSetup>;
setup: (core: PluginSetupContext, plugins: TPluginsSetup) => TSetup | Promise<TSetup>;
```

View file

@ -9,5 +9,5 @@ The `plugin` export at the root of a plugin's `public` directory should conform
<b>Signature:</b>
```typescript
export declare type PluginInitializer<TSetup, TDependencies extends Record<string, unknown> = {}> = (core: PluginInitializerContext) => Plugin<TSetup, TDependencies>;
export declare type PluginInitializer<TSetup, TPluginsSetup extends Record<string, unknown> = {}> = (core: PluginInitializerContext) => Plugin<TSetup, TPluginsSetup>;
```

View file

@ -0,0 +1,23 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ConfigService](./kibana-plugin-server.configservice.md) &gt; [atPath](./kibana-plugin-server.configservice.atpath.md)
## ConfigService.atPath() method
Reads the subset of the config at the specified `path` and validates it against the static `schema` on the given `ConfigClass`<!-- -->.
<b>Signature:</b>
```typescript
atPath<TSchema extends Type<any>, TConfig>(path: ConfigPath, ConfigClass: ConfigWithSchema<TSchema, TConfig>): Observable<TConfig>;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| path | <code>ConfigPath</code> | The path to the desired subset of the config. |
| ConfigClass | <code>ConfigWithSchema&lt;TSchema, TConfig&gt;</code> | A class (not an instance of a class) that contains a static <code>schema</code> that we validate the config at the given <code>path</code> against. |
<b>Returns:</b>
`Observable<TConfig>`

View file

@ -0,0 +1,15 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ConfigService](./kibana-plugin-server.configservice.md) &gt; [getConfig$](./kibana-plugin-server.configservice.getconfig$.md)
## ConfigService.getConfig$() method
Returns the full config object observable. This is not intended for "normal use", but for features that \_need\_ access to the full object.
<b>Signature:</b>
```typescript
getConfig$(): Observable<Config>;
```
<b>Returns:</b>
`Observable<Config>`

View file

@ -0,0 +1,13 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ConfigService](./kibana-plugin-server.configservice.md) &gt; [getUnusedPaths](./kibana-plugin-server.configservice.getunusedpaths.md)
## ConfigService.getUnusedPaths() method
<b>Signature:</b>
```typescript
getUnusedPaths(): Promise<string[]>;
```
<b>Returns:</b>
`Promise<string[]>`

View file

@ -0,0 +1,13 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ConfigService](./kibana-plugin-server.configservice.md) &gt; [getUsedPaths](./kibana-plugin-server.configservice.getusedpaths.md)
## ConfigService.getUsedPaths() method
<b>Signature:</b>
```typescript
getUsedPaths(): Promise<string[]>;
```
<b>Returns:</b>
`Promise<string[]>`

View file

@ -0,0 +1,20 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ConfigService](./kibana-plugin-server.configservice.md) &gt; [isEnabledAtPath](./kibana-plugin-server.configservice.isenabledatpath.md)
## ConfigService.isEnabledAtPath() method
<b>Signature:</b>
```typescript
isEnabledAtPath(path: ConfigPath): Promise<boolean>;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| path | <code>ConfigPath</code> | |
<b>Returns:</b>
`Promise<boolean>`

View file

@ -0,0 +1,22 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ConfigService](./kibana-plugin-server.configservice.md)
## ConfigService class
<b>Signature:</b>
```typescript
export declare class ConfigService
```
## Methods
| Method | Modifiers | Description |
| --- | --- | --- |
| [atPath(path, ConfigClass)](./kibana-plugin-server.configservice.atpath.md) | | Reads the subset of the config at the specified <code>path</code> and validates it against the static <code>schema</code> on the given <code>ConfigClass</code>. |
| [getConfig$()](./kibana-plugin-server.configservice.getconfig$.md) | | Returns the full config object observable. This is not intended for "normal use", but for features that \_need\_ access to the full object. |
| [getUnusedPaths()](./kibana-plugin-server.configservice.getunusedpaths.md) | | |
| [getUsedPaths()](./kibana-plugin-server.configservice.getusedpaths.md) | | |
| [isEnabledAtPath(path)](./kibana-plugin-server.configservice.isenabledatpath.md) | | |
| [optionalAtPath(path, ConfigClass)](./kibana-plugin-server.configservice.optionalatpath.md) | | Same as <code>atPath</code>, but returns <code>undefined</code> if there is no config at the specified path.[ConfigService.atPath()](./kibana-plugin-server.configservice.atpath.md) |

View file

@ -0,0 +1,25 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ConfigService](./kibana-plugin-server.configservice.md) &gt; [optionalAtPath](./kibana-plugin-server.configservice.optionalatpath.md)
## ConfigService.optionalAtPath() method
Same as `atPath`<!-- -->, but returns `undefined` if there is no config at the specified path.
[ConfigService.atPath()](./kibana-plugin-server.configservice.atpath.md)
<b>Signature:</b>
```typescript
optionalAtPath<TSchema extends Type<any>, TConfig>(path: ConfigPath, ConfigClass: ConfigWithSchema<TSchema, TConfig>): Observable<TConfig | undefined>;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| path | <code>ConfigPath</code> | |
| ConfigClass | <code>ConfigWithSchema&lt;TSchema, TConfig&gt;</code> | |
<b>Returns:</b>
`Observable<TConfig | undefined>`

View file

@ -0,0 +1,9 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) &gt; [adminClient$](./kibana-plugin-server.elasticsearchservicesetup.adminclient$.md)
## ElasticsearchServiceSetup.adminClient$ property
<b>Signature:</b>
```typescript
readonly adminClient$: Observable<ClusterClient>;
```

View file

@ -0,0 +1,9 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) &gt; [createClient](./kibana-plugin-server.elasticsearchservicesetup.createclient.md)
## ElasticsearchServiceSetup.createClient property
<b>Signature:</b>
```typescript
readonly createClient: (type: string, config: ElasticsearchClientConfig) => ClusterClient;
```

View file

@ -0,0 +1,9 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) &gt; [dataClient$](./kibana-plugin-server.elasticsearchservicesetup.dataclient$.md)
## ElasticsearchServiceSetup.dataClient$ property
<b>Signature:</b>
```typescript
readonly dataClient$: Observable<ClusterClient>;
```

View file

@ -0,0 +1,11 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) &gt; [legacy](./kibana-plugin-server.elasticsearchservicesetup.legacy.md)
## ElasticsearchServiceSetup.legacy property
<b>Signature:</b>
```typescript
readonly legacy: {
readonly config$: Observable<ElasticsearchConfig>;
};
```

View file

@ -0,0 +1,20 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md)
## ElasticsearchServiceSetup interface
<b>Signature:</b>
```typescript
export interface ElasticsearchServiceSetup
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [adminClient$](./kibana-plugin-server.elasticsearchservicesetup.adminclient$.md) | <code>Observable&lt;ClusterClient&gt;</code> | |
| [createClient](./kibana-plugin-server.elasticsearchservicesetup.createclient.md) | <code>(type: string, config: ElasticsearchClientConfig) =&gt; ClusterClient</code> | |
| [dataClient$](./kibana-plugin-server.elasticsearchservicesetup.dataclient$.md) | <code>Observable&lt;ClusterClient&gt;</code> | |
| [legacy](./kibana-plugin-server.elasticsearchservicesetup.legacy.md) | <code>{`<p/>` readonly config$: Observable&lt;ElasticsearchConfig&gt;;`<p/>` }</code> | |

View file

@ -9,6 +9,7 @@
| Class | Description |
| --- | --- |
| [ClusterClient](./kibana-plugin-server.clusterclient.md) | Represents an Elasticsearch cluster API client and allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via <code>asScoped(...)</code>). |
| [ConfigService](./kibana-plugin-server.configservice.md) | |
| [ScopedClusterClient](./kibana-plugin-server.scopedclusterclient.md) | Serves the same purpose as "normal" <code>ClusterClient</code> but exposes additional <code>callAsCurrentUser</code> method that doesn't use credentials of the Kibana internal user (as <code>callAsInternalUser</code> does) to request Elasticsearch API, but rather passes HTTP headers extracted from the current user request to the API |
## Interfaces
@ -17,9 +18,11 @@
| --- | --- |
| [CallAPIOptions](./kibana-plugin-server.callapioptions.md) | The set of options that defines how API call should be made and result be processed. |
| [CoreSetup](./kibana-plugin-server.coresetup.md) | |
| [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) | |
| [Logger](./kibana-plugin-server.logger.md) | Logger exposes all the necessary methods to log any type of information and this is the interface used by the logging consumers including plugins. |
| [LoggerFactory](./kibana-plugin-server.loggerfactory.md) | The single purpose of <code>LoggerFactory</code> interface is to define a way to retrieve a context-based logger instance. |
| [LogMeta](./kibana-plugin-server.logmeta.md) | Contextual metadata |
| [Plugin](./kibana-plugin-server.plugin.md) | The interface that should be returned by a <code>PluginInitializer</code>. |
| [PluginInitializerContext](./kibana-plugin-server.plugininitializercontext.md) | Context that's available to plugins during initialization stage. |
| [PluginSetupContext](./kibana-plugin-server.pluginsetupcontext.md) | Context passed to the plugins <code>setup</code> method. |
@ -30,5 +33,6 @@
| [APICaller](./kibana-plugin-server.apicaller.md) | |
| [ElasticsearchClientConfig](./kibana-plugin-server.elasticsearchclientconfig.md) | |
| [Headers](./kibana-plugin-server.headers.md) | |
| [PluginInitializer](./kibana-plugin-server.plugininitializer.md) | The <code>plugin</code> export at the root of a plugin's <code>server</code> directory should conform to this interface. |
| [PluginName](./kibana-plugin-server.pluginname.md) | Dedicated type for plugin name/id that is supposed to make Map/Set/Arrays that use it as a key or value more obvious. |

View file

@ -0,0 +1,19 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [Plugin](./kibana-plugin-server.plugin.md)
## Plugin interface
The interface that should be returned by a `PluginInitializer`<!-- -->.
<b>Signature:</b>
```typescript
export interface Plugin<TSetup, TPluginsSetup extends Record<PluginName, unknown> =
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [setup](./kibana-plugin-server.plugin.setup.md) | <code>(pluginSetupContext: PluginSetupContext, plugins: TPluginsSetup) =&gt; TSetup &#124; Promise&lt;TSetup&gt;</code> | |
| [stop](./kibana-plugin-server.plugin.stop.md) | <code>() =&gt; void</code> | |

View file

@ -0,0 +1,9 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [Plugin](./kibana-plugin-server.plugin.md) &gt; [setup](./kibana-plugin-server.plugin.setup.md)
## Plugin.setup property
<b>Signature:</b>
```typescript
setup: (pluginSetupContext: PluginSetupContext, plugins: TPluginsSetup) => TSetup | Promise<TSetup>;
```

View file

@ -0,0 +1,9 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [Plugin](./kibana-plugin-server.plugin.md) &gt; [stop](./kibana-plugin-server.plugin.stop.md)
## Plugin.stop property
<b>Signature:</b>
```typescript
stop?: () => void;
```

View file

@ -0,0 +1,11 @@
[Home](./index) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [PluginInitializer](./kibana-plugin-server.plugininitializer.md)
## PluginInitializer type
The `plugin` export at the root of a plugin's `server` directory should conform to this interface.
<b>Signature:</b>
```typescript
export declare type PluginInitializer<TSetup, TPluginsSetup extends Record<PluginName, unknown> = {}> = (coreContext: PluginInitializerContext) => Plugin<TSetup, TPluginsSetup>;
```

View file

@ -32,9 +32,9 @@ Authorization: Basic kibana changeme
"actions":[
"version:7.0.0-alpha1-SNAPSHOT",
"action:login",
"action:saved_objects/dashboard/get",
"action:saved_objects/dashboard/bulk_get",
"action:saved_objects/dashboard/find",
"saved_object:dashboard/get",
"saved_object:dashboard/bulk_get",
"saved_object:dashboard/find",
...
],"metadata":{}}
}
@ -90,7 +90,7 @@ Authorization: Basic foo_read_only_user password
"application":"kibana-.kibana",
"resources":["*"],
"privileges":[
"action:saved_objects/dashboard/save",
"saved_object:dashboard/save",
]
}
]
@ -120,7 +120,7 @@ Authorization: Basic foo_legacy_user password
"application":"kibana-.kibana",
"resources":["*"],
"privileges":[
"action:saved_objects/dashboard/save"
"saved_object:dashboard/save"
]
}
],
@ -152,7 +152,7 @@ Here is an example response if the user does not have application privileges, bu
"application": {
"kibana-.kibana": {
"*": {
"action:saved_objects/dashboard/save": false
"saved_object:dashboard/save": false
}
}
}

View file

@ -37,7 +37,7 @@ shasum -a 512 kibana-{version}-linux-x86_64.tar.gz <1>
tar -xzf kibana-{version}-linux-x86_64.tar.gz
cd kibana-{version}-linux-x86_64/ <2>
--------------------------------------------
<1> Compare the SHA produced by or `shasum` with the
<1> Compare the SHA produced by `shasum` with the
https://artifacts.elastic.co/downloads/kibana/kibana-{version}-linux-x86_64.tar.gz.sha512[published SHA].
<2> This directory is known as `$KIBANA_HOME`.
@ -64,7 +64,7 @@ shasum -a 512 kibana-{version}-darwin-x86_64.tar.gz <1>
tar -xzf kibana-{version}-darwin-x86_64.tar.gz
cd kibana-{version}-darwin-x86_64/ <2>
--------------------------------------------
<1> Compare the SHA produced by or `shasum` with the
<1> Compare the SHA produced by `shasum` with the
https://artifacts.elastic.co/downloads/kibana/kibana-{version}-darwin-x86_64.tar.gz.sha512[published SHA].
<2> This directory is known as `$KIBANA_HOME`.

View file

@ -68,7 +68,7 @@
"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",
"core:acceptApiChanges": "yarn build:types && node scripts/check_core_api_changes.js --accept",
"core:acceptApiChanges": "node scripts/check_core_api_changes.js --accept",
"kbn:bootstrap": "yarn build:types && node scripts/register_git_hook"
},
"repository": {
@ -248,7 +248,6 @@
"webpack-merge": "4.1.4",
"whatwg-fetch": "^3.0.0",
"wreck": "^14.0.2",
"x-pack": "8.0.0",
"yauzl": "2.7.0"
},
"devDependencies": {
@ -263,7 +262,7 @@
"@kbn/plugin-generator": "1.0.0",
"@kbn/test": "1.0.0",
"@microsoft/api-documenter": "7.0.49",
"@microsoft/api-extractor": "7.0.31",
"@microsoft/api-extractor": "7.0.41",
"@octokit/rest": "^15.10.0",
"@types/angular": "1.6.50",
"@types/angular-mocks": "^1.7.0",
@ -334,7 +333,7 @@
"chance": "1.0.10",
"cheerio": "0.22.0",
"chokidar": "1.6.0",
"chromedriver": "2.42.1",
"chromedriver": "2.46.0",
"classnames": "2.2.5",
"dedent": "^0.7.0",
"delete-empty": "^2.0.0",

View file

@ -23,6 +23,11 @@ module.exports = {
],
settings: {
'import/resolver': {
node: {
extensions: ['.mjs', '.js', '.json', '.ts', '.tsx'],
},
},
react: {
version: semver.valid(semver.coerce(PKG.dependencies.react)),
},

View file

@ -156,6 +156,22 @@ ruleTester.run('@kbn/eslint/no-restricted-paths', rule, {
},
],
},
{
code: 'const d = require("./deep/d.js")',
filename: path.join(__dirname, './files/no_restricted_paths/server/b.js'),
options: [
{
basePath: __dirname,
zones: [
{
allowSameFolder: true,
target: 'files/no_restricted_paths/**/*',
from: ['files/no_restricted_paths/**/*', '!files/no_restricted_paths/server/b*'],
},
],
},
],
},
],
invalid: [

View file

@ -51,8 +51,10 @@ function traverseToTopFolder(src, pattern) {
}
function isSameFolderOrDescendent(src, imported, pattern) {
const srcFileFolderRoot = traverseToTopFolder(src, pattern);
const importedFileFolderRoot = traverseToTopFolder(imported, pattern);
// to allow to exclude file by name in pattern (e.g., !**/index*) we start with file dirname and then traverse
const srcFileFolderRoot = traverseToTopFolder(path.dirname(src), pattern);
const importedFileFolderRoot = traverseToTopFolder(path.dirname(imported), pattern);
return srcFileFolderRoot === importedFileFolderRoot;
}

View file

@ -55,6 +55,7 @@ describe('plugin generator sao integration', () => {
expect(uiExports).not.toContain('app:');
expect(uiExports).not.toContain('hacks:');
expect(uiExports).not.toContain('init(server, options)');
expect(uiExports).not.toContain('registerFeature(');
});
it('includes app when answering yes', async () => {
@ -73,8 +74,9 @@ describe('plugin generator sao integration', () => {
const uiExports = getConfig(res.files['index.js']);
expect(uiExports).toContain('app:');
expect(uiExports).toContain('init(server, options)');
expect(uiExports).toContain('registerFeature(');
expect(uiExports).not.toContain('hacks:');
expect(uiExports).not.toContain('init(server, options)');
});
it('includes hack when answering yes', async () => {
@ -94,7 +96,8 @@ describe('plugin generator sao integration', () => {
const uiExports = getConfig(res.files['index.js']);
expect(uiExports).toContain('app:');
expect(uiExports).toContain('hacks:');
expect(uiExports).not.toContain('init(server, options)');
expect(uiExports).toContain('init(server, options)');
expect(uiExports).toContain('registerFeature(');
});
it('includes server api when answering yes', async () => {
@ -115,6 +118,7 @@ describe('plugin generator sao integration', () => {
expect(uiExports).toContain('app:');
expect(uiExports).toContain('hacks:');
expect(uiExports).toContain('init(server, options)');
expect(uiExports).toContain('registerFeature(');
});
it('plugin config has correct name and main path', async () => {

View file

@ -3,6 +3,11 @@ import { resolve } from 'path';
import { existsSync } from 'fs';
<% } -%>
<% if (generateApp) { -%>
import { i18n } from '@kbn/i18n';
<% } -%>
<% if (generateApi) { -%>
import exampleRoute from './server/routes/example';
@ -34,11 +39,49 @@ export default function (kibana) {
enabled: Joi.boolean().default(true),
}).default();
},
<%_ if (generateApi) { -%>
<%_ if (generateApi || generateApp) { -%>
init(server, options) { // eslint-disable-line no-unused-vars
<%_ if (generateApp) { -%>
const xpackMainPlugin = server.plugins.xpack_main;
if (xpackMainPlugin) {
const featureId = '<%= snakeCase(name) %>';
xpackMainPlugin.registerFeature({
id: featureId,
name: i18n.translate('<%= camelCase(name) %>.featureRegistry.featureName', {
defaultMessage: '<%= name %>',
}),
navLinkId: featureId,
icon: 'questionInCircle',
app: [featureId, 'kibana'],
catalogue: [],
privileges: {
all: {
api: [],
savedObject: {
all: [],
read: ['config'],
},
ui: ['show'],
},
read: {
api: [],
savedObject: {
all: [],
read: ['config'],
},
ui: ['show'],
},
},
});
}
<%_ } -%>
<%_ if (generateApi) { -%>
// Add server routes and initialize the plugin here
exampleRoute(server);
<%_ } -%>
}
<%_ } -%>
});

View file

@ -99,8 +99,17 @@ __webpack_require__.r(__webpack_exports__);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; });
/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(131);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "copyWorkspacePackages", function() { return _utils_workspaces__WEBPACK_IMPORTED_MODULE_2__["copyWorkspacePackages"]; });
/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(35);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjects", function() { return _utils_projects__WEBPACK_IMPORTED_MODULE_2__["getProjects"]; });
/* harmony import */ var _utils_project__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(53);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return _utils_project__WEBPACK_IMPORTED_MODULE_3__["Project"]; });
/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(131);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "copyWorkspacePackages", function() { return _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__["copyWorkspacePackages"]; });
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(132);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return _config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"]; });
/*
* Licensed to Elasticsearch B.V. under one or more contributor
@ -124,6 +133,9 @@ __webpack_require__.r(__webpack_exports__);
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
@ -2646,6 +2658,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "chmod", function() { return chmod; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readFile", function() { return readFile; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mkdirp", function() { return mkdirp; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isSymlink", function() { return isSymlink; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isDirectory", function() { return isDirectory; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isFile", function() { return isFile; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createSymlink", function() { return createSymlink; });
@ -2685,7 +2698,7 @@ __webpack_require__.r(__webpack_exports__);
const stat = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.stat);
const lstat = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.lstat);
const readFile = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.readFile);
const symlink = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.symlink);
const chmod = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.chmod);
@ -2697,7 +2710,7 @@ const copyDirectory = Object(util__WEBPACK_IMPORTED_MODULE_5__["promisify"])(ncp
async function statTest(path, block) {
try {
return block((await stat(path)));
return block((await lstat(path)));
} catch (e) {
if (e.code === 'ENOENT') {
return false;
@ -2707,11 +2720,19 @@ async function statTest(path, block) {
}
}
/**
* Test if a path points to a directory.
* Test if a path points to a symlink.
* @param path
*/
async function isSymlink(path) {
return await statTest(path, stats => stats.isSymbolicLink());
}
/**
* Test if a path points to a directory.
* @param path
*/
async function isDirectory(path) {
return await statTest(path, stats => stats.isDirectory());
}
@ -7822,14 +7843,16 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return Project; });
/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(16);
/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(29);
/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(52);
/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(33);
/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(54);
/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(92);
/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(23);
/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(16);
/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(29);
/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(52);
/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(33);
/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(54);
/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(92);
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@ -7859,9 +7882,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
class Project {
static async fromPath(path) {
const pkgJson = await Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["readPackageJson"])(path);
const pkgJson = await Object(_package_json__WEBPACK_IMPORTED_MODULE_6__["readPackageJson"])(path);
return new Project(pkgJson, path);
}
@ -7890,9 +7914,9 @@ class Project {
this.json = Object.freeze(packageJson);
this.path = projectPath;
this.packageJsonLocation = Object(path__WEBPACK_IMPORTED_MODULE_1__["resolve"])(this.path, 'package.json');
this.nodeModulesLocation = Object(path__WEBPACK_IMPORTED_MODULE_1__["resolve"])(this.path, 'node_modules');
this.targetLocation = Object(path__WEBPACK_IMPORTED_MODULE_1__["resolve"])(this.path, 'target');
this.packageJsonLocation = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(this.path, 'package.json');
this.nodeModulesLocation = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(this.path, 'node_modules');
this.targetLocation = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(this.path, 'target');
this.productionDependencies = this.json.dependencies || {};
this.devDependencies = this.json.devDependencies || {};
this.allDependencies = _objectSpread({}, this.devDependencies, this.productionDependencies);
@ -7911,7 +7935,7 @@ class Project {
if (dependentProjectIsInWorkspace) {
expectedVersionInPackageJson = project.json.version;
} else {
const relativePathToProject = normalizePath(Object(path__WEBPACK_IMPORTED_MODULE_1__["relative"])(this.path, project.path));
const relativePathToProject = normalizePath(Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(this.path, project.path));
expectedVersionInPackageJson = `link:${relativePathToProject}`;
} // No issues!
@ -7922,15 +7946,15 @@ class Project {
let problemMsg;
if (Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["isLinkDependency"])(versionInPackageJson) && dependentProjectIsInWorkspace) {
if (Object(_package_json__WEBPACK_IMPORTED_MODULE_6__["isLinkDependency"])(versionInPackageJson) && dependentProjectIsInWorkspace) {
problemMsg = `but should be using a workspace`;
} else if (Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["isLinkDependency"])(versionInPackageJson)) {
} else if (Object(_package_json__WEBPACK_IMPORTED_MODULE_6__["isLinkDependency"])(versionInPackageJson)) {
problemMsg = `using 'link:', but the path is wrong`;
} else {
problemMsg = `but it's not using the local package`;
}
throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](`[${this.name}] depends on [${project.name}] ${problemMsg}. Update its package.json to the expected value below.`, {
throw new _errors__WEBPACK_IMPORTED_MODULE_4__["CliError"](`[${this.name}] depends on [${project.name}] ${problemMsg}. Update its package.json to the expected value below.`, {
actual: `"${project.name}": "${versionInPackageJson}"`,
expected: `"${project.name}": "${expectedVersionInPackageJson}"`,
package: `${this.name} (${this.packageJsonLocation})`
@ -7948,7 +7972,7 @@ class Project {
getIntermediateBuildDirectory() {
return Object(path__WEBPACK_IMPORTED_MODULE_1__["resolve"])(this.path, this.getBuildConfig().intermediateBuildDirectory || '.');
return Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(this.path, this.getBuildConfig().intermediateBuildDirectory || '.');
}
getCleanConfig() {
@ -7968,7 +7992,7 @@ class Project {
if (typeof raw === 'string') {
return {
[this.name]: Object(path__WEBPACK_IMPORTED_MODULE_1__["resolve"])(this.path, raw)
[this.name]: Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(this.path, raw)
};
}
@ -7976,25 +8000,25 @@ class Project {
const binsConfig = {};
for (const binName of Object.keys(raw)) {
binsConfig[binName] = Object(path__WEBPACK_IMPORTED_MODULE_1__["resolve"])(this.path, raw[binName]);
binsConfig[binName] = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(this.path, raw[binName]);
}
return binsConfig;
}
throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](`[${this.name}] has an invalid "bin" field in its package.json, ` + `expected an object or a string`, {
binConfig: Object(util__WEBPACK_IMPORTED_MODULE_2__["inspect"])(raw),
throw new _errors__WEBPACK_IMPORTED_MODULE_4__["CliError"](`[${this.name}] has an invalid "bin" field in its package.json, ` + `expected an object or a string`, {
binConfig: Object(util__WEBPACK_IMPORTED_MODULE_3__["inspect"])(raw),
package: `${this.name} (${this.packageJsonLocation})`
});
}
async runScript(scriptName, args = []) {
_log__WEBPACK_IMPORTED_MODULE_4__["log"].write(chalk__WEBPACK_IMPORTED_MODULE_0___default.a.bold(`\n\nRunning script [${chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(scriptName)}] in [${chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(this.name)}]:\n`));
return Object(_scripts__WEBPACK_IMPORTED_MODULE_6__["runScriptInPackage"])(scriptName, args, this);
_log__WEBPACK_IMPORTED_MODULE_5__["log"].write(chalk__WEBPACK_IMPORTED_MODULE_0___default.a.bold(`\n\nRunning script [${chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(scriptName)}] in [${chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(this.name)}]:\n`));
return Object(_scripts__WEBPACK_IMPORTED_MODULE_7__["runScriptInPackage"])(scriptName, args, this);
}
runScriptStreaming(scriptName, args = []) {
return Object(_scripts__WEBPACK_IMPORTED_MODULE_6__["runScriptInPackageStreaming"])(scriptName, args, this);
return Object(_scripts__WEBPACK_IMPORTED_MODULE_7__["runScriptInPackageStreaming"])(scriptName, args, this);
}
hasDependencies() {
@ -8004,8 +8028,45 @@ class Project {
async installDependencies({
extraArgs
}) {
_log__WEBPACK_IMPORTED_MODULE_4__["log"].write(chalk__WEBPACK_IMPORTED_MODULE_0___default.a.bold(`\n\nInstalling dependencies in [${chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(this.name)}]:\n`));
return Object(_scripts__WEBPACK_IMPORTED_MODULE_6__["installInDir"])(this.path, extraArgs);
_log__WEBPACK_IMPORTED_MODULE_5__["log"].write(chalk__WEBPACK_IMPORTED_MODULE_0___default.a.bold(`\n\nInstalling dependencies in [${chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(this.name)}]:\n`));
await Object(_scripts__WEBPACK_IMPORTED_MODULE_7__["installInDir"])(this.path, extraArgs);
await this.removeExtraneousNodeModules();
}
/**
* Yarn workspaces symlinks workspace projects to the root node_modules, even
* when there is no depenency on the project. This results in unnecicary, and
* often duplicated code in the build archives.
*/
async removeExtraneousNodeModules() {
// this is only relevant for the root workspace
if (!this.isWorkspaceRoot) {
return;
}
const workspacesInfo = await Object(_scripts__WEBPACK_IMPORTED_MODULE_7__["yarnWorkspacesInfo"])(this.path);
const unusedWorkspaces = new Set(Object.keys(workspacesInfo)); // check for any cross-project dependency
for (const name of Object.keys(workspacesInfo)) {
const workspace = workspacesInfo[name];
workspace.workspaceDependencies.forEach(w => unusedWorkspaces.delete(w));
}
unusedWorkspaces.forEach(name => {
const {
dependencies,
devDependencies
} = this.json;
const nodeModulesPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(this.nodeModulesLocation, name);
const isDependency = dependencies && dependencies.hasOwnProperty(name);
const isDevDependency = devDependencies && devDependencies.hasOwnProperty(name);
if (!isDependency && !isDevDependency && fs__WEBPACK_IMPORTED_MODULE_1___default.a.existsSync(nodeModulesPath)) {
_log__WEBPACK_IMPORTED_MODULE_5__["log"].write(`No dependency on ${name}, removing link in node_modules`);
fs__WEBPACK_IMPORTED_MODULE_1___default.a.unlinkSync(nodeModulesPath);
}
});
}
} // We normalize all path separators to `/` in generated files
@ -13451,6 +13512,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "installInDir", function() { return installInDir; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runScriptInPackage", function() { return runScriptInPackage; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runScriptInPackageStreaming", function() { return runScriptInPackageStreaming; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "yarnWorkspacesInfo", function() { return yarnWorkspacesInfo; });
/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(93);
/*
* Licensed to Elasticsearch B.V. under one or more contributor
@ -13505,6 +13567,14 @@ function runScriptInPackageStreaming(script, args, pkg) {
prefix: pkg.name
});
}
async function yarnWorkspacesInfo(directory) {
const workspacesInfo = await Object(_child_process__WEBPACK_IMPORTED_MODULE_0__["spawn"])('yarn', ['workspaces', 'info', '--json'], {
cwd: directory,
stdio: 'pipe'
});
const stdout = JSON.parse(workspacesInfo.stdout);
return JSON.parse(stdout.data);
}
/***/ }),
/* 93 */
@ -13557,17 +13627,17 @@ function generateColors() {
}
function spawn(command, args, opts) {
return execa__WEBPACK_IMPORTED_MODULE_1___default()(command, args, _objectSpread({}, opts, {
return execa__WEBPACK_IMPORTED_MODULE_1___default()(command, args, _objectSpread({
stdio: 'inherit'
}));
}, opts));
}
const nextColor = generateColors();
function spawnStreaming(command, args, opts, {
prefix
}) {
const spawned = execa__WEBPACK_IMPORTED_MODULE_1___default()(command, args, _objectSpread({}, opts, {
const spawned = execa__WEBPACK_IMPORTED_MODULE_1___default()(command, args, _objectSpread({
stdio: ['ignore', 'pipe', 'pipe']
}));
}, opts));
const color = nextColor();
const prefixedStdout = strong_log_transformer__WEBPACK_IMPORTED_MODULE_3___default()({
tag: `${color.bold(prefix)}:`
@ -17423,10 +17493,16 @@ async function workspacePackagePaths(rootPath) {
return workspaceProjectsPaths;
}
async function copyWorkspacePackages(rootPath) {
const workspaceProjects = await getWorkspaceProjects(rootPath);
const projectPaths = Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])(rootPath, {});
const projects = await Object(_projects__WEBPACK_IMPORTED_MODULE_6__["getProjects"])(rootPath, projectPaths);
for (const project of projects.values()) {
const dest = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(rootPath, 'node_modules', project.name);
if ((await Object(_fs__WEBPACK_IMPORTED_MODULE_4__["isSymlink"])(dest)) === false) {
continue;
} // Remove the symlink
for (const project of workspaceProjects.values()) {
const dest = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(rootPath, 'node_modules', project.name); // Remove the symlink
await Object(_fs__WEBPACK_IMPORTED_MODULE_4__["unlink"])(dest); // Copy in the package
@ -17434,19 +17510,6 @@ async function copyWorkspacePackages(rootPath) {
}
}
async function getWorkspaceProjects(rootPath) {
const projectPaths = Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])(rootPath, {});
const projects = await Object(_projects__WEBPACK_IMPORTED_MODULE_6__["getProjects"])(rootPath, projectPaths);
for (const [key, project] of projects.entries()) {
if (!project.isWorkspaceProject) {
projects.delete(key);
}
}
return projects;
}
function packagesFromGlobPattern({
pattern,
rootPath
@ -17496,7 +17559,7 @@ __webpack_require__.r(__webpack_exports__);
/**
* Returns all the paths where plugins are located
*/
function getProjectPaths(rootPath, options) {
function getProjectPaths(rootPath, options = {}) {
const skipKibanaPlugins = Boolean(options['skip-kibana-plugins']);
const ossOnly = Boolean(options.oss);
const projectPaths = [rootPath, Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'packages/*')]; // This is needed in order to install the dependencies for the declared
@ -31713,9 +31776,10 @@ __webpack_require__.r(__webpack_exports__);
async function buildProductionProjects({
kibanaRoot,
buildRoots
buildRoot,
onlyOSS
}) {
const projects = await getProductionProjects(kibanaRoot);
const projects = await getProductionProjects(kibanaRoot, onlyOSS);
const projectGraph = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["buildProjectGraph"])(projects);
const batchedProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["topologicallyBatchProjects"])(projects, projectGraph);
const projectNames = [...projects.values()].map(project => project.name);
@ -31725,10 +31789,7 @@ async function buildProductionProjects({
for (const project of batch) {
await deleteTarget(project);
await buildProject(project);
for (const buildRoot of buildRoots) {
await copyToBuild(project, kibanaRoot, buildRoot);
}
await copyToBuild(project, kibanaRoot, buildRoot);
}
}
}
@ -31736,17 +31797,33 @@ async function buildProductionProjects({
* Returns the subset of projects that should be built into the production
* bundle. As we copy these into Kibana's `node_modules` during the build step,
* and let Kibana's build process be responsible for installing dependencies,
* we only include Kibana's transitive _production_ dependencies.
* we only include Kibana's transitive _production_ dependencies. If onlyOSS
* is supplied, we omit projects with build.oss in their package.json set to false.
*/
async function getProductionProjects(rootPath) {
async function getProductionProjects(rootPath, onlyOSS) {
const projectPaths = Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])(rootPath, {});
const projects = await Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["getProjects"])(rootPath, projectPaths);
const productionProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["includeTransitiveProjects"])([projects.get('kibana')], projects, {
const projectsSubset = [projects.get('kibana')];
if (projects.has('x-pack')) {
projectsSubset.push(projects.get('x-pack'));
}
const productionProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["includeTransitiveProjects"])(projectsSubset, projects, {
onlyProductionDependencies: true
}); // We remove Kibana, as we're already building Kibana
productionProjects.delete('kibana');
if (onlyOSS) {
productionProjects.forEach(project => {
if (project.getBuildConfig().oss === false) {
productionProjects.delete(project.json.name);
}
});
}
return productionProjects;
}

View file

@ -27,12 +27,13 @@ import { linkProjectExecutables } from '../utils/link_project_executables';
import { IPackageJson } from '../utils/package_json';
import { Project } from '../utils/project';
import { buildProjectGraph } from '../utils/projects';
import { installInDir, runScriptInPackageStreaming } from '../utils/scripts';
import { installInDir, runScriptInPackageStreaming, yarnWorkspacesInfo } from '../utils/scripts';
import { BootstrapCommand } from './bootstrap';
const mockInstallInDir = installInDir as jest.Mock;
const mockRunScriptInPackageStreaming = runScriptInPackageStreaming as jest.Mock;
const mockLinkProjectExecutables = linkProjectExecutables as jest.Mock;
const mockYarnWorkspacesInfo = yarnWorkspacesInfo as jest.Mock;
const createProject = (packageJson: IPackageJson, path = '.') => {
const project = new Project(
@ -57,6 +58,10 @@ const noop = () => {
// noop
};
beforeEach(() => {
mockYarnWorkspacesInfo.mockResolvedValue({});
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();

View file

@ -27,7 +27,7 @@ export interface IProjectPathOptions {
/**
* Returns all the paths where plugins are located
*/
export function getProjectPaths(rootPath: string, options: IProjectPathOptions) {
export function getProjectPaths(rootPath: string, options: IProjectPathOptions = {}) {
const skipKibanaPlugins = Boolean(options['skip-kibana-plugins']);
const ossOnly = Boolean(options.oss);

View file

@ -19,4 +19,7 @@
export { run } from './cli';
export { buildProductionProjects, prepareExternalProjectDependencies } from './production';
export { getProjects } from './utils/projects';
export { Project } from './utils/project';
export { copyWorkspacePackages } from './utils/workspaces';
export { getProjectPaths } from './config';

View file

@ -35,12 +35,14 @@ import {
export async function buildProductionProjects({
kibanaRoot,
buildRoots,
buildRoot,
onlyOSS,
}: {
kibanaRoot: string;
buildRoots: string[];
buildRoot: string;
onlyOSS?: boolean;
}) {
const projects = await getProductionProjects(kibanaRoot);
const projects = await getProductionProjects(kibanaRoot, onlyOSS);
const projectGraph = buildProjectGraph(projects);
const batchedProjects = topologicallyBatchProjects(projects, projectGraph);
@ -51,9 +53,7 @@ export async function buildProductionProjects({
for (const project of batch) {
await deleteTarget(project);
await buildProject(project);
for (const buildRoot of buildRoots) {
await copyToBuild(project, kibanaRoot, buildRoot);
}
await copyToBuild(project, kibanaRoot, buildRoot);
}
}
}
@ -62,19 +62,33 @@ export async function buildProductionProjects({
* Returns the subset of projects that should be built into the production
* bundle. As we copy these into Kibana's `node_modules` during the build step,
* and let Kibana's build process be responsible for installing dependencies,
* we only include Kibana's transitive _production_ dependencies.
* we only include Kibana's transitive _production_ dependencies. If onlyOSS
* is supplied, we omit projects with build.oss in their package.json set to false.
*/
async function getProductionProjects(rootPath: string) {
async function getProductionProjects(rootPath: string, onlyOSS?: boolean) {
const projectPaths = getProjectPaths(rootPath, {});
const projects = await getProjects(rootPath, projectPaths);
const projectsSubset = [projects.get('kibana')!];
const productionProjects = includeTransitiveProjects([projects.get('kibana')!], projects, {
if (projects.has('x-pack')) {
projectsSubset.push(projects.get('x-pack')!);
}
const productionProjects = includeTransitiveProjects(projectsSubset, projects, {
onlyProductionDependencies: true,
});
// We remove Kibana, as we're already building Kibana
productionProjects.delete('kibana');
if (onlyOSS) {
productionProjects.forEach(project => {
if (project.getBuildConfig().oss === false) {
productionProjects.delete(project.json.name);
}
});
}
return productionProjects;
}

View file

@ -3,6 +3,11 @@
"version": "1.0.0",
"private": true,
"main": "./target/index.js",
"kibana": {
"build": {
"oss": false
}
},
"dependencies": {
"@elastic/bar": "link:../bar"
},

View file

@ -1,5 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`kbn-pm production builds and copies only OSS projects for production 1`] = `
Array [
"packages/bar/package.json",
"packages/bar/src/index.js",
"packages/bar/target/index.js",
"packages/bar/yarn.lock",
"packages/baz/index.js",
"packages/baz/package.json",
"packages/quux/index.js",
"packages/quux/package.json",
]
`;
exports[`kbn-pm production builds and copies projects for production 1`] = `
Array [
"packages/bar/package.json",
@ -53,6 +66,11 @@ Object {
"@babel/preset-env": "^7.3.4",
"moment": "2.20.1",
},
"kibana": Object {
"build": Object {
"oss": false,
},
},
"main": "./target/index.js",
"name": "@elastic/foo",
"private": true,

View file

@ -27,31 +27,38 @@ import { getProjects } from '../../utils/projects';
import { buildProductionProjects } from '../build_production_projects';
describe('kbn-pm production', () => {
let tmpDir: string;
let buildRoot: string;
const timeout = 1 * 60 * 1000;
beforeEach(async () => {
tmpDir = tempy.directory();
buildRoot = tempy.directory();
const fixturesPath = resolve(__dirname, '__fixtures__');
// Copy all the test fixtures into a tmp dir, as we will be mutating them
await copy(['**/*'], tmpDir, {
cwd: fixturesPath,
dot: true,
nodir: true,
parents: true,
});
const projects = await getProjects(tmpDir, ['.', './packages/*']);
for (const project of projects.values()) {
// This will both install dependencies and generate `yarn.lock` files
await project.installDependencies({
extraArgs: ['--silent', '--no-progress'],
});
}
}, timeout);
test(
'builds and copies projects for production',
async () => {
const tmpDir = tempy.directory();
const buildRoot = tempy.directory();
const fixturesPath = resolve(__dirname, '__fixtures__');
// Copy all the test fixtures into a tmp dir, as we will be mutating them
await copy(['**/*'], tmpDir, {
cwd: fixturesPath,
dot: true,
nodir: true,
parents: true,
});
const projects = await getProjects(tmpDir, ['.', './packages/*']);
for (const project of projects.values()) {
// This will both install dependencies and generate `yarn.lock` files
await project.installDependencies({
extraArgs: ['--silent', '--no-progress'],
});
}
await buildProductionProjects({ kibanaRoot: tmpDir, buildRoots: [buildRoot] });
await buildProductionProjects({ kibanaRoot: tmpDir, buildRoot });
const files = await globby(['**/*', '!**/node_modules/**'], {
cwd: buildRoot,
@ -65,6 +72,20 @@ describe('kbn-pm production', () => {
}
}
},
2 * 60 * 1000
timeout
);
test(
'builds and copies only OSS projects for production',
async () => {
await buildProductionProjects({ kibanaRoot: tmpDir, buildRoot, onlyOSS: true });
const files = await globby(['**/*', '!**/node_modules/**'], {
cwd: buildRoot,
});
expect(files.sort()).toMatchSnapshot();
},
timeout
);
});

View file

@ -14,6 +14,7 @@ Object {
"<repoRoot>/packages/kbn-pm/src/utils/bar/bin/bar.js",
],
],
"isSymlink": Array [],
"mkdirp": Array [],
"readFile": Array [],
"unlink": Array [],
@ -54,6 +55,7 @@ Object {
"<repoRoot>/packages/kbn-pm/src/utils/bar/bin/bar.js",
],
],
"isSymlink": Array [],
"mkdirp": Array [
Array [
"<repoRoot>/packages/kbn-pm/src/utils/foo/node_modules/.bin",

View file

@ -33,8 +33,8 @@ function generateColors() {
export function spawn(command: string, args: string[], opts: execa.Options) {
return execa(command, args, {
...opts,
stdio: 'inherit',
...opts,
});
}
@ -47,8 +47,8 @@ export function spawnStreaming(
{ prefix }: { prefix: string }
) {
const spawned = execa(command, args, {
...opts,
stdio: ['ignore', 'pipe', 'pipe'],
...opts,
});
const color = nextColor();

View file

@ -24,7 +24,7 @@ import { ncp } from 'ncp';
import { dirname, relative } from 'path';
import { promisify } from 'util';
const stat = promisify(fs.stat);
const lstat = promisify(fs.lstat);
const readFile = promisify(fs.readFile);
const symlink = promisify(fs.symlink);
const chmod = promisify(fs.chmod);
@ -37,7 +37,7 @@ export { chmod, readFile, mkdirp };
async function statTest(path: string, block: (stats: fs.Stats) => boolean) {
try {
return block(await stat(path));
return block(await lstat(path));
} catch (e) {
if (e.code === 'ENOENT') {
return false;
@ -46,6 +46,14 @@ async function statTest(path: string, block: (stats: fs.Stats) => boolean) {
}
}
/**
* Test if a path points to a symlink.
* @param path
*/
export async function isSymlink(path: string) {
return await statTest(path, stats => stats.isSymbolicLink());
}
/**
* Test if a path points to a directory.
* @param path

View file

@ -18,6 +18,7 @@
*/
import chalk from 'chalk';
import fs from 'fs';
import { relative, resolve as resolvePath } from 'path';
import { inspect } from 'util';
@ -30,11 +31,17 @@ import {
isLinkDependency,
readPackageJson,
} from './package_json';
import { installInDir, runScriptInPackage, runScriptInPackageStreaming } from './scripts';
import {
installInDir,
runScriptInPackage,
runScriptInPackageStreaming,
yarnWorkspacesInfo,
} from './scripts';
interface BuildConfig {
skip?: boolean;
intermediateBuildDirectory?: string;
oss?: boolean;
}
interface CleanConfig {
@ -190,7 +197,41 @@ export class Project {
public async installDependencies({ extraArgs }: { extraArgs: string[] }) {
log.write(chalk.bold(`\n\nInstalling dependencies in [${chalk.green(this.name)}]:\n`));
return installInDir(this.path, extraArgs);
await installInDir(this.path, extraArgs);
await this.removeExtraneousNodeModules();
}
/**
* Yarn workspaces symlinks workspace projects to the root node_modules, even
* when there is no depenency on the project. This results in unnecicary, and
* often duplicated code in the build archives.
*/
public async removeExtraneousNodeModules() {
// this is only relevant for the root workspace
if (!this.isWorkspaceRoot) {
return;
}
const workspacesInfo = await yarnWorkspacesInfo(this.path);
const unusedWorkspaces = new Set(Object.keys(workspacesInfo));
// check for any cross-project dependency
for (const name of Object.keys(workspacesInfo)) {
const workspace = workspacesInfo[name];
workspace.workspaceDependencies.forEach(w => unusedWorkspaces.delete(w));
}
unusedWorkspaces.forEach(name => {
const { dependencies, devDependencies } = this.json;
const nodeModulesPath = resolvePath(this.nodeModulesLocation, name);
const isDependency = dependencies && dependencies.hasOwnProperty(name);
const isDevDependency = devDependencies && devDependencies.hasOwnProperty(name);
if (!isDependency && !isDevDependency && fs.existsSync(nodeModulesPath)) {
log.write(`No dependency on ${name}, removing link in node_modules`);
fs.unlinkSync(nodeModulesPath);
}
});
}
}

View file

@ -20,6 +20,15 @@
import { spawn, spawnStreaming } from './child_process';
import { Project } from './project';
interface WorkspaceInfo {
location: string;
workspaceDependencies: string[];
}
interface WorkspacesInfo {
[s: string]: WorkspaceInfo;
}
/**
* Install all dependencies in the given directory
*/
@ -56,3 +65,13 @@ export function runScriptInPackageStreaming(script: string, args: string[], pkg:
prefix: pkg.name,
});
}
export async function yarnWorkspacesInfo(directory: string): Promise<WorkspacesInfo> {
const workspacesInfo = await spawn('yarn', ['workspaces', 'info', '--json'], {
cwd: directory,
stdio: 'pipe',
});
const stdout = JSON.parse(workspacesInfo.stdout);
return JSON.parse(stdout.data);
}

View file

@ -22,7 +22,7 @@ import path from 'path';
import { promisify } from 'util';
import { getProjectPaths } from '../config';
import { copyDirectory, unlink } from './fs';
import { copyDirectory, isSymlink, unlink } from './fs';
import { readPackageJson } from './package_json';
import { getProjects } from './projects';
@ -56,29 +56,22 @@ export async function workspacePackagePaths(rootPath: string): Promise<string[]>
}
export async function copyWorkspacePackages(rootPath: string): Promise<void> {
const workspaceProjects = await getWorkspaceProjects(rootPath);
for (const project of workspaceProjects.values()) {
const dest = path.resolve(rootPath, 'node_modules', project.name);
// Remove the symlink
await unlink(dest);
// Copy in the package
await copyDirectory(project.path, dest);
}
}
async function getWorkspaceProjects(rootPath: string) {
const projectPaths = getProjectPaths(rootPath, {});
const projects = await getProjects(rootPath, projectPaths);
for (const [key, project] of projects.entries()) {
if (!project.isWorkspaceProject) {
projects.delete(key);
}
}
for (const project of projects.values()) {
const dest = path.resolve(rootPath, 'node_modules', project.name);
return projects;
if ((await isSymlink(dest)) === false) {
continue;
}
// Remove the symlink
await unlink(dest);
// Copy in the package
await copyDirectory(project.path, dest);
}
}
function packagesFromGlobPattern({ pattern, rootPath }: { pattern: string; rootPath: string }) {

View file

@ -47,6 +47,7 @@ export function KuiListingTable({
toolBarActions,
onFilter,
onItemSelectionChanged,
enableSelection,
selectedRowIds,
filter,
prompt,
@ -76,11 +77,12 @@ export function KuiListingTable({
}
}
function renderTableRows() {
function renderTableRows(enableSelection) {
return rows.map((row, rowIndex) => {
return (
<KuiListingTableRow
key={rowIndex}
enableSelection={enableSelection}
isSelected={selectedRowIds.indexOf(row.id) >= 0}
onSelectionChanged={toggleRow}
row={row}
@ -111,15 +113,17 @@ export function KuiListingTable({
return (
<KuiTable>
<KuiTableHeader>
<KuiTableHeaderCheckBoxCell
isChecked={areAllRowsSelected()}
onChange={toggleAll}
/>
{enableSelection &&
<KuiTableHeaderCheckBoxCell
isChecked={areAllRowsSelected()}
onChange={toggleAll}
/>
}
{renderHeader()}
</KuiTableHeader>
<KuiTableBody>
{renderTableRows()}
{renderTableRows(enableSelection)}
</KuiTableBody>
</KuiTable>
);
@ -171,6 +175,7 @@ KuiListingTable.propTypes = {
})),
pager: PropTypes.node,
onItemSelectionChanged: PropTypes.func.isRequired,
enableSelection: PropTypes.bool,
selectedRowIds: PropTypes.array,
prompt: PropTypes.node, // If given, will be shown instead of a table with rows.
onFilter: PropTypes.func,
@ -181,4 +186,5 @@ KuiListingTable.propTypes = {
KuiListingTable.defaultProps = {
rows: [],
selectedRowIds: [],
enableSelection: true,
};

View file

@ -57,13 +57,15 @@ export class KuiListingTableRow extends React.PureComponent {
}
render() {
const { isSelected } = this.props;
const { enableSelection, isSelected } = this.props;
return (
<KuiTableRow>
<KuiTableRowCheckBoxCell
isChecked={isSelected}
onChange={this.onSelectionChanged}
/>
{enableSelection &&
<KuiTableRowCheckBoxCell
isChecked={isSelected}
onChange={this.onSelectionChanged}
/>
}
{this.renderCells()}
</KuiTableRow>
);
@ -83,6 +85,11 @@ KuiListingTableRow.propTypes = {
],
)),
}).isRequired,
enableSelection: PropTypes.bool,
onSelectionChanged: PropTypes.func.isRequired,
isSelected: PropTypes.bool,
};
KuiListingTableRow.defaultProps = {
enableSelection: true,
};

View file

@ -18,10 +18,10 @@
*/
import _ from 'lodash';
import { statSync, lstatSync, realpathSync } from 'fs';
import { statSync } from 'fs';
import { resolve } from 'path';
import { fromRoot } from '../../legacy/utils';
import { fromRoot, IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
import { getConfig } from '../../legacy/server/path';
import { bootstrap } from '../../core/server';
import { readKeystore } from './read_keystore';
@ -41,17 +41,6 @@ function canRequire(path) {
}
}
function isSymlinkTo(link, dest) {
try {
const stat = lstatSync(link);
return stat.isSymbolicLink() && realpathSync(link) === dest;
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
}
const CLUSTER_MANAGER_PATH = resolve(__dirname, '../cluster/cluster_manager');
const CAN_CLUSTER = canRequire(CLUSTER_MANAGER_PATH);
@ -60,10 +49,8 @@ const CAN_REPL = canRequire(REPL_PATH);
// xpack is installed in both dev and the distributable, it's optional if
// install is a link to the source, not an actual install
const XPACK_INSTALLED_DIR = resolve(__dirname, '../../../node_modules/x-pack');
const XPACK_SOURCE_DIR = resolve(__dirname, '../../../x-pack');
const XPACK_INSTALLED = canRequire(XPACK_INSTALLED_DIR);
const XPACK_OPTIONAL = isSymlinkTo(XPACK_INSTALLED_DIR, XPACK_SOURCE_DIR);
const XPACK_DIR = resolve(__dirname, '../../../x-pack');
const XPACK_INSTALLED = canRequire(XPACK_DIR);
const pathCollector = function () {
const paths = [];
@ -127,8 +114,8 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) {
get('plugins.paths'),
opts.pluginPath,
XPACK_INSTALLED && (!XPACK_OPTIONAL || !opts.oss)
? [XPACK_INSTALLED_DIR]
XPACK_INSTALLED && !opts.oss
? [XPACK_DIR]
: [],
)));
@ -183,7 +170,7 @@ export default function (program) {
command.option('--repl', 'Run the server with a REPL prompt and access to the server object');
}
if (XPACK_OPTIONAL) {
if (!IS_KIBANA_DISTRIBUTABLE) {
command
.option('--oss', 'Start Kibana without X-Pack');
}
@ -226,7 +213,7 @@ export default function (program) {
},
features: {
isClusterModeSupported: CAN_CLUSTER,
isOssModeSupported: XPACK_OPTIONAL,
isOssModeSupported: !IS_KIBANA_DISTRIBUTABLE,
isXPackInstalled: XPACK_INSTALLED,
isReplModeSupported: CAN_REPL,
},

View file

@ -17,15 +17,9 @@
* under the License.
*/
export function isOSS() {
try {
require.resolve('x-pack');
return false;
} catch (error) {
if (error.code !== 'MODULE_NOT_FOUND') {
throw error;
}
import fs from 'fs';
import path from 'path';
return true;
}
export function isOSS() {
return !fs.existsSync(path.resolve(__dirname, '../../../x-pack'));
}

View file

@ -0,0 +1,45 @@
/*
* 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 { Capabilities, CapabilitiesService, CapabilitiesSetup } from './capabilities_service';
const createSetupContractMock = () => {
const setupContract: jest.Mocked<CapabilitiesSetup> = {
getCapabilities: jest.fn(),
};
setupContract.getCapabilities.mockReturnValue({
catalogue: {},
management: {},
navLinks: {},
} as Capabilities);
return setupContract;
};
type CapabilitiesServiceContract = PublicMethodsOf<CapabilitiesService>;
const createMock = () => {
const mocked: jest.Mocked<CapabilitiesServiceContract> = {
setup: jest.fn(),
};
mocked.setup.mockReturnValue(createSetupContractMock());
return mocked;
};
export const capabilitiesServiceMock = {
create: createMock,
createSetupContract: createSetupContractMock,
};

View file

@ -0,0 +1,60 @@
/*
* 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 { InjectedMetadataService } from '../injected_metadata';
import { CapabilitiesService } from './capabilities_service';
describe('#start', () => {
it('returns a service with getCapabilities', () => {
const injectedMetadata = new InjectedMetadataService({
injectedMetadata: {
vars: {
uiCapabilities: {
foo: 'bar',
bar: 'baz',
},
},
} as any,
});
const service = new CapabilitiesService();
const startContract = service.setup({ injectedMetadata: injectedMetadata.setup() });
expect(startContract.getCapabilities()).toEqual({
foo: 'bar',
bar: 'baz',
});
});
it(`does not allow Capabilities to be modified`, () => {
const injectedMetadata = new InjectedMetadataService({
injectedMetadata: {
vars: {
uiCapabilities: {
foo: 'bar',
bar: 'baz',
},
},
} as any,
});
const service = new CapabilitiesService();
const startContract = service.setup({ injectedMetadata: injectedMetadata.setup() });
const capabilities = startContract.getCapabilities();
// @ts-ignore TypeScript knows this shouldn't be possible
expect(() => (capabilities.foo = 'foo')).toThrowError();
});
});

View file

@ -0,0 +1,72 @@
/*
* 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 { InjectedMetadataSetup } from '../injected_metadata';
import { deepFreeze } from '../utils/deep_freeze';
interface StartDeps {
injectedMetadata: InjectedMetadataSetup;
}
/**
* The read-only set of capabilities available for the current UI session.
* Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID,
* and the boolean is a flag indicating if the capability is enabled or disabled.
*
* @public
*/
export interface Capabilities {
/** Navigation link capabilities. */
navLinks: Record<string, boolean>;
/** Management section capabilities. */
management: {
[sectionId: string]: Record<string, boolean>;
};
/** Catalogue capabilities. Catalogue entries drive the visibility of the Kibana homepage options. */
catalogue: Record<string, boolean>;
/** Custom capabilities, registered by plugins. */
[key: string]: Record<string, boolean | Record<string, boolean>>;
}
/**
* Capabilities Setup.
* @public
*/
export interface CapabilitiesSetup {
/**
* Gets the read-only capabilities.
*/
getCapabilities: () => Capabilities;
}
/** @internal */
/**
* Service that is responsible for UI Capabilities.
*/
export class CapabilitiesService {
public setup({ injectedMetadata }: StartDeps): CapabilitiesSetup {
return {
getCapabilities: () =>
deepFreeze<Capabilities>(injectedMetadata.getInjectedVar('uiCapabilities') as Capabilities),
};
}
}

View file

@ -0,0 +1,20 @@
/*
* 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 { Capabilities, CapabilitiesService, CapabilitiesSetup } from './capabilities_service';

View file

@ -23,6 +23,7 @@ import { Subject } from 'rxjs';
import { CoreSetup } from '.';
import { BasePathService } from './base_path';
import { CapabilitiesService } from './capabilities';
import { ChromeService } from './chrome';
import { FatalErrorsService } from './fatal_errors';
import { HttpService } from './http';
@ -64,6 +65,7 @@ export class CoreSystem {
private readonly basePath: BasePathService;
private readonly chrome: ChromeService;
private readonly i18n: I18nService;
private readonly capabilities: CapabilitiesService;
private readonly overlay: OverlayService;
private readonly plugins: PluginsService;
@ -85,6 +87,8 @@ export class CoreSystem {
this.i18n = new I18nService();
this.capabilities = new CapabilitiesService();
this.injectedMetadata = new InjectedMetadataService({
injectedMetadata,
});
@ -128,6 +132,7 @@ export class CoreSystem {
const http = this.http.setup({ fatalErrors });
const overlays = this.overlay.setup({ i18n });
const basePath = this.basePath.setup({ injectedMetadata });
const capabilities = this.capabilities.setup({ injectedMetadata });
const uiSettings = this.uiSettings.setup({
notifications,
http,
@ -145,6 +150,7 @@ export class CoreSystem {
fatalErrors,
http,
i18n,
capabilities,
injectedMetadata,
notifications,
uiSettings,

View file

@ -18,6 +18,7 @@
*/
import { BasePathSetup } from './base_path';
import { Capabilities, CapabilitiesSetup } from './capabilities';
import { ChromeBrand, ChromeBreadcrumb, ChromeHelpExtension, ChromeSetup } from './chrome';
import { FatalErrorsSetup } from './fatal_errors';
import { HttpSetup } from './http';
@ -42,6 +43,7 @@ export interface CoreSetup {
notifications: NotificationsSetup;
http: HttpSetup;
basePath: BasePathSetup;
capabilities: CapabilitiesSetup;
uiSettings: UiSettingsSetup;
chrome: ChromeSetup;
overlays: OverlaySetup;
@ -52,6 +54,8 @@ export {
HttpSetup,
FatalErrorsSetup,
I18nSetup,
CapabilitiesSetup,
Capabilities,
ChromeSetup,
ChromeBreadcrumb,
ChromeBrand,

View file

@ -20,7 +20,7 @@
import { get } from 'lodash';
import { DiscoveredPlugin, PluginName } from '../../server';
import { UiSettingsState } from '../ui_settings';
import { deepFreeze } from './deep_freeze';
import { deepFreeze } from '../utils/deep_freeze';
/** @internal */
export interface InjectedMetadataParams {

View file

@ -6,6 +6,7 @@ Array [
"ui/i18n",
"ui/notify/fatal_error",
"ui/notify/toasts",
"ui/capabilities",
"ui/chrome/api/loading_count",
"ui/chrome/api/base_path",
"ui/chrome/api/ui_settings",
@ -26,6 +27,7 @@ Array [
"ui/i18n",
"ui/notify/fatal_error",
"ui/notify/toasts",
"ui/capabilities",
"ui/chrome/api/loading_count",
"ui/chrome/api/base_path",
"ui/chrome/api/ui_settings",

View file

@ -53,6 +53,14 @@ jest.mock('ui/i18n', () => {
};
});
const mockUICapabilitiesInit = jest.fn();
jest.mock('ui/capabilities', () => {
mockLoadOrder.push('ui/capabilities');
return {
__newPlatformInit__: mockUICapabilitiesInit,
};
});
const mockFatalErrorInit = jest.fn();
jest.mock('ui/notify/fatal_error', () => {
mockLoadOrder.push('ui/notify/fatal_error');
@ -142,6 +150,7 @@ jest.mock('ui/chrome/services/global_nav_state', () => {
});
import { basePathServiceMock } from '../base_path/base_path_service.mock';
import { capabilitiesServiceMock } from '../capabilities/capabilities_service.mock';
import { chromeServiceMock } from '../chrome/chrome_service.mock';
import { fatalErrorsServiceMock } from '../fatal_errors/fatal_errors_service.mock';
import { httpServiceMock } from '../http/http_service.mock';
@ -159,6 +168,7 @@ const httpSetup = httpServiceMock.createSetupContract();
const i18nSetup = i18nServiceMock.createSetupContract();
const injectedMetadataSetup = injectedMetadataServiceMock.createSetupContract();
const notificationsSetup = notificationServiceMock.createSetupContract();
const capabilitiesSetup = capabilitiesServiceMock.createSetupContract();
const uiSettingsSetup = uiSettingsServiceMock.createSetupContract();
const overlaySetup = overlayServiceMock.createSetupContract();
@ -176,6 +186,7 @@ const defaultSetupDeps = {
notifications: notificationsSetup,
http: httpSetup,
basePath: basePathSetup,
capabilities: capabilitiesSetup,
uiSettings: uiSettingsSetup,
chrome: chromeSetup,
overlays: overlaySetup,
@ -215,6 +226,17 @@ describe('#setup()', () => {
expect(mockI18nContextInit).toHaveBeenCalledWith(i18nSetup.Context);
});
it('passes uiCapabilities to ui/capabilities', () => {
const legacyPlatform = new LegacyPlatformService({
...defaultParams,
});
legacyPlatform.setup(defaultSetupDeps);
expect(mockUICapabilitiesInit).toHaveBeenCalledTimes(1);
expect(mockUICapabilitiesInit).toHaveBeenCalledWith(capabilitiesSetup);
});
it('passes fatalErrors service to ui/notify/fatal_errors', () => {
const legacyPlatform = new LegacyPlatformService({
...defaultParams,

View file

@ -44,6 +44,7 @@ export class LegacyPlatformService {
notifications,
http,
basePath,
capabilities,
uiSettings,
chrome,
} = core;
@ -54,6 +55,7 @@ export class LegacyPlatformService {
require('ui/i18n').__newPlatformInit__(i18n.Context);
require('ui/notify/fatal_error').__newPlatformInit__(fatalErrors);
require('ui/notify/toasts').__newPlatformInit__(notifications.toasts);
require('ui/capabilities').__newPlatformInit__(capabilities);
require('ui/chrome/api/loading_count').__newPlatformInit__(http);
require('ui/chrome/api/base_path').__newPlatformInit__(basePath);
require('ui/chrome/api/ui_settings').__newPlatformInit__(uiSettings);

View file

@ -26,8 +26,8 @@ import { loadPluginBundle } from './plugin_loader';
*
* @public
*/
export interface Plugin<TSetup, TDependencies extends Record<string, unknown> = {}> {
setup: (core: PluginSetupContext, dependencies: TDependencies) => TSetup | Promise<TSetup>;
export interface Plugin<TSetup, TPluginsSetup extends Record<string, unknown> = {}> {
setup: (core: PluginSetupContext, plugins: TPluginsSetup) => TSetup | Promise<TSetup>;
stop?: () => void;
}
@ -37,9 +37,9 @@ export interface Plugin<TSetup, TDependencies extends Record<string, unknown> =
*
* @public
*/
export type PluginInitializer<TSetup, TDependencies extends Record<string, unknown> = {}> = (
export type PluginInitializer<TSetup, TPluginsSetup extends Record<string, unknown> = {}> = (
core: PluginInitializerContext
) => Plugin<TSetup, TDependencies>;
) => Plugin<TSetup, TPluginsSetup>;
/**
* Lightweight wrapper around discovered plugin that is responsible for instantiating
@ -49,14 +49,14 @@ export type PluginInitializer<TSetup, TDependencies extends Record<string, unkno
*/
export class PluginWrapper<
TSetup = unknown,
TDependenciesSetup extends Record<PluginName, unknown> = Record<PluginName, unknown>
TPluginsSetup extends Record<PluginName, unknown> = Record<PluginName, unknown>
> {
public readonly name: DiscoveredPlugin['id'];
public readonly configPath: DiscoveredPlugin['configPath'];
public readonly requiredDependencies: DiscoveredPlugin['requiredPlugins'];
public readonly optionalDependencies: DiscoveredPlugin['optionalPlugins'];
private initializer?: PluginInitializer<TSetup, TDependenciesSetup>;
private instance?: Plugin<TSetup, TDependenciesSetup>;
public readonly requiredPlugins: DiscoveredPlugin['requiredPlugins'];
public readonly optionalPlugins: DiscoveredPlugin['optionalPlugins'];
private initializer?: PluginInitializer<TSetup, TPluginsSetup>;
private instance?: Plugin<TSetup, TPluginsSetup>;
constructor(
readonly discoveredPlugin: DiscoveredPlugin,
@ -64,8 +64,8 @@ export class PluginWrapper<
) {
this.name = discoveredPlugin.id;
this.configPath = discoveredPlugin.configPath;
this.requiredDependencies = discoveredPlugin.requiredPlugins;
this.optionalDependencies = discoveredPlugin.optionalPlugins;
this.requiredPlugins = discoveredPlugin.requiredPlugins;
this.optionalPlugins = discoveredPlugin.optionalPlugins;
}
/**
@ -74,20 +74,20 @@ export class PluginWrapper<
* @param addBasePath Function that adds the base path to a string for plugin bundle path.
*/
public async load(addBasePath: (path: string) => string) {
this.initializer = await loadPluginBundle<TSetup, TDependenciesSetup>(addBasePath, this.name);
this.initializer = await loadPluginBundle<TSetup, TPluginsSetup>(addBasePath, this.name);
}
/**
* Instantiates plugin and calls `setup` function exposed by the plugin initializer.
* @param setupContext Context that consists of various core services tailored specifically
* for the `setup` lifecycle event.
* @param dependencies The dictionary where the key is the dependency name and the value
* @param plugins The dictionary where the key is the dependency name and the value
* is the contract returned by the dependency's `setup` function.
*/
public async setup(setupContext: PluginSetupContext, dependencies: TDependenciesSetup) {
public async setup(setupContext: PluginSetupContext, plugins: TPluginsSetup) {
this.instance = await this.createPluginInstance();
return await this.instance.setup(setupContext, dependencies);
return await this.instance.setup(setupContext, plugins);
}
/**

View file

@ -62,34 +62,35 @@ export class PluginsService implements CoreService<PluginsServiceSetup> {
// Load plugin bundles
await this.loadPluginBundles(deps.basePath.addToPath);
// Setup each plugin with correct dependencies
// Setup each plugin with required and optional plugin contracts
const contracts = new Map<string, unknown>();
for (const [pluginName, plugin] of this.plugins.entries()) {
const dependencies = new Set([
...plugin.requiredDependencies,
...plugin.optionalDependencies.filter(optPlugin => this.plugins.get(optPlugin)),
const pluginDeps = new Set([
...plugin.requiredPlugins,
...plugin.optionalPlugins.filter(optPlugin => this.plugins.get(optPlugin)),
]);
const dependencyContracts = [...dependencies.keys()].reduce(
(depContracts, dependency) => {
const pluginDepContracts = [...pluginDeps.keys()].reduce(
(depContracts, dependencyName) => {
// Only set if present. Could be absent if plugin does not have client-side code or is a
// missing optional dependency.
if (contracts.get(dependency) !== undefined) {
depContracts[dependency] = contracts.get(dependency);
// missing optional plugin.
if (contracts.has(dependencyName)) {
depContracts[dependencyName] = contracts.get(dependencyName);
}
return depContracts;
},
{} as { [dep: string]: unknown }
{} as Record<PluginName, unknown>
);
contracts.set(
pluginName,
await plugin.setup(
createPluginSetupContext(this.coreContext, deps, plugin),
dependencyContracts
pluginDepContracts
)
);
this.satupPlugins.push(pluginName);
}
@ -98,7 +99,7 @@ export class PluginsService implements CoreService<PluginsServiceSetup> {
}
public async stop() {
// Stop plugins in reverse dependency order.
// Stop plugins in reverse topological order.
for (const pluginName of this.satupPlugins.reverse()) {
this.plugins.get(pluginName)!.stop();
}

View file

@ -1,4 +1,4 @@
## API Review File for "kibana"
## API Report File for "kibana"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
@ -18,6 +18,21 @@ export interface BasePathSetup {
removeFromPath(path: string): string;
}
// @public
export interface Capabilities {
[key: string]: Record<string, boolean | Record<string, boolean>>;
catalogue: Record<string, boolean>;
management: {
[sectionId: string]: Record<string, boolean>;
};
navLinks: Record<string, boolean>;
}
// @public
export interface CapabilitiesSetup {
getCapabilities: () => Capabilities;
}
// @public (undocumented)
export interface ChromeBrand {
// (undocumented)
@ -39,13 +54,9 @@ export interface ChromeBreadcrumb {
// @public (undocumented)
export type ChromeHelpExtension = (element: HTMLDivElement) => (() => void);
// Warning: (ae-forgotten-export) The symbol "ChromeService" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type ChromeSetup = ReturnType<ChromeService['setup']>;
// Warning: (ae-internal-missing-underscore) The name CoreContext should be prefixed with an underscore because the declaration is marked as "@internal"
//
// @internal (undocumented)
export interface CoreContext {
}
@ -55,6 +66,8 @@ export interface CoreSetup {
// (undocumented)
basePath: BasePathSetup;
// (undocumented)
capabilities: CapabilitiesSetup;
// (undocumented)
chrome: ChromeSetup;
// (undocumented)
fatalErrors: FatalErrorsSetup;
@ -72,13 +85,8 @@ export interface CoreSetup {
uiSettings: UiSettingsSetup;
}
// Warning: (ae-internal-missing-underscore) The name CoreSystem should be prefixed with an underscore because the declaration is marked as "@internal"
//
// @internal
export class CoreSystem {
// Warning: (ae-forgotten-export) The symbol "Params" needs to be exported by the entry point index.d.ts
//
// (undocumented)
constructor(params: Params);
// (undocumented)
setup(): Promise<{
@ -91,20 +99,16 @@ export class CoreSystem {
// @public
export interface FatalErrorsSetup {
add: (error: string | Error, source?: string) => never;
// Warning: (ae-forgotten-export) The symbol "ErrorInfo" needs to be exported by the entry point index.d.ts
get$: () => Rx.Observable<ErrorInfo>;
}
// @public
export class FlyoutRef {
// (undocumented)
constructor();
close(): Promise<void>;
readonly onClose: Promise<void>;
}
// Warning: (ae-forgotten-export) The symbol "HttpService" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type HttpSetup = ReturnType<HttpService['setup']>;
@ -115,8 +119,6 @@ export interface I18nSetup {
}) => JSX.Element;
}
// Warning: (ae-internal-missing-underscore) The name InjectedMetadataParams should be prefixed with an underscore because the declaration is marked as "@internal"
//
// @internal (undocumented)
export interface InjectedMetadataParams {
// (undocumented)
@ -194,15 +196,11 @@ export interface InjectedMetadataSetup {
}>;
}
// Warning: (ae-forgotten-export) The symbol "NotificationsService" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type NotificationsSetup = ReturnType<NotificationsService['setup']>;
// @public (undocumented)
export interface OverlaySetup {
// Warning: (ae-forgotten-export) The symbol "React" needs to be exported by the entry point index.d.ts
//
// (undocumented)
openFlyout: (flyoutChildren: React.ReactNode, flyoutProps?: {
closeButtonAriaLabel?: string;
@ -211,15 +209,15 @@ export interface OverlaySetup {
}
// @public
export interface Plugin<TSetup, TDependencies extends Record<string, unknown> = {}> {
export interface Plugin<TSetup, TPluginsSetup extends Record<string, unknown> = {}> {
// (undocumented)
setup: (core: PluginSetupContext, dependencies: TDependencies) => TSetup | Promise<TSetup>;
setup: (core: PluginSetupContext, plugins: TPluginsSetup) => TSetup | Promise<TSetup>;
// (undocumented)
stop?: () => void;
}
// @public
export type PluginInitializer<TSetup, TDependencies extends Record<string, unknown> = {}> = (core: PluginInitializerContext) => Plugin<TSetup, TDependencies>;
export type PluginInitializer<TSetup, TPluginsSetup extends Record<string, unknown> = {}> = (core: PluginInitializerContext) => Plugin<TSetup, TPluginsSetup>;
// @public
export interface PluginInitializerContext {
@ -264,7 +262,6 @@ export class ToastsSetup {
// @public (undocumented)
export class UiSettingsClient {
// (undocumented)
constructor(params: UiSettingsClientParams);
get$(key: string, defaultOverride?: any): Rx.Observable<any>;
get(key: string, defaultOverride?: any): any;
@ -284,8 +281,6 @@ export class UiSettingsClient {
isDefault(key: string): boolean;
isOverridden(key: string): boolean;
overrideLocalDefault(key: string, newDefault: any): void;
// Warning: (ae-forgotten-export) The symbol "UiSettingsClientParams" needs to be exported by the entry point index.d.ts
//
// (undocumented)
readonly params: UiSettingsClientParams;
remove(key: string): Promise<boolean>;
@ -298,19 +293,11 @@ export type UiSettingsSetup = UiSettingsClient;
// @public (undocumented)
export interface UiSettingsState {
// Warning: (ae-forgotten-export) The symbol "InjectedUiSettingsDefault" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "InjectedUiSettingsUser" needs to be exported by the entry point index.d.ts
//
// (undocumented)
[key: string]: InjectedUiSettingsDefault & InjectedUiSettingsUser;
}
// Warnings were encountered during analysis:
//
// src/core/public/injected_metadata/injected_metadata_service.ts:38:9 - (ae-forgotten-export) The symbol "PluginName" needs to be exported by the entry point index.d.ts
// src/core/public/injected_metadata/injected_metadata_service.ts:39:9 - (ae-forgotten-export) The symbol "DiscoveredPlugin" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)
```

View file

@ -26,6 +26,10 @@ interface InjectedUiSettingsDefault {
type?: string;
readOnly?: boolean;
options?: string[] | { [key: string]: any };
/**
* Whether a change in that setting will only take affect after a page reload.
*/
requiresPageReload?: boolean;
}
// properties that come from legacyInjectedMetadata.uiSettings.user

View file

@ -25,7 +25,7 @@ import { distinctUntilChanged, first, map } from 'rxjs/operators';
import { Config, ConfigPath, ConfigWithSchema, Env } from '.';
import { Logger, LoggerFactory } from '../logging';
/** @internal */
/** @public */
export class ConfigService {
private readonly log: Logger;
@ -37,7 +37,7 @@ export class ConfigService {
constructor(
private readonly config$: Observable<Config>,
readonly env: Env,
private readonly env: Env,
logger: LoggerFactory
) {
this.log = logger.get('config');
@ -55,8 +55,8 @@ export class ConfigService {
* Reads the subset of the config at the specified `path` and validates it
* against the static `schema` on the given `ConfigClass`.
*
* @param path The path to the desired subset of the config.
* @param ConfigClass A class (not an instance of a class) that contains a
* @param path - The path to the desired subset of the config.
* @param ConfigClass - A class (not an instance of a class) that contains a
* static `schema` that we validate the config at the given `path` against.
*/
public atPath<TSchema extends Type<any>, TConfig>(
@ -72,7 +72,7 @@ export class ConfigService {
* Same as `atPath`, but returns `undefined` if there is no config at the
* specified path.
*
* @see atPath
* {@link ConfigService.atPath}
*/
public optionalAtPath<TSchema extends Type<any>, TConfig>(
path: ConfigPath,

View file

@ -26,12 +26,14 @@ import { ClusterClient } from './cluster_client';
import { ElasticsearchClientConfig } from './elasticsearch_client_config';
import { ElasticsearchConfig } from './elasticsearch_config';
/** @internal */
interface CoreClusterClients {
config: ElasticsearchConfig;
adminClient: ClusterClient;
dataClient: ClusterClient;
}
/** @public */
export interface ElasticsearchServiceSetup {
// Required for the BWC with the legacy Kibana only.
readonly legacy: {

View file

@ -21,6 +21,7 @@ import { HttpServiceSetup } from './http';
import { PluginsServiceSetup } from './plugins';
export { bootstrap } from './bootstrap';
export { ConfigService } from './config';
export {
CallAPIOptions,
ClusterClient,
@ -30,8 +31,11 @@ export {
APICaller,
} from './elasticsearch';
export { Logger, LoggerFactory, LogMeta, LogRecord, LogLevel } from './logging';
export {
DiscoveredPlugin,
Plugin,
PluginInitializer,
PluginInitializerContext,
PluginName,
PluginSetupContext,
@ -43,3 +47,5 @@ export interface CoreSetup {
elasticsearch: ElasticsearchServiceSetup;
plugins: PluginsServiceSetup;
}
export { ElasticsearchServiceSetup, HttpServiceSetup, PluginsServiceSetup };

View file

@ -98,8 +98,6 @@ export class LegacyPlatformProxy extends EventEmitter {
* Neither new nor legacy platform should use this method directly.
*/
public getConnections(callback: (error: Error | null, count?: number) => void) {
this.log.debug('"getConnections" has been called.');
// This method is used by `even-better` (before we start platform).
// It seems that the latest version of parent `good` doesn't use this anymore.
this.server.getConnections(callback);

View file

@ -25,7 +25,7 @@ import { first, map, toArray } from 'rxjs/operators';
import { Config, ConfigService, Env, ObjectToConfigAdapter } from '../../config';
import { getEnvOptions } from '../../config/__mocks__/env';
import { loggingServiceMock } from '../../logging/logging_service.mock';
import { Plugin } from '../plugin';
import { PluginWrapper } from '../plugin';
import { PluginsConfig } from '../plugins_config';
import { discover } from './plugins_discovery';
@ -142,10 +142,10 @@ test('properly iterates through plugin search locations', async () => {
TEST_EXTRA_PLUGIN_PATH,
]) {
const discoveredPlugin = plugins.find(plugin => plugin.path === path)!;
expect(discoveredPlugin).toBeInstanceOf(Plugin);
expect(discoveredPlugin).toBeInstanceOf(PluginWrapper);
expect(discoveredPlugin.configPath).toEqual(['core', 'config']);
expect(discoveredPlugin.requiredDependencies).toEqual(['a', 'b']);
expect(discoveredPlugin.optionalDependencies).toEqual(['c', 'd']);
expect(discoveredPlugin.requiredPlugins).toEqual(['a', 'b']);
expect(discoveredPlugin.optionalPlugins).toEqual(['c', 'd']);
}
await expect(

View file

@ -23,7 +23,7 @@ import { bindNodeCallback, from, merge } from 'rxjs';
import { catchError, filter, map, mergeMap, shareReplay } from 'rxjs/operators';
import { CoreContext } from '../../core_context';
import { Logger } from '../../logging';
import { Plugin } from '../plugin';
import { PluginWrapper } from '../plugin';
import { createPluginInitializerContext } from '../plugin_context';
import { PluginsConfig } from '../plugins_config';
import { PluginDiscoveryError } from './plugin_discovery_error';
@ -67,9 +67,11 @@ export function discover(config: PluginsConfig, coreContext: CoreContext) {
);
return {
plugin$: discoveryResults$.pipe(filter((entry): entry is Plugin => entry instanceof Plugin)),
plugin$: discoveryResults$.pipe(
filter((entry): entry is PluginWrapper => entry instanceof PluginWrapper)
),
error$: discoveryResults$.pipe(
filter((entry): entry is PluginDiscoveryError => !(entry instanceof Plugin))
filter((entry): entry is PluginDiscoveryError => !(entry instanceof PluginWrapper))
),
};
}
@ -115,7 +117,11 @@ function createPlugin$(path: string, log: Logger, coreContext: CoreContext) {
return from(parseManifest(path, coreContext.env.packageInfo)).pipe(
map(manifest => {
log.debug(`Successfully discovered plugin "${manifest.id}" at "${path}"`);
return new Plugin(path, manifest, createPluginInitializerContext(coreContext, manifest));
return new PluginWrapper(
path,
manifest,
createPluginInitializerContext(coreContext, manifest)
);
}),
catchError(err => [err])
);

View file

@ -22,5 +22,11 @@ export { PluginsService, PluginsServiceSetup } from './plugins_service';
/** @internal */
export { isNewPlatformPlugin } from './discovery';
/** @internal */
export { DiscoveredPlugin, DiscoveredPluginInternal, PluginName } from './plugin';
export {
DiscoveredPlugin,
DiscoveredPluginInternal,
Plugin,
PluginInitializer,
PluginName,
} from './plugin';
export { PluginInitializerContext, PluginSetupContext } from './plugin_context';

View file

@ -25,7 +25,7 @@ import { CoreContext } from '../core_context';
import { elasticsearchServiceMock } from '../elasticsearch/elasticsearch_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { Plugin, PluginManifest } from './plugin';
import { PluginWrapper, PluginManifest } from './plugin';
import { createPluginInitializerContext, createPluginSetupContext } from './plugin_context';
const mockPluginInitializer = jest.fn();
@ -78,7 +78,7 @@ afterEach(() => {
test('`constructor` correctly initializes plugin instance', () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
const plugin = new PluginWrapper(
'some-plugin-path',
manifest,
createPluginInitializerContext(coreContext, manifest)
@ -87,13 +87,13 @@ test('`constructor` correctly initializes plugin instance', () => {
expect(plugin.name).toBe('some-plugin-id');
expect(plugin.configPath).toBe('path');
expect(plugin.path).toBe('some-plugin-path');
expect(plugin.requiredDependencies).toEqual(['some-required-dep']);
expect(plugin.optionalDependencies).toEqual(['some-optional-dep']);
expect(plugin.requiredPlugins).toEqual(['some-required-dep']);
expect(plugin.optionalPlugins).toEqual(['some-optional-dep']);
});
test('`setup` fails if `plugin` initializer is not exported', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
const plugin = new PluginWrapper(
'plugin-without-initializer-path',
manifest,
createPluginInitializerContext(coreContext, manifest)
@ -108,7 +108,7 @@ test('`setup` fails if `plugin` initializer is not exported', async () => {
test('`setup` fails if plugin initializer is not a function', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
const plugin = new PluginWrapper(
'plugin-with-wrong-initializer-path',
manifest,
createPluginInitializerContext(coreContext, manifest)
@ -123,7 +123,7 @@ test('`setup` fails if plugin initializer is not a function', async () => {
test('`setup` fails if initializer does not return object', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
const plugin = new PluginWrapper(
'plugin-with-initializer-path',
manifest,
createPluginInitializerContext(coreContext, manifest)
@ -140,7 +140,7 @@ test('`setup` fails if initializer does not return object', async () => {
test('`setup` fails if object returned from initializer does not define `setup` function', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
const plugin = new PluginWrapper(
'plugin-with-initializer-path',
manifest,
createPluginInitializerContext(coreContext, manifest)
@ -159,7 +159,7 @@ test('`setup` fails if object returned from initializer does not define `setup`
test('`setup` initializes plugin and calls appropriate lifecycle hook', async () => {
const manifest = createPluginManifest();
const initializerContext = createPluginInitializerContext(coreContext, manifest);
const plugin = new Plugin('plugin-with-initializer-path', manifest, initializerContext);
const plugin = new PluginWrapper('plugin-with-initializer-path', manifest, initializerContext);
const mockPluginInstance = { setup: jest.fn().mockResolvedValue({ contract: 'yes' }) };
mockPluginInitializer.mockReturnValue(mockPluginInstance);
@ -177,7 +177,7 @@ test('`setup` initializes plugin and calls appropriate lifecycle hook', async ()
test('`stop` fails if plugin is not set up', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
const plugin = new PluginWrapper(
'plugin-with-initializer-path',
manifest,
createPluginInitializerContext(coreContext, manifest)
@ -194,7 +194,7 @@ test('`stop` fails if plugin is not set up', async () => {
test('`stop` does nothing if plugin does not define `stop` function', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
const plugin = new PluginWrapper(
'plugin-with-initializer-path',
manifest,
createPluginInitializerContext(coreContext, manifest)
@ -208,7 +208,7 @@ test('`stop` does nothing if plugin does not define `stop` function', async () =
test('`stop` calls `stop` defined by the plugin instance', async () => {
const manifest = createPluginManifest();
const plugin = new Plugin(
const plugin = new PluginWrapper(
'plugin-with-initializer-path',
manifest,
createPluginInitializerContext(coreContext, manifest)

View file

@ -124,15 +124,28 @@ export interface DiscoveredPluginInternal extends DiscoveredPlugin {
readonly path: string;
}
type PluginInitializer<TExposedSetup, TDependenciesSetup extends Record<PluginName, unknown>> = (
coreContext: PluginInitializerContext
) => {
/**
* The interface that should be returned by a `PluginInitializer`.
*
* @public
*/
export interface Plugin<TSetup, TPluginsSetup extends Record<PluginName, unknown> = {}> {
setup: (
pluginSetupContext: PluginSetupContext,
dependencies: TDependenciesSetup
) => TExposedSetup;
plugins: TPluginsSetup
) => TSetup | Promise<TSetup>;
stop?: () => void;
};
}
/**
* The `plugin` export at the root of a plugin's `server` directory should conform
* to this interface.
*
* @public
*/
export type PluginInitializer<TSetup, TPluginsSetup extends Record<PluginName, unknown> = {}> = (
coreContext: PluginInitializerContext
) => Plugin<TSetup, TPluginsSetup>;
/**
* Lightweight wrapper around discovered plugin that is responsible for instantiating
@ -140,20 +153,20 @@ type PluginInitializer<TExposedSetup, TDependenciesSetup extends Record<PluginNa
*
* @internal
*/
export class Plugin<
export class PluginWrapper<
TSetup = unknown,
TDependenciesSetup extends Record<PluginName, unknown> = Record<PluginName, unknown>
TPluginsSetup extends Record<PluginName, unknown> = Record<PluginName, unknown>
> {
public readonly name: PluginManifest['id'];
public readonly configPath: PluginManifest['configPath'];
public readonly requiredDependencies: PluginManifest['requiredPlugins'];
public readonly optionalDependencies: PluginManifest['optionalPlugins'];
public readonly requiredPlugins: PluginManifest['requiredPlugins'];
public readonly optionalPlugins: PluginManifest['optionalPlugins'];
public readonly includesServerPlugin: PluginManifest['server'];
public readonly includesUiPlugin: PluginManifest['ui'];
private readonly log: Logger;
private instance?: ReturnType<PluginInitializer<TSetup, TDependenciesSetup>>;
private instance?: Plugin<TSetup, TPluginsSetup>;
constructor(
public readonly path: string,
@ -163,8 +176,8 @@ export class Plugin<
this.log = initializerContext.logger.get();
this.name = manifest.id;
this.configPath = manifest.configPath;
this.requiredDependencies = manifest.requiredPlugins;
this.optionalDependencies = manifest.optionalPlugins;
this.requiredPlugins = manifest.requiredPlugins;
this.optionalPlugins = manifest.optionalPlugins;
this.includesServerPlugin = manifest.server;
this.includesUiPlugin = manifest.ui;
}
@ -173,15 +186,15 @@ export class Plugin<
* Instantiates plugin and calls `setup` function exposed by the plugin initializer.
* @param setupContext Context that consists of various core services tailored specifically
* for the `setup` lifecycle event.
* @param dependencies The dictionary where the key is the dependency name and the value
* @param plugins The dictionary where the key is the dependency name and the value
* is the contract returned by the dependency's `setup` function.
*/
public async setup(setupContext: PluginSetupContext, dependencies: TDependenciesSetup) {
public async setup(setupContext: PluginSetupContext, plugins: TPluginsSetup) {
this.instance = this.createPluginInstance();
this.log.info('Setting up plugin');
return await this.instance.setup(setupContext, dependencies);
return await this.instance.setup(setupContext, plugins);
}
/**
@ -211,7 +224,7 @@ export class Plugin<
}
const { plugin: initializer } = pluginDefinition as {
plugin: PluginInitializer<TSetup, TDependenciesSetup>;
plugin: PluginInitializer<TSetup, TPluginsSetup>;
};
if (!initializer || typeof initializer !== 'function') {
throw new Error(`Definition of plugin "${this.name}" should be a function (${this.path}).`);

View file

@ -23,7 +23,7 @@ import { ConfigWithSchema, EnvironmentMode } from '../config';
import { CoreContext } from '../core_context';
import { ClusterClient } from '../elasticsearch';
import { LoggerFactory } from '../logging';
import { Plugin, PluginManifest } from './plugin';
import { PluginWrapper, PluginManifest } from './plugin';
import { PluginsServiceSetupDeps } from './plugins_service';
/**
@ -126,7 +126,7 @@ export function createPluginInitializerContext(
export function createPluginSetupContext<TPlugin, TPluginDependencies>(
coreContext: CoreContext,
deps: PluginsServiceSetupDeps,
plugin: Plugin<TPlugin, TPluginDependencies>
plugin: PluginWrapper<TPlugin, TPluginDependencies>
): PluginSetupContext {
return {
elasticsearch: {

View file

@ -27,7 +27,7 @@ import { getEnvOptions } from '../config/__mocks__/env';
import { elasticsearchServiceMock } from '../elasticsearch/elasticsearch_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { PluginDiscoveryError } from './discovery';
import { Plugin } from './plugin';
import { PluginWrapper } from './plugin';
import { PluginsService } from './plugins_service';
import { PluginsSystem } from './plugins_system';
@ -110,7 +110,7 @@ test('`setup` throws if discovered plugins with conflicting names', async () =>
mockDiscover.mockReturnValue({
error$: from([]),
plugin$: from([
new Plugin(
new PluginWrapper(
'path-4',
{
id: 'conflicting-id',
@ -124,7 +124,7 @@ test('`setup` throws if discovered plugins with conflicting names', async () =>
},
{ logger } as any
),
new Plugin(
new PluginWrapper(
'path-5',
{
id: 'conflicting-id',
@ -160,7 +160,7 @@ test('`setup` properly detects plugins that should be disabled.', async () => {
mockDiscover.mockReturnValue({
error$: from([]),
plugin$: from([
new Plugin(
new PluginWrapper(
'path-1',
{
id: 'explicitly-disabled-plugin',
@ -174,7 +174,7 @@ test('`setup` properly detects plugins that should be disabled.', async () => {
},
{ logger } as any
),
new Plugin(
new PluginWrapper(
'path-2',
{
id: 'plugin-with-missing-required-deps',
@ -188,7 +188,7 @@ test('`setup` properly detects plugins that should be disabled.', async () => {
},
{ logger } as any
),
new Plugin(
new PluginWrapper(
'path-3',
{
id: 'plugin-with-disabled-transitive-dep',
@ -202,7 +202,7 @@ test('`setup` properly detects plugins that should be disabled.', async () => {
},
{ logger } as any
),
new Plugin(
new PluginWrapper(
'path-4',
{
id: 'another-explicitly-disabled-plugin',
@ -247,7 +247,7 @@ Array [
});
test('`setup` properly invokes `discover` and ignores non-critical errors.', async () => {
const firstPlugin = new Plugin(
const firstPlugin = new PluginWrapper(
'path-1',
{
id: 'some-id',
@ -262,7 +262,7 @@ test('`setup` properly invokes `discover` and ignores non-critical errors.', asy
{ logger } as any
);
const secondPlugin = new Plugin(
const secondPlugin = new PluginWrapper(
'path-2',
{
id: 'some-other-id',

View file

@ -24,7 +24,7 @@ import { CoreContext } from '../core_context';
import { ElasticsearchServiceSetup } from '../elasticsearch/elasticsearch_service';
import { Logger } from '../logging';
import { discover, PluginDiscoveryError, PluginDiscoveryErrorType } from './discovery';
import { DiscoveredPlugin, DiscoveredPluginInternal, Plugin, PluginName } from './plugin';
import { DiscoveredPlugin, DiscoveredPluginInternal, PluginWrapper, PluginName } from './plugin';
import { PluginsConfig } from './plugins_config';
import { PluginsSystem } from './plugins_system';
@ -106,8 +106,11 @@ export class PluginsService implements CoreService<PluginsServiceSetup> {
}
}
private async handleDiscoveredPlugins(plugin$: Observable<Plugin>) {
const pluginEnableStatuses = new Map<PluginName, { plugin: Plugin; isEnabled: boolean }>();
private async handleDiscoveredPlugins(plugin$: Observable<PluginWrapper>) {
const pluginEnableStatuses = new Map<
PluginName,
{ plugin: PluginWrapper; isEnabled: boolean }
>();
await plugin$
.pipe(
mergeMap(async plugin => {
@ -139,13 +142,13 @@ export class PluginsService implements CoreService<PluginsServiceSetup> {
private shouldEnablePlugin(
pluginName: PluginName,
pluginEnableStatuses: Map<PluginName, { plugin: Plugin; isEnabled: boolean }>
pluginEnableStatuses: Map<PluginName, { plugin: PluginWrapper; isEnabled: boolean }>
): boolean {
const pluginInfo = pluginEnableStatuses.get(pluginName);
return (
pluginInfo !== undefined &&
pluginInfo.isEnabled &&
pluginInfo.plugin.requiredDependencies.every(dependencyName =>
pluginInfo.plugin.requiredPlugins.every(dependencyName =>
this.shouldEnablePlugin(dependencyName, pluginEnableStatuses)
)
);

View file

@ -25,7 +25,7 @@ import { getEnvOptions } from '../config/__mocks__/env';
import { CoreContext } from '../core_context';
import { elasticsearchServiceMock } from '../elasticsearch/elasticsearch_service.mock';
import { loggingServiceMock } from '../logging/logging_service.mock';
import { Plugin, PluginName } from './plugin';
import { PluginWrapper, PluginName } from './plugin';
import { PluginsSystem } from './plugins_system';
const logger = loggingServiceMock.create();
@ -37,7 +37,7 @@ function createPlugin(
server = true,
}: { required?: string[]; optional?: string[]; server?: boolean } = {}
) {
return new Plugin(
return new PluginWrapper(
'some-path',
{
id,
@ -140,7 +140,7 @@ test('`setupPlugins` correctly orders plugins and returns exposed values', async
createPlugin('order-3', { required: ['order-2'], optional: ['missing-dep'] }),
{ 'order-2': 'added-as-2' },
],
] as Array<[Plugin, Record<PluginName, unknown>]>);
] as Array<[PluginWrapper, Record<PluginName, unknown>]>);
const setupContextMap = new Map();
@ -251,7 +251,7 @@ test('`uiPlugins` returns ordered Maps of all plugin manifests', async () => {
createPlugin('order-3', { required: ['order-2'], optional: ['missing-dep'] }),
{ 'order-2': 'added-as-2' },
],
] as Array<[Plugin, Record<PluginName, unknown>]>);
] as Array<[PluginWrapper, Record<PluginName, unknown>]>);
[...plugins.keys()].forEach(plugin => {
pluginsSystem.addPlugin(plugin);

View file

@ -21,13 +21,13 @@ import { pick } from 'lodash';
import { CoreContext } from '../core_context';
import { Logger } from '../logging';
import { DiscoveredPlugin, DiscoveredPluginInternal, Plugin, PluginName } from './plugin';
import { DiscoveredPlugin, DiscoveredPluginInternal, PluginWrapper, PluginName } from './plugin';
import { createPluginSetupContext } from './plugin_context';
import { PluginsServiceSetupDeps } from './plugins_service';
/** @internal */
export class PluginsSystem {
private readonly plugins = new Map<PluginName, Plugin>();
private readonly plugins = new Map<PluginName, PluginWrapper>();
private readonly log: Logger;
// `satup`, the past-tense version of the noun `setup`.
private readonly satupPlugins: PluginName[] = [];
@ -36,14 +36,14 @@ export class PluginsSystem {
this.log = coreContext.logger.get('plugins-system');
}
public addPlugin(plugin: Plugin) {
public addPlugin(plugin: PluginWrapper) {
this.plugins.set(plugin.name, plugin);
}
public async setupPlugins(deps: PluginsServiceSetupDeps) {
const exposedValues = new Map<PluginName, unknown>();
const contracts = new Map<PluginName, unknown>();
if (this.plugins.size === 0) {
return exposedValues;
return contracts;
}
const sortedPlugins = this.getTopologicallySortedPluginNames();
@ -57,29 +57,31 @@ export class PluginsSystem {
this.log.debug(`Setting up plugin "${pluginName}"...`);
const exposedDependencyValues = [
...plugin.requiredDependencies,
...plugin.optionalDependencies,
].reduce(
(dependencies, dependencyName) => {
dependencies[dependencyName] = exposedValues.get(dependencyName);
return dependencies;
const pluginDepContracts = [...plugin.requiredPlugins, ...plugin.optionalPlugins].reduce(
(depContracts, dependencyName) => {
// Only set if present. Could be absent if plugin does not have server-side code or is a
// missing optional dependency.
if (contracts.has(dependencyName)) {
depContracts[dependencyName] = contracts.get(dependencyName);
}
return depContracts;
},
{} as Record<PluginName, unknown>
);
exposedValues.set(
contracts.set(
pluginName,
await plugin.setup(
createPluginSetupContext(this.coreContext, deps, plugin),
exposedDependencyValues
pluginDepContracts
)
);
this.satupPlugins.push(pluginName);
}
return exposedValues;
return contracts;
}
public async stopPlugins() {
@ -151,8 +153,8 @@ export class PluginsSystem {
return [
pluginName,
new Set([
...plugin.requiredDependencies,
...plugin.optionalDependencies.filter(dependency => this.plugins.has(dependency)),
...plugin.requiredPlugins,
...plugin.optionalPlugins.filter(dependency => this.plugins.has(dependency)),
]),
] as [PluginName, Set<PluginName>];
})

View file

@ -1,4 +1,4 @@
## API Review File for "kibana"
## API Report File for "kibana"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
@ -15,9 +15,6 @@ import { TypeOf } from '@kbn/config-schema';
// @public (undocumented)
export type APICaller = (endpoint: string, clientParams: Record<string, unknown>, options?: CallAPIOptions) => Promise<unknown>;
// Warning: (ae-forgotten-export) The symbol "BootstrapArgs" needs to be exported by the entry point index.d.ts
// Warning: (ae-internal-missing-underscore) The name bootstrap should be prefixed with an underscore because the declaration is marked as "@internal"
//
// @internal (undocumented)
export function bootstrap({ configs, cliArgs, applyConfigOverrides, features, }: BootstrapArgs): Promise<void>;
@ -28,7 +25,6 @@ export interface CallAPIOptions {
// @public
export class ClusterClient {
// (undocumented)
constructor(config: ElasticsearchClientConfig, log: Logger);
asScoped(req?: {
headers?: Headers;
@ -37,35 +33,38 @@ export class ClusterClient {
close(): void;
}
// @public (undocumented)
export class ConfigService {
constructor(config$: Observable<Config>, env: Env, logger: LoggerFactory);
atPath<TSchema extends Type<any>, TConfig>(path: ConfigPath, ConfigClass: ConfigWithSchema<TSchema, TConfig>): Observable<TConfig>;
getConfig$(): Observable<Config>;
// (undocumented)
getUnusedPaths(): Promise<string[]>;
// (undocumented)
getUsedPaths(): Promise<string[]>;
// (undocumented)
isEnabledAtPath(path: ConfigPath): Promise<boolean>;
optionalAtPath<TSchema extends Type<any>, TConfig>(path: ConfigPath, ConfigClass: ConfigWithSchema<TSchema, TConfig>): Observable<TConfig | undefined>;
}
// @public (undocumented)
export interface CoreSetup {
// Warning: (ae-forgotten-export) The symbol "ElasticsearchServiceSetup" needs to be exported by the entry point index.d.ts
//
// (undocumented)
elasticsearch: ElasticsearchServiceSetup;
// Warning: (ae-forgotten-export) The symbol "HttpServiceSetup" needs to be exported by the entry point index.d.ts
//
// (undocumented)
http: HttpServiceSetup;
// Warning: (ae-forgotten-export) The symbol "PluginsServiceSetup" needs to be exported by the entry point index.d.ts
//
// (undocumented)
plugins: PluginsServiceSetup;
}
// Warning: (ae-internal-missing-underscore) The name DiscoveredPlugin should be prefixed with an underscore because the declaration is marked as "@internal"
//
// @internal
export interface DiscoveredPlugin {
// Warning: (ae-forgotten-export) The symbol "ConfigPath" needs to be exported by the entry point index.d.ts
readonly configPath: ConfigPath;
readonly id: PluginName;
readonly optionalPlugins: ReadonlyArray<PluginName>;
readonly requiredPlugins: ReadonlyArray<PluginName>;
}
// Warning: (ae-forgotten-export) The symbol "ElasticsearchConfig" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type ElasticsearchClientConfig = Pick<ConfigOptions, 'keepAlive' | 'log' | 'plugins'> & Pick<ElasticsearchConfig, 'apiVersion' | 'customHeaders' | 'logQueries' | 'requestHeadersWhitelist' | 'sniffOnStart' | 'sniffOnConnectionFault' | 'hosts' | 'username' | 'password'> & {
pingTimeout?: ElasticsearchConfig['pingTimeout'] | ConfigOptions['pingTimeout'];
@ -74,9 +73,26 @@ export type ElasticsearchClientConfig = Pick<ConfigOptions, 'keepAlive' | 'log'
ssl?: Partial<ElasticsearchConfig['ssl']>;
};
// @public (undocumented)
export interface ElasticsearchServiceSetup {
// (undocumented)
readonly adminClient$: Observable<ClusterClient>;
// (undocumented)
readonly createClient: (type: string, config: ElasticsearchClientConfig) => ClusterClient;
// (undocumented)
readonly dataClient$: Observable<ClusterClient>;
// (undocumented)
readonly legacy: {
readonly config$: Observable<ElasticsearchConfig>;
};
}
// @public (undocumented)
export type Headers = Record<string, string | string[] | undefined>;
// @internal (undocumented)
export type HttpServiceSetup = HttpServerInfo;
// @public
export interface Logger {
debug(message: string, meta?: LogMeta): void;
@ -94,8 +110,6 @@ export interface LoggerFactory {
get(...contextParts: string[]): Logger;
}
// Warning: (ae-internal-missing-underscore) The name LogLevel should be prefixed with an underscore because the declaration is marked as "@internal"
//
// @internal
export class LogLevel {
// (undocumented)
@ -107,8 +121,6 @@ export class LogLevel {
// (undocumented)
static readonly Fatal: LogLevel;
static fromId(level: LogLevelId): LogLevel;
// Warning: (ae-forgotten-export) The symbol "LogLevelId" needs to be exported by the entry point index.d.ts
//
// (undocumented)
readonly id: LogLevelId;
// (undocumented)
@ -130,8 +142,6 @@ export interface LogMeta {
[key: string]: any;
}
// Warning: (ae-internal-missing-underscore) The name LogRecord should be prefixed with an underscore because the declaration is marked as "@internal"
//
// @internal
export interface LogRecord {
// (undocumented)
@ -150,6 +160,17 @@ export interface LogRecord {
timestamp: Date;
}
// @public
export interface Plugin<TSetup, TPluginsSetup extends Record<PluginName, unknown> = {}> {
// (undocumented)
setup: (pluginSetupContext: PluginSetupContext, plugins: TPluginsSetup) => TSetup | Promise<TSetup>;
// (undocumented)
stop?: () => void;
}
// @public
export type PluginInitializer<TSetup, TPluginsSetup extends Record<PluginName, unknown> = {}> = (coreContext: PluginInitializerContext) => Plugin<TSetup, TPluginsSetup>;
// @public
export interface PluginInitializerContext {
// (undocumented)
@ -177,20 +198,25 @@ export interface PluginSetupContext {
};
}
// @internal (undocumented)
export interface PluginsServiceSetup {
// (undocumented)
contracts: Map<PluginName, unknown>;
// (undocumented)
uiPlugins: {
public: Map<PluginName, DiscoveredPlugin>;
internal: Map<PluginName, DiscoveredPluginInternal>;
};
}
// @public
export class ScopedClusterClient {
// (undocumented)
constructor(internalAPICaller: APICaller, scopedAPICaller: APICaller, headers?: Record<string, string | string[] | undefined> | undefined);
callAsCurrentUser(endpoint: string, clientParams?: Record<string, unknown>, options?: CallAPIOptions): Promise<unknown>;
callAsInternalUser(endpoint: string, clientParams?: Record<string, unknown>, options?: CallAPIOptions): Promise<unknown>;
}
// Warnings were encountered during analysis:
//
// src/core/server/plugins/plugin_context.ts:35:9 - (ae-forgotten-export) The symbol "EnvironmentMode" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/plugin_context.ts:39:9 - (ae-forgotten-export) The symbol "ConfigWithSchema" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)
```

View file

@ -68,13 +68,13 @@ import { mkdirp } from '../lib';
*/
export const BuildPackagesTask = {
global: true,
description: 'Building distributable versions of packages',
async run(config, log, builds) {
async run(config, log, build) {
await mkdirp(config.resolveFromRepo('target'));
await buildProductionProjects({
kibanaRoot: config.resolveFromRepo(),
buildRoots: builds.map(build => build.resolvePath()),
buildRoot: build.resolvePath(),
onlyOSS: build.isOss(),
});
},
};

View file

@ -40,12 +40,12 @@ export const CleanPackagesTask = {
async run(config, log, build) {
await deleteAll([
build.resolvePath('packages'),
build.resolvePath('x-pack'),
build.resolvePath('yarn.lock'),
], log);
},
};
export const CleanTypescriptTask = {
description:
'Cleaning typescript source files that have been transpiled to JS',
@ -70,103 +70,112 @@ export const CleanExtraFilesFromModulesTask = {
minimatch.makeRe(pattern, { nocase: true })
);
const regularExpressions = makeRegexps([
// tests
'**/test',
'**/tests',
'**/__tests__',
'**/mocha.opts',
'**/*.test.js',
'**/*.snap',
'**/coverage',
// docs
'**/doc',
'**/docs',
'**/CONTRIBUTING.md',
'**/Contributing.md',
'**/contributing.md',
'**/History.md',
'**/HISTORY.md',
'**/history.md',
'**/CHANGELOG.md',
'**/Changelog.md',
'**/changelog.md',
// examples
'**/example',
'**/examples',
'**/demo',
'**/samples',
// bins
'**/.bin',
// linters
'**/.eslintrc',
'**/.eslintrc.js',
'**/.eslintrc.yml',
'**/.prettierrc',
'**/.jshintrc',
'**/.babelrc',
'**/.jscs.json',
'**/.lint',
// hints
'**/*.flow',
'**/*.webidl',
'**/*.map',
'**/@types',
// scripts
'**/*.sh',
'**/*.bat',
'**/*.exe',
'**/Gruntfile.js',
'**/gulpfile.js',
'**/Makefile',
// untranspiled sources
'**/*.coffee',
'**/*.scss',
'**/*.sass',
'**/.ts',
'**/.tsx',
// editors
'**/.editorconfig',
'**/.vscode',
// git
'**/.gitattributes',
'**/.gitkeep',
'**/.gitempty',
'**/.gitmodules',
'**/.keep',
'**/.empty',
// ci
'**/.travis.yml',
'**/.coveralls.yml',
'**/.instanbul.yml',
'**/appveyor.yml',
'**/.zuul.yml',
// metadata
'**/package-lock.json',
'**/component.json',
'**/bower.json',
'**/yarn.lock',
// misc
'**/.*ignore',
'**/.DS_Store',
'**/Dockerfile',
'**/docker-compose.yml',
]);
log.info('Deleted %d files', await scanDelete({
directory: build.resolvePath('node_modules'),
regularExpressions: makeRegexps([
// tests
'**/test',
'**/tests',
'**/__tests__',
'**/mocha.opts',
'**/*.test.js',
'**/*.snap',
'**/coverage',
// docs
'**/doc',
'**/docs',
'**/CONTRIBUTING.md',
'**/Contributing.md',
'**/contributing.md',
'**/History.md',
'**/HISTORY.md',
'**/history.md',
'**/CHANGELOG.md',
'**/Changelog.md',
'**/changelog.md',
// examples
'**/example',
'**/examples',
'**/demo',
'**/samples',
// bins
'**/.bin',
// linters
'**/.eslintrc',
'**/.eslintrc.js',
'**/.eslintrc.yml',
'**/.prettierrc',
'**/.jshintrc',
'**/.babelrc',
'**/.jscs.json',
'**/.lint',
// hints
'**/*.flow',
'**/*.webidl',
'**/*.map',
'**/@types',
// scripts
'**/*.sh',
'**/*.bat',
'**/*.exe',
'**/Gruntfile.js',
'**/gulpfile.js',
'**/Makefile',
// untranspiled sources
'**/*.coffee',
'**/*.scss',
'**/*.sass',
'**/.ts',
'**/.tsx',
// editors
'**/.editorconfig',
'**/.vscode',
// git
'**/.gitattributes',
'**/.gitkeep',
'**/.gitempty',
'**/.gitmodules',
'**/.keep',
'**/.empty',
// ci
'**/.travis.yml',
'**/.coveralls.yml',
'**/.instanbul.yml',
'**/appveyor.yml',
'**/.zuul.yml',
// metadata
'**/package-lock.json',
'**/component.json',
'**/bower.json',
'**/yarn.lock',
// misc
'**/.*ignore',
'**/.DS_Store',
'**/Dockerfile',
'**/docker-compose.yml'
])
regularExpressions
}));
if (!build.isOss()) {
log.info('Deleted %d files', await scanDelete({
directory: build.resolvePath('x-pack/node_modules'),
regularExpressions
}));
}
},
};
@ -194,7 +203,7 @@ export const CleanExtraBrowsersTask = {
async run(config, log, build) {
const getBrowserPathsForPlatform = platform => {
const reportingDir = 'node_modules/x-pack/plugins/reporting';
const reportingDir = 'x-pack/plugins/reporting';
const chromiumDir = '.chromium';
const chromiumPath = p =>
build.resolvePathForPlatform(platform, reportingDir, chromiumDir, p);

View file

@ -27,14 +27,7 @@ export const CreateArchivesSourcesTask = {
// copy all files from generic build source directory into platform-specific build directory
await scanCopy({
source: build.resolvePath(),
destination: build.resolvePathForPlatform(platform),
filter: record => !(record.isDirectory && record.name === 'node_modules')
});
await scanCopy({
source: build.resolvePath('node_modules'),
destination: build.resolvePathForPlatform(platform, 'node_modules'),
time: new Date()
destination: build.resolvePathForPlatform(platform)
});
log.debug('Generic build source copied into', platform.getName(), 'specific build directory');

View file

@ -50,7 +50,6 @@ export const CreatePackageJsonTask = {
};
if (build.isOss()) {
delete newPkg.dependencies['x-pack'];
newPkg.workspaces.packages = newPkg.workspaces.packages.filter(p => !p.startsWith('x-pack'));
}

View file

@ -17,18 +17,25 @@
* under the License.
*/
import { exec } from '../lib';
import { Project } from '@kbn/pm';
export const InstallDependenciesTask = {
description: 'Installing node_modules, including production builds of packages',
async run(config, log, build) {
// We're using --no-bin-links to support systems that don't have symlinks.
// This is commonly seen in shared folders on virtual machines
const args = ['--production', '--ignore-optional', '--frozen-lockfile', '--no-bin-links', '--prefer-offline'];
const project = await Project.fromPath(build.resolvePath());
await exec(log, 'yarn', args, {
cwd: build.resolvePath(),
await project.installDependencies({
extraArgs: [
'--production',
'--ignore-optional',
'--frozen-lockfile',
'--prefer-offline',
// We're using --no-bin-links to support systems that don't have symlinks.
// This is commonly seen in shared folders on virtual machines
'--no-bin-links',
]
});
},
};

View file

@ -38,7 +38,7 @@ export const CleanClientModulesOnDLLTask = {
`${baseDir}/src/cli`,
`${baseDir}/src/cli_keystore`,
`${baseDir}/src/cli_plugin`,
`${baseDir}/node_modules/x-pack`,
`${baseDir}/x-pack`,
...kbnWebpackLoaders.map(loader => `${baseDir}/node_modules/${loader}`)
];
const discoveredLegacyCorePluginEntries = await globby([

View file

@ -18,43 +18,60 @@
*/
import { ToolingLog } from '@kbn/dev-utils';
import { Extractor, IExtractorConfig } from '@microsoft/api-extractor';
import {
Extractor,
IConfigFile,
ExtractorLogLevel,
ExtractorConfig,
ExtractorResult,
ExtractorMessage,
} from '@microsoft/api-extractor';
import chalk from 'chalk';
import dedent from 'dedent';
import execa from 'execa';
import fs from 'fs';
import path from 'path';
import getopts from 'getopts';
const apiExtractorConfig = (folder: string): IExtractorConfig => {
return {
const apiExtractorConfig = (folder: string): ExtractorConfig => {
const config: IConfigFile = {
compiler: {
configType: 'tsconfig',
rootFolder: '.',
tsconfigFilePath: '<projectFolder>/tsconfig.json',
},
project: {
entryPointSourceFile: `target/types/${folder}/index.d.ts`,
},
apiReviewFile: {
projectFolder: path.resolve('./'),
mainEntryPointFilePath: `target/types/${folder}/index.d.ts`,
apiReport: {
enabled: true,
apiReviewFolder: `./src/core/${folder}/`,
tempFolder: `./build/${folder}/`,
reportFileName: `${folder}.api.md`,
reportFolder: `<projectFolder>/src/core/${folder}/`,
reportTempFolder: `<projectFolder>/build/${folder}/`,
},
apiJsonFile: {
docModel: {
enabled: true,
outputFolder: `./build/${folder}`,
apiJsonFilePath: `./build/${folder}/${folder}.api.json`,
},
dtsRollup: {
tsdocMetadata: {
enabled: false,
trimming: true,
publishFolderForInternal: '',
publishFolderForBeta: '',
publishFolderForPublic: '',
mainDtsRollupPath: '',
},
policies: {
namespaceSupport: 'permissive',
messages: {
extractorMessageReporting: {
default: {
logLevel: 'warning' as ExtractorLogLevel.Warning,
},
'ae-internal-missing-underscore': {
logLevel: 'none' as ExtractorLogLevel.None,
addToApiReportFile: false,
},
},
},
};
const con = ExtractorConfig.prepare({
configObject: config,
configObjectFullPath: undefined,
packageJsonFullPath: path.resolve('package.json'),
});
return con;
};
const runBuildTypes = async () => {
@ -67,62 +84,58 @@ const runApiDocumenter = async (folder: string) => {
);
};
const isApiChangedWarning = (warning: string) => {
return warning.startsWith('You have changed the public API signature for this project.');
};
const renameExtractedApiPackageName = async (folder: string) => {
const json = JSON.parse(fs.readFileSync(`build/${folder}/kibana.api.json`).toString());
const json = JSON.parse(fs.readFileSync(`build/${folder}/${folder}.api.json`).toString());
json.canonicalReference = `kibana-plugin-${folder}`;
json.name = `kibana-plugin-${folder}`;
fs.writeFileSync(`build/${folder}/kibana.api.json`, JSON.stringify(json, null, 2));
fs.writeFileSync(`build/${folder}/${folder}.api.json`, JSON.stringify(json, null, 2));
};
/**
* Runs api-extractor with a custom logger in order to extract results from the process
*
* TODO: Once Microsoft/web-build-tools#1133 is fixed, use the updated interface instead
* of parsing log strings.
*/
const runApiExtractor = (folder: string, acceptChanges: boolean = false) => {
// Because of the internals of api-extractor ILogger can't be implemented as a typescript Class
const warnings = [] as string[];
const errors = [] as string[];
const memoryLogger = {
logVerbose(message: string) {
return null;
},
logInfo(message: string) {
return null;
},
logWarning(message: string) {
warnings.push(message);
},
logError(message: string) {
errors.push(message);
},
};
const runApiExtractor = (
log: ToolingLog,
folder: string,
acceptChanges: boolean = false
): ExtractorResult => {
const config = apiExtractorConfig(folder);
const options = {
// Indicates that API Extractor is running as part of a local build,
// e.g. on developer's machine. For example, if the *.api.ts output file
// has differences, it will be automatically overwritten for a
// local build, whereas this should report an error for a production build.
localBuild: acceptChanges,
customLogger: memoryLogger,
messageCallback: (message: ExtractorMessage) => {
if (message.messageId === 'console-api-report-not-copied') {
// ConsoleMessageId.ApiReportNotCopied
log.warning(`You have changed the signature of the ${folder} Core API`);
log.warning(
'To accept these changes run `node scripts/check_core_api_changes.js --accept` and then:\n' +
"\t 1. Commit the updated documentation and API review file '" +
config.reportFilePath +
"' \n" +
"\t 2. Describe the change in your PR including whether it's a major, minor or patch"
);
message.handled = true;
} else if (message.messageId === 'console-api-report-copied') {
// ConsoleMessageId.ApiReportCopied
log.warning(`You have changed the signature of the ${folder} Core API`);
log.warning(
"Please commit the updated API documentation and the review file in '" +
config.reportFilePath
);
message.handled = true;
} else if (message.messageId === 'console-api-report-unchanged') {
// ConsoleMessageId.ApiReportUnchanged
log.info(`Core ${folder} API: no changes detected ✔`);
message.handled = true;
}
},
};
const extractor = new Extractor(apiExtractorConfig(folder), options);
extractor.processProject();
const printableWarnings = warnings.filter(msg => !isApiChangedWarning(msg));
const apiChanged = warnings.some(isApiChangedWarning);
return { apiChanged, warnings: printableWarnings, errors };
return Extractor.invoke(config, options);
};
async function run(folder: string): Promise<boolean> {
@ -188,34 +201,17 @@ async function run(folder: string): Promise<boolean> {
return false;
}
const { apiChanged, warnings, errors } = runApiExtractor(folder, opts.accept);
await renameExtractedApiPackageName(folder);
const { apiReportChanged, succeeded } = runApiExtractor(log, folder, opts.accept);
const apiReviewFilePath =
apiExtractorConfig(folder)!.apiReviewFile!.apiReviewFolder + 'kibana.api.md';
if (apiChanged && opts.accept) {
log.warning(`You have changed the public signature of the ${folder} Core API`);
log.warning(
`Please commit the updated API documentation and the review file in '${apiReviewFilePath}' \n`
);
// If we're not accepting changes and there's a failure, exit.
if (!opts.accept && !succeeded) {
return false;
}
if (apiChanged && !opts.accept) {
log.warning('You have changed the public signature of the Kibana Core API');
log.warning(
'To accept these changes run `node scripts/check_core_api_changes.js --accept` and then:\n' +
`\t 1. Commit the updated documentation and API review file ${apiReviewFilePath}' \n` +
"\t 2. Describe the change in your PR including whether it's a major, minor or patch"
);
}
if (!apiChanged) {
log.info(`Core ${folder} API: no changes detected ✔`);
}
if (opts.accept || opts.docs) {
// Attempt to generate docs even if api-extractor didn't succeed
if ((opts.accept && apiReportChanged) || opts.docs) {
try {
await renameExtractedApiPackageName(folder);
await runApiDocumenter(folder);
} catch (e) {
log.error(e);
@ -224,20 +220,8 @@ async function run(folder: string): Promise<boolean> {
log.info(`Core ${folder} API: updated documentation ✔`);
}
// If the API changed and we're not accepting the changes, exit process with error
if (apiChanged && !opts.accept) {
return false;
}
// If any errors or warnings occured, exit with an error
if (errors.length > 0 || warnings.length > 0) {
log.error(`Core ${folder} API: api-extractor failed with the following errors or warnings:`);
errors.forEach(msg => log.error(msg));
warnings.forEach(msg => log.warning(msg));
return false;
}
return true;
return succeeded;
}
(async () => {

View file

@ -33,10 +33,7 @@ export async function buildSass({ log, kibanaDir }) {
resolve(kibanaDir, 'src/legacy/core_plugins')
];
const paths = [
resolve(kibanaDir, 'x-pack'),
resolve(kibanaDir, 'node_modules/x-pack')
];
const paths = [ resolve(kibanaDir, 'x-pack') ];
const { spec$ } = findPluginSpecs({ plugins: { scanDirs, paths } });
const enabledPlugins = await spec$.pipe(toArray()).toPromise();

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