ContextSetup | [ContextSetup](./kibana-plugin-core-server.contextsetup.md) |
| [elasticsearch](./kibana-plugin-core-server.coresetup.elasticsearch.md) | ElasticsearchServiceSetup
| [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) |
| [getStartServices](./kibana-plugin-core-server.coresetup.getstartservices.md) | StartServicesAccessor<TPluginsStart, TStart>
| [StartServicesAccessor](./kibana-plugin-core-server.startservicesaccessor.md) |
-| [http](./kibana-plugin-core-server.coresetup.http.md) | HttpServiceSetup
| [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) |
+| [http](./kibana-plugin-core-server.coresetup.http.md) | HttpServiceSetup & {
resources: HttpResources;
}
| [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) |
| [metrics](./kibana-plugin-core-server.coresetup.metrics.md) | MetricsServiceSetup
| [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) |
| [savedObjects](./kibana-plugin-core-server.coresetup.savedobjects.md) | SavedObjectsServiceSetup
| [SavedObjectsServiceSetup](./kibana-plugin-core-server.savedobjectsservicesetup.md) |
| [status](./kibana-plugin-core-server.coresetup.status.md) | StatusServiceSetup
| [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) |
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresources.md b/docs/development/core/server/kibana-plugin-core-server.httpresources.md
new file mode 100644
index 0000000000000..cb3170e989e17
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresources.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResources](./kibana-plugin-core-server.httpresources.md)
+
+## HttpResources interface
+
+HttpResources service is responsible for serving static & dynamic assets for Kibana application via HTTP. Provides API allowing plug-ins to respond with: - a pre-configured HTML page bootstrapping Kibana client app - custom HTML page - custom JS script file.
+
+Signature:
+
+```typescript
+export interface HttpResources
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [register](./kibana-plugin-core-server.httpresources.register.md) | <P, Q, B>(route: RouteConfig<P, Q, B, 'get'>, handler: HttpResourcesRequestHandler<P, Q, B>) => void
| To register a route handler executing passed function to form response. |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresources.register.md b/docs/development/core/server/kibana-plugin-core-server.httpresources.register.md
new file mode 100644
index 0000000000000..fe3803a6ffe52
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresources.register.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResources](./kibana-plugin-core-server.httpresources.md) > [register](./kibana-plugin-core-server.httpresources.register.md)
+
+## HttpResources.register property
+
+To register a route handler executing passed function to form response.
+
+Signature:
+
+```typescript
+register: (route: RouteConfig
, handler: HttpResourcesRequestHandler
) => void;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.headers.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.headers.md
new file mode 100644
index 0000000000000..bb6dec504ff42
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.headers.md
@@ -0,0 +1,18 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesRenderOptions](./kibana-plugin-core-server.httpresourcesrenderoptions.md) > [headers](./kibana-plugin-core-server.httpresourcesrenderoptions.headers.md)
+
+## HttpResourcesRenderOptions.headers property
+
+HTTP Headers with additional information about response.
+
+Signature:
+
+```typescript
+headers?: ResponseHeaders;
+```
+
+## Remarks
+
+All HTML pages are already pre-configured with `content-security-policy` header that cannot be overridden.
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.md
new file mode 100644
index 0000000000000..6563e3c636a99
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesRenderOptions](./kibana-plugin-core-server.httpresourcesrenderoptions.md)
+
+## HttpResourcesRenderOptions interface
+
+Allows to configure HTTP response parameters
+
+Signature:
+
+```typescript
+export interface HttpResourcesRenderOptions
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [headers](./kibana-plugin-core-server.httpresourcesrenderoptions.headers.md) | ResponseHeaders
| HTTP Headers with additional information about response. |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesrequesthandler.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrequesthandler.md
new file mode 100644
index 0000000000000..20f930382955e
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrequesthandler.md
@@ -0,0 +1,18 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesRequestHandler](./kibana-plugin-core-server.httpresourcesrequesthandler.md)
+
+## HttpResourcesRequestHandler type
+
+Extended version of [RequestHandler](./kibana-plugin-core-server.requesthandler.md) having access to [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) to respond with HTML or JS resources.
+
+Signature:
+
+```typescript
+export declare type HttpResourcesRequestHandler
= RequestHandler
;
+```
+
+## Example
+
+\`\`\`typescript httpResources.register({ path: '/login', validate: { params: schema.object({ id: schema.string() }), }, }, async (context, request, response) => { //.. return response.renderCoreApp(); });
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesresponseoptions.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesresponseoptions.md
new file mode 100644
index 0000000000000..2ea3ea7e58c78
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesresponseoptions.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesResponseOptions](./kibana-plugin-core-server.httpresourcesresponseoptions.md)
+
+## HttpResourcesResponseOptions type
+
+HTTP Resources response parameters
+
+Signature:
+
+```typescript
+export declare type HttpResourcesResponseOptions = HttpResponseOptions;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.md
new file mode 100644
index 0000000000000..1c221e13f534f
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.md
@@ -0,0 +1,23 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md)
+
+## HttpResourcesServiceToolkit interface
+
+Extended set of [KibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) helpers used to respond with HTML or JS resource.
+
+Signature:
+
+```typescript
+export interface HttpResourcesServiceToolkit
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [renderAnonymousCoreApp](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md) | (options?: HttpResourcesRenderOptions) => Promise<IKibanaResponse>
| To respond with HTML page bootstrapping Kibana application without retrieving user-specific information. |
+| [renderCoreApp](./kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md) | (options?: HttpResourcesRenderOptions) => Promise<IKibanaResponse>
| To respond with HTML page bootstrapping Kibana application. |
+| [renderHtml](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderhtml.md) | (options: HttpResourcesResponseOptions) => IKibanaResponse
| To respond with a custom HTML page. |
+| [renderJs](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderjs.md) | (options: HttpResourcesResponseOptions) => IKibanaResponse
| To respond with a custom JS script file. |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md
new file mode 100644
index 0000000000000..3dce9d88c8036
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) > [renderAnonymousCoreApp](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md)
+
+## HttpResourcesServiceToolkit.renderAnonymousCoreApp property
+
+To respond with HTML page bootstrapping Kibana application without retrieving user-specific information.
+
+Signature:
+
+```typescript
+renderAnonymousCoreApp: (options?: HttpResourcesRenderOptions) => Promise;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md
new file mode 100644
index 0000000000000..eb4f095bc19be
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) > [renderCoreApp](./kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md)
+
+## HttpResourcesServiceToolkit.renderCoreApp property
+
+To respond with HTML page bootstrapping Kibana application.
+
+Signature:
+
+```typescript
+renderCoreApp: (options?: HttpResourcesRenderOptions) => Promise;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderhtml.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderhtml.md
new file mode 100644
index 0000000000000..325d19625df44
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderhtml.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) > [renderHtml](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderhtml.md)
+
+## HttpResourcesServiceToolkit.renderHtml property
+
+To respond with a custom HTML page.
+
+Signature:
+
+```typescript
+renderHtml: (options: HttpResourcesResponseOptions) => IKibanaResponse;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderjs.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderjs.md
new file mode 100644
index 0000000000000..f8d4418fc6cba
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderjs.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) > [renderJs](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderjs.md)
+
+## HttpResourcesServiceToolkit.renderJs property
+
+To respond with a custom JS script file.
+
+Signature:
+
+```typescript
+renderJs: (options: HttpResourcesResponseOptions) => IKibanaResponse;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.irouter.handlelegacyerrors.md b/docs/development/core/server/kibana-plugin-core-server.irouter.handlelegacyerrors.md
index 94cf3c94187b0..35d109975c83a 100644
--- a/docs/development/core/server/kibana-plugin-core-server.irouter.handlelegacyerrors.md
+++ b/docs/development/core/server/kibana-plugin-core-server.irouter.handlelegacyerrors.md
@@ -9,5 +9,5 @@ Wrap a router handler to catch and converts legacy boom errors to proper custom
Signature:
```typescript
-handleLegacyErrors: (handler: RequestHandler
) => RequestHandler
;
+handleLegacyErrors: RequestHandlerWrapper;
```
diff --git a/docs/development/core/server/kibana-plugin-core-server.irouter.md b/docs/development/core/server/kibana-plugin-core-server.irouter.md
index 073f02f1a4191..4bade638a65a5 100644
--- a/docs/development/core/server/kibana-plugin-core-server.irouter.md
+++ b/docs/development/core/server/kibana-plugin-core-server.irouter.md
@@ -18,7 +18,7 @@ export interface IRouter
| --- | --- | --- |
| [delete](./kibana-plugin-core-server.irouter.delete.md) | RouteRegistrar<'delete'>
| Register a route handler for DELETE
request. |
| [get](./kibana-plugin-core-server.irouter.get.md) | RouteRegistrar<'get'>
| Register a route handler for GET
request. |
-| [handleLegacyErrors](./kibana-plugin-core-server.irouter.handlelegacyerrors.md) | <P, Q, B>(handler: RequestHandler<P, Q, B>) => RequestHandler<P, Q, B>
| Wrap a router handler to catch and converts legacy boom errors to proper custom errors. |
+| [handleLegacyErrors](./kibana-plugin-core-server.irouter.handlelegacyerrors.md) | RequestHandlerWrapper
| Wrap a router handler to catch and converts legacy boom errors to proper custom errors. |
| [patch](./kibana-plugin-core-server.irouter.patch.md) | RouteRegistrar<'patch'>
| Register a route handler for PATCH
request. |
| [post](./kibana-plugin-core-server.irouter.post.md) | RouteRegistrar<'post'>
| Register a route handler for POST
request. |
| [put](./kibana-plugin-core-server.irouter.put.md) | RouteRegistrar<'put'>
| Register a route handler for PUT
request. |
diff --git a/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.md b/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.md
deleted file mode 100644
index 0632b5e5e2297..0000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IScopedRenderingClient](./kibana-plugin-core-server.iscopedrenderingclient.md)
-
-## IScopedRenderingClient interface
-
-
-Signature:
-
-```typescript
-export interface IScopedRenderingClient
-```
-
-## Methods
-
-| Method | Description |
-| --- | --- |
-| [render(options)](./kibana-plugin-core-server.iscopedrenderingclient.render.md) | Generate a KibanaResponse
which renders an HTML page bootstrapped with the core
bundle. Intended as a response body for HTTP route handlers. |
-
diff --git a/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.render.md b/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.render.md
deleted file mode 100644
index ca114bed21149..0000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.render.md
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IScopedRenderingClient](./kibana-plugin-core-server.iscopedrenderingclient.md) > [render](./kibana-plugin-core-server.iscopedrenderingclient.render.md)
-
-## IScopedRenderingClient.render() method
-
-Generate a `KibanaResponse` which renders an HTML page bootstrapped with the `core` bundle. Intended as a response body for HTTP route handlers.
-
-Signature:
-
-```typescript
-render(options?: Pick): Promise;
-```
-
-## Parameters
-
-| Parameter | Type | Description |
-| --- | --- | --- |
-| options | Pick<IRenderOptions, 'includeUserSettings'>
| |
-
-Returns:
-
-`Promise`
-
-## Example
-
-
-```ts
-router.get(
- { path: '/', validate: false },
- (context, request, response) =>
- response.ok({
- body: await context.core.rendering.render(),
- headers: {
- 'content-security-policy': context.core.http.csp.header,
- },
- })
-);
-
-```
-
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.md b/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.md
index f037b7b3e7cb2..a5c1d59be06d3 100644
--- a/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.md
+++ b/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.md
@@ -20,4 +20,5 @@ export interface LegacyServiceSetupDeps
| --- | --- | --- |
| [core](./kibana-plugin-core-server.legacyservicesetupdeps.core.md) | LegacyCoreSetup
| |
| [plugins](./kibana-plugin-core-server.legacyservicesetupdeps.plugins.md) | Record<string, unknown>
| |
+| [uiPlugins](./kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md) | UiPlugins
| |
diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md b/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md
new file mode 100644
index 0000000000000..d19a7dfcbfcfa
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LegacyServiceSetupDeps](./kibana-plugin-core-server.legacyservicesetupdeps.md) > [uiPlugins](./kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md)
+
+## LegacyServiceSetupDeps.uiPlugins property
+
+Signature:
+
+```typescript
+uiPlugins: UiPlugins;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md
index 5c0f10cac5179..5450e84417f89 100644
--- a/docs/development/core/server/kibana-plugin-core-server.md
+++ b/docs/development/core/server/kibana-plugin-core-server.md
@@ -80,6 +80,9 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) | |
| [ErrorHttpResponseOptions](./kibana-plugin-core-server.errorhttpresponseoptions.md) | HTTP response parameters |
| [FakeRequest](./kibana-plugin-core-server.fakerequest.md) | Fake request object created manually by Kibana plugins. |
+| [HttpResources](./kibana-plugin-core-server.httpresources.md) | HttpResources service is responsible for serving static & dynamic assets for Kibana application via HTTP. Provides API allowing plug-ins to respond with: - a pre-configured HTML page bootstrapping Kibana client app - custom HTML page - custom JS script file. |
+| [HttpResourcesRenderOptions](./kibana-plugin-core-server.httpresourcesrenderoptions.md) | Allows to configure HTTP response parameters |
+| [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) | Extended set of [KibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) helpers used to respond with HTML or JS resource. |
| [HttpResponseOptions](./kibana-plugin-core-server.httpresponseoptions.md) | HTTP response parameters |
| [HttpServerInfo](./kibana-plugin-core-server.httpserverinfo.md) | |
| [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) | Kibana HTTP Service provides own abstraction for work with HTTP stack. Plugins don't have direct access to hapi
server and its primitives anymore. Moreover, plugins shouldn't rely on the fact that HTTP Service uses one or another library under the hood. This gives the platform flexibility to upgrade or changing our internal HTTP stack without breaking plugins. If the HTTP Service lacks functionality you need, we are happy to discuss and support your needs. |
@@ -92,7 +95,6 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [IndexSettingsDeprecationInfo](./kibana-plugin-core-server.indexsettingsdeprecationinfo.md) | |
| [IRenderOptions](./kibana-plugin-core-server.irenderoptions.md) | |
| [IRouter](./kibana-plugin-core-server.irouter.md) | Registers route handlers for specified resource path and method. See [RouteConfig](./kibana-plugin-core-server.routeconfig.md) and [RequestHandler](./kibana-plugin-core-server.requesthandler.md) for more information about arguments to route registrations. |
-| [IScopedRenderingClient](./kibana-plugin-core-server.iscopedrenderingclient.md) | |
| [IUiSettingsClient](./kibana-plugin-core-server.iuisettingsclient.md) | Server-side client that provides access to the advanced settings stored in elasticsearch. The settings provide control over the behavior of the Kibana application. For example, a user can specify how to display numeric or date fields. Users can adjust the settings via Management UI. |
| [KibanaRequestEvents](./kibana-plugin-core-server.kibanarequestevents.md) | Request events. |
| [KibanaRequestRoute](./kibana-plugin-core-server.kibanarequestroute.md) | Request specific route information exposed to a handler. |
@@ -118,7 +120,7 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [PluginConfigDescriptor](./kibana-plugin-core-server.pluginconfigdescriptor.md) | Describes a plugin configuration properties. |
| [PluginInitializerContext](./kibana-plugin-core-server.plugininitializercontext.md) | Context that's available to plugins during initialization stage. |
| [PluginManifest](./kibana-plugin-core-server.pluginmanifest.md) | Describes the set of required and optional properties plugin can define in its mandatory JSON manifest file. |
-| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.Provides the following clients and services: - [rendering](./kibana-plugin-core-server.iscopedrenderingclient.md) - Rendering client which uses the data of the incoming request - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.dataClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request |
+| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.dataClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request |
| [RouteConfig](./kibana-plugin-core-server.routeconfig.md) | Route specific configuration. |
| [RouteConfigOptions](./kibana-plugin-core-server.routeconfigoptions.md) | Additional route options. |
| [RouteConfigOptionsBody](./kibana-plugin-core-server.routeconfigoptionsbody.md) | Additional body options for a route |
@@ -216,6 +218,8 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [HandlerFunction](./kibana-plugin-core-server.handlerfunction.md) | A function that accepts a context object and an optional number of additional arguments. Used for the generic types in [IContextContainer](./kibana-plugin-core-server.icontextcontainer.md) |
| [HandlerParameters](./kibana-plugin-core-server.handlerparameters.md) | Extracts the types of the additional arguments of a [HandlerFunction](./kibana-plugin-core-server.handlerfunction.md), excluding the [HandlerContextType](./kibana-plugin-core-server.handlercontexttype.md). |
| [Headers](./kibana-plugin-core-server.headers.md) | Http request headers to read. |
+| [HttpResourcesRequestHandler](./kibana-plugin-core-server.httpresourcesrequesthandler.md) | Extended version of [RequestHandler](./kibana-plugin-core-server.requesthandler.md) having access to [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) to respond with HTML or JS resources. |
+| [HttpResourcesResponseOptions](./kibana-plugin-core-server.httpresourcesresponseoptions.md) | HTTP Resources response parameters |
| [HttpResponsePayload](./kibana-plugin-core-server.httpresponsepayload.md) | Data send to the client as a response payload. |
| [IBasePath](./kibana-plugin-core-server.ibasepath.md) | Access or manipulate the Kibana base path[BasePath](./kibana-plugin-core-server.basepath.md) |
| [IClusterClient](./kibana-plugin-core-server.iclusterclient.md) | Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)
).See [ClusterClient](./kibana-plugin-core-server.clusterclient.md). |
@@ -245,6 +249,7 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [RequestHandler](./kibana-plugin-core-server.requesthandler.md) | A function executed when route path matched requested resource path. Request handler is expected to return a result of one of [KibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) functions. |
| [RequestHandlerContextContainer](./kibana-plugin-core-server.requesthandlercontextcontainer.md) | An object that handles registration of http request context providers. |
| [RequestHandlerContextProvider](./kibana-plugin-core-server.requesthandlercontextprovider.md) | Context provider for request handler. Extends request context object with provided functionality or data. |
+| [RequestHandlerWrapper](./kibana-plugin-core-server.requesthandlerwrapper.md) | Type-safe wrapper for [RequestHandler](./kibana-plugin-core-server.requesthandler.md) function. |
| [ResponseError](./kibana-plugin-core-server.responseerror.md) | Error message and optional data send to the client in case of error. |
| [ResponseErrorAttributes](./kibana-plugin-core-server.responseerrorattributes.md) | Additional data to provide error details. |
| [ResponseHeaders](./kibana-plugin-core-server.responseheaders.md) | Http response headers to set. |
diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandler.md b/docs/development/core/server/kibana-plugin-core-server.requesthandler.md
index 156f38fab0983..cecef7c923568 100644
--- a/docs/development/core/server/kibana-plugin-core-server.requesthandler.md
+++ b/docs/development/core/server/kibana-plugin-core-server.requesthandler.md
@@ -9,7 +9,7 @@ A function executed when route path matched requested resource path. Request han
Signature:
```typescript
-export declare type RequestHandler = (context: RequestHandlerContext, request: KibanaRequest
, response: KibanaResponseFactory) => IKibanaResponse | Promise>;
+export declare type RequestHandler = (context: RequestHandlerContext, request: KibanaRequest
, response: ResponseFactory) => IKibanaResponse | Promise>;
```
## Example
diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md
index 3c6bee114b6ab..0d640e52c3a03 100644
--- a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md
+++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md
@@ -8,7 +8,6 @@
```typescript
core: {
- rendering: IScopedRenderingClient;
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md
index b65ae47f0e0c1..0966b91a4ebf2 100644
--- a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md
+++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md
@@ -6,7 +6,7 @@
Plugin specific context passed to a route handler.
-Provides the following clients and services: - [rendering](./kibana-plugin-core-server.iscopedrenderingclient.md) - Rendering client which uses the data of the incoming request - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.dataClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request
+Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.dataClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request
Signature:
@@ -18,5 +18,5 @@ export interface RequestHandlerContext
| Property | Type | Description |
| --- | --- | --- |
-| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | {
rendering: IScopedRenderingClient;
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
};
elasticsearch: {
dataClient: IScopedClusterClient;
adminClient: IScopedClusterClient;
};
uiSettings: {
client: IUiSettingsClient;
};
}
| |
+| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | {
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
};
elasticsearch: {
dataClient: IScopedClusterClient;
adminClient: IScopedClusterClient;
};
uiSettings: {
client: IUiSettingsClient;
};
}
| |
diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlerwrapper.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlerwrapper.md
new file mode 100644
index 0000000000000..a9fe188ee2bff
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlerwrapper.md
@@ -0,0 +1,27 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [RequestHandlerWrapper](./kibana-plugin-core-server.requesthandlerwrapper.md)
+
+## RequestHandlerWrapper type
+
+Type-safe wrapper for [RequestHandler](./kibana-plugin-core-server.requesthandler.md) function.
+
+Signature:
+
+```typescript
+export declare type RequestHandlerWrapper = (handler: RequestHandler
) => RequestHandler
;
+```
+
+## Example
+
+
+```typescript
+export const wrapper: RequestHandlerWrapper = handler => {
+ return async (context, request, response) => {
+ // do some logic
+ ...
+ };
+}
+
+```
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.responseheaders.md b/docs/development/core/server/kibana-plugin-core-server.responseheaders.md
index 4551d1cab8632..fb7d6a10c6b6c 100644
--- a/docs/development/core/server/kibana-plugin-core-server.responseheaders.md
+++ b/docs/development/core/server/kibana-plugin-core-server.responseheaders.md
@@ -9,9 +9,5 @@ Http response headers to set.
Signature:
```typescript
-export declare type ResponseHeaders = {
- [header in KnownHeaders]?: string | string[];
-} & {
- [header: string]: string | string[];
-};
+export declare type ResponseHeaders = Record | Record;
```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md
index d8202545f0eae..a8894286de910 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md
@@ -25,6 +25,5 @@ This is only internal for now, and will only be public when we expose the regist
| [mappings](./kibana-plugin-core-server.savedobjectstype.mappings.md) | SavedObjectsTypeMappingDefinition
| The [mapping definition](./kibana-plugin-core-server.savedobjectstypemappingdefinition.md) for the type. |
| [migrations](./kibana-plugin-core-server.savedobjectstype.migrations.md) | SavedObjectMigrationMap
| An optional map of [migrations](./kibana-plugin-core-server.savedobjectmigrationfn.md) to be used to migrate the type. |
| [name](./kibana-plugin-core-server.savedobjectstype.name.md) | string
| The name of the type, which is also used as the internal id. |
-| [namespaceAgnostic](./kibana-plugin-core-server.savedobjectstype.namespaceagnostic.md) | boolean
| Is the type global (true), or not (false). |
| [namespaceType](./kibana-plugin-core-server.savedobjectstype.namespacetype.md) | SavedObjectsNamespaceType
| The [namespace type](./kibana-plugin-core-server.savedobjectsnamespacetype.md) for the type. |
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespaceagnostic.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespaceagnostic.md
deleted file mode 100644
index e347421590482..0000000000000
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespaceagnostic.md
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsType](./kibana-plugin-core-server.savedobjectstype.md) > [namespaceAgnostic](./kibana-plugin-core-server.savedobjectstype.namespaceagnostic.md)
-
-## SavedObjectsType.namespaceAgnostic property
-
-> Warning: This API is now obsolete.
->
-> Use `namespaceType` instead.
->
-
-Is the type global (true), or not (false).
-
-Signature:
-
-```typescript
-namespaceAgnostic?: boolean;
-```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespacetype.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespacetype.md
index 69912f9144980..3a3b0f7f3a9a5 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespacetype.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespacetype.md
@@ -9,5 +9,5 @@ The [namespace type](./kibana-plugin-core-server.savedobjectsnamespacetype.md) f
Signature:
```typescript
-namespaceType?: SavedObjectsNamespaceType;
+namespaceType: SavedObjectsNamespaceType;
```
diff --git a/docs/images/add_remote_cluster.png b/docs/images/add_remote_cluster.png
index 376b1d8392366..160d29b741c62 100755
Binary files a/docs/images/add_remote_cluster.png and b/docs/images/add_remote_cluster.png differ
diff --git a/docs/images/auto_follow_pattern.png b/docs/images/auto_follow_pattern.png
index 3bf86458eddd7..f80de9352280f 100755
Binary files a/docs/images/auto_follow_pattern.png and b/docs/images/auto_follow_pattern.png differ
diff --git a/docs/images/cross-cluster-replication-list-view.png b/docs/images/cross-cluster-replication-list-view.png
new file mode 100755
index 0000000000000..4c45174cff7f1
Binary files /dev/null and b/docs/images/cross-cluster-replication-list-view.png differ
diff --git a/docs/images/follower_indices.png b/docs/images/follower_indices.png
old mode 100644
new mode 100755
index f103bb3cf2acf..505adeb45ae23
Binary files a/docs/images/follower_indices.png and b/docs/images/follower_indices.png differ
diff --git a/docs/images/remote-clusters-list-view.png b/docs/images/remote-clusters-list-view.png
new file mode 100755
index 0000000000000..c28379863b74b
Binary files /dev/null and b/docs/images/remote-clusters-list-view.png differ
diff --git a/docs/management/managing-ccr.asciidoc b/docs/management/managing-ccr.asciidoc
new file mode 100644
index 0000000000000..b2db5a80cfe7e
--- /dev/null
+++ b/docs/management/managing-ccr.asciidoc
@@ -0,0 +1,73 @@
+[role="xpack"]
+[[managing-cross-cluster-replication]]
+== Cross-Cluster Replication
+
+Use *Cross-Cluster Replication* to reproduce indices in
+remote clusters on a local cluster. {ref}/xpack-ccr.html[Cross-cluster replication]
+is commonly used to provide remote backups for disaster recovery and for
+geo-proximite copies of data.
+
+To get started, go to *Management > Cross-Cluster Replication*.
+
+[role="screenshot"]
+image::images/cross-cluster-replication-list-view.png[][Cross-cluster replication list view]
+
+[float]
+=== Prerequisites
+
+* You must have a {ref}/modules-remote-clusters.html[remote cluster].
+* Leader indices must meet {ref}/ccr-requirements.html[these requirements].
+* The Elasticsearch version of the local cluster must be the same as or newer than the remote cluster.
+Refer to {ref}/ccr-overview.html[this document] for more information.
+
+[float]
+[[configure-replication]]
+=== Configure replication
+
+Replication requires a leader index, the index being replicated, and a
+follower index, which will contain the leader index's replicated data.
+The follower index is passive in that it can read requests and searches,
+but cannot accept direct writes. Only the leader index is active for direct writes.
+
+You can configure follower indices in two ways:
+
+* Create specific follower indices
+* Create follower indices from an auto-follow pattern
+
+[float]
+==== Create specific follower indices
+
+To replicate data from existing indices, or set up local followers on a case-by-case basis,
+go to *Follower indices*. When you create the follower index, you must reference the
+remote cluster and the leader index that you created in the remote cluster.
+
+[role="screenshot"]
+image::images/follower_indices.png[][UI for adding follower indices]
+
+[float]
+==== Create follower indices from an auto-follow pattern
+
+To automatically detect and follow new indices when they are created on a remote cluster,
+go to *Auto-follow patterns*. Creating an auto-follow pattern is useful when you have
+time series data, like event logs, on the remote cluster that is created or rolled over on a daily basis.
+
+When creating the pattern, you must reference the remote cluster that you
+connected to your local cluster. You must also specify a collection of index patterns
+that match the indices you want to automatically follow.
+
+Once you configure an
+auto-follow pattern, any time a new index with a name that matches the pattern is
+created in the remote cluster, a follower index is automatically configured in the local cluster.
+
+[role="screenshot"]
+image::images/auto_follow_pattern.png[UI for adding an auto-follow pattern]
+
+[float]
+[[manage-replication]]
+=== Manage replication
+
+Use the list views in *Cross-Cluster Replication* to monitor whether the replication is active and
+pause and resume replication. You can also edit and remove the follower indices and auto-follow patterns.
+
+For an example of cross-cluster replication,
+refer to https://www.elastic.co/blog/bi-directional-replication-with-elasticsearch-cross-cluster-replication-ccr[Bi-directional replication with Elasticsearch cross-cluster replication].
diff --git a/docs/management/managing-remote-clusters.asciidoc b/docs/management/managing-remote-clusters.asciidoc
index 6b69cfef5b768..00ec5c7d2ddea 100644
--- a/docs/management/managing-remote-clusters.asciidoc
+++ b/docs/management/managing-remote-clusters.asciidoc
@@ -1,67 +1,39 @@
[[working-remote-clusters]]
== Remote Clusters
-{kib} *Management* provides user interfaces for working with data from remote
-clusters and managing the {ccr} process. You can replicate indices from a
-leader remote cluster to a follower index in a local cluster. The local follower indices
-can be used to provide remote backups for disaster recovery or for geo-proximite copies of data.
+Use *Remote Clusters* to establish a unidirectional
+connection from your cluster to other clusters. This functionality is
+required for {ref}/xpack-ccr.html[cross-cluster replication] and
+{ref}/modules-cross-cluster-search.html[cross-cluster search].
-Before using these features, you should be familiar with the following concepts:
+To get started, go to *Management > Remote Clusters*.
-* {ref}/xpack-ccr.html[{ccr-cap}]
-* {ref}/modules-cross-cluster-search.html[{ccs-cap}]
-* {ref}/cross-cluster-configuring.html[Cross-cluster security requirements]
+[role="screenshot"]
+image::images/remote-clusters-list-view.png[Remote Clusters list view, including Add a remote cluster button]
[float]
[[managing-remote-clusters]]
-== Managing remote clusters
-
-*Remote clusters* helps you manage remote clusters for use with
-{ccs} and {ccr}. You can add and remove remote clusters and check their connectivity.
+=== Add a remote cluster
-Before you use this feature, you should be familiar with the concept of
-{ref}/modules-remote-clusters.html[remote clusters].
+A {ref}/modules-remote-clusters.html[remote cluster] connection works by configuring a remote cluster and
+connecting to a limited number of nodes, called {ref}/modules-remote-clusters.html#sniff-mode[seed nodes],
+in that cluster.
+Alternatively, you can define a single proxy address for the remote cluster.
-Go to *Management > Elasticsearch > Remote clusters* to create or manage your remotes.
+By default, a cross-cluster request, such as a cross-cluster search or
+replication request, fails if any cluster in the request is unavailable.
+To skip a cluster when its unavailable,
+set *Skip if unavailable* to true.
-To set up a new remote, click *Add a remote cluster*. Give the cluster a unique name
-and define the seed nodes for cluster discovery. You can edit or remove your remote clusters
-from the *Remote clusters* list view.
+Once you add a remote cluster, you can configure <>
+to reproduce indices in the remote cluster on a local cluster.
[role="screenshot"]
image::images/add_remote_cluster.png[][UI for adding a remote cluster]
-Once a remote cluster is registered, you can use the tools under *{ccr-cap}*
-to add and manage follower indices on the local cluster, and replicate data from
-indices on the remote cluster based on an auto-follow index pattern.
-
[float]
-[[managing-cross-cluster-replication]]
-== [xpack]#Managing {ccr}#
-
-*{ccr-cap}* helps you create and manage the {ccr} process.
-If you want to replicate data from existing indices, or set up
-local followers on a case-by-case basis, go to *Follower indices*.
-If you want to automatically detect and follow new indices when they are created
-on a remote cluster, you can do so from *Auto-follow patterns*.
-
-Creating an auto-follow pattern is useful when you have time-series data, like a logs index, on the
-remote cluster that is created or rolled over on a daily basis. Once you have configured an
-auto-follow pattern, any time a new index with a name that matches the pattern is
-created in the remote cluster, a follower index is automatically configured in the local cluster.
-
-From the same view, you can also see a list of your saved auto-follow patterns for
-a given remote cluster, and monitor whether the replication is active.
+[[manage-remote-clusters]]
+=== Manage remote clusters
-Before you use these features, you should be familiar with the following concepts:
-
-* {ref}/ccr-requirements.html[Requirements for leader indices]
-* {ref}/ccr-auto-follow.html[Automatically following indices]
-
-To get started, go to *Management > Elasticsearch > {ccr-cap}*.
-
-[role="screenshot"]
-image::images/auto_follow_pattern.png[][UI for adding an auto-follow pattern]
-
-[role="screenshot"]
-image::images/follower_indices.png[][UI for adding follower indices]
+From the *Remote Clusters* list view, you can drill down into each cluster and
+view its status. You can also edit and delete a cluster.
diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc
index fd835bde83322..a5503969a3ec1 100644
--- a/docs/redirects.asciidoc
+++ b/docs/redirects.asciidoc
@@ -23,7 +23,7 @@ For more {kib} configuration settings, see <>.
[role="exclude",id="uptime-security"]
== Uptime security
-This page has moved. Please see the new section in the {uptime-guide}/uptime-security.html[Uptime Monitoring Guide].
+This page has moved. Please see the new section in the {heartbeat-ref}/securing-heartbeat.html[Uptime Monitoring Guide].
[role="exclude",id="infra-read-only-access"]
== Configure source read-only access
diff --git a/docs/uptime-guide/index.asciidoc b/docs/uptime-guide/index.asciidoc
index 7bbc01bb303f1..09763182fa88f 100644
--- a/docs/uptime-guide/index.asciidoc
+++ b/docs/uptime-guide/index.asciidoc
@@ -12,4 +12,3 @@ include::install.asciidoc[]
include::deployment-arch.asciidoc[]
-include::security.asciidoc[]
diff --git a/docs/uptime-guide/install.asciidoc b/docs/uptime-guide/install.asciidoc
index e7c50bb7604ce..0ed1270ca92ce 100644
--- a/docs/uptime-guide/install.asciidoc
+++ b/docs/uptime-guide/install.asciidoc
@@ -56,6 +56,11 @@ Additional information is available in {heartbeat-ref}/heartbeat-configuration.h
[role="screenshot"]
image::images/uptime-setup.png[Installation instructions on the Uptime page in Kibana]
+[[setup-security]]
+=== Step 4: Setup Security
+
+Secure your installation by following the {heartbeat-ref}/securing-heartbeat.html[Secure Heartbeat] documentation.
+
[float]
==== Important considerations
diff --git a/docs/uptime-guide/security.asciidoc b/docs/uptime-guide/security.asciidoc
deleted file mode 100644
index 0c6fa4c6c4f56..0000000000000
--- a/docs/uptime-guide/security.asciidoc
+++ /dev/null
@@ -1,60 +0,0 @@
-[[uptime-security]]
-== Elasticsearch Security
-
-If you use Elasticsearch security, you'll need to enable certain privileges for users
-that would like to access the Uptime app. For example, create user and support roles to implement the privileges:
-
-[float]
-=== Create a role
-
-You'll need a role that lets you access the Heartbeat indices, which by default are `heartbeat-*`.
-You can create this with the following request:
-
-["source","sh",subs="attributes,callouts"]
----------------------------------------------------------------
-PUT /_security/role/uptime
-{ "indices" : [
- {
- "names" : [
- "heartbeat-*"
- ],
- "privileges" : [
- "read",
- "view_index_metadata"
- ],
- "field_security" : {
- "grant" : [
- "*"
- ]
- },
- "allow_restricted_indices" : false
- }
- ],
- "transient_metadata" : {
- "enabled" : true
- }
-}
----------------------------------------------------------------
-// CONSOLE
-
-[float]
-=== Assign the role to a user
-
-Next, you'll need to create a user with both the `uptime` role, and another role with sufficient {kibana-ref}/kibana-privileges.html[Kibana privileges],
-such as the `kibana_admin` role.
-You can do this with the following request:
-
-["source","sh",subs="attributes,callouts"]
----------------------------------------------------------------
-PUT /_security/user/jacknich
-{
- "password" : "j@rV1s",
- "roles" : [ "uptime", "kibana_admin" ],
- "full_name" : "Jack Nicholson",
- "email" : "jacknich@example.com",
- "metadata" : {
- "intelligence" : 7
- }
-}
----------------------------------------------------------------
-// CONSOLE
diff --git a/docs/uptime/images/alert-flyout.png b/docs/uptime/images/alert-flyout.png
new file mode 100644
index 0000000000000..7fc1e3d9aefe2
Binary files /dev/null and b/docs/uptime/images/alert-flyout.png differ
diff --git a/docs/uptime/images/check-history.png b/docs/uptime/images/check-history.png
index 6418495eee9ed..91565bf59aa7f 100644
Binary files a/docs/uptime/images/check-history.png and b/docs/uptime/images/check-history.png differ
diff --git a/docs/uptime/images/error-list.png b/docs/uptime/images/error-list.png
deleted file mode 100644
index 99f017f2945a5..0000000000000
Binary files a/docs/uptime/images/error-list.png and /dev/null differ
diff --git a/docs/uptime/images/monitor-charts.png b/docs/uptime/images/monitor-charts.png
index dbfa43f47656e..522f34662657e 100644
Binary files a/docs/uptime/images/monitor-charts.png and b/docs/uptime/images/monitor-charts.png differ
diff --git a/docs/uptime/images/observability_integrations.png b/docs/uptime/images/observability_integrations.png
index d5c612c7589ca..6589c0c5565dd 100644
Binary files a/docs/uptime/images/observability_integrations.png and b/docs/uptime/images/observability_integrations.png differ
diff --git a/docs/uptime/images/settings.png b/docs/uptime/images/settings.png
new file mode 100644
index 0000000000000..dd36f0a6d702b
Binary files /dev/null and b/docs/uptime/images/settings.png differ
diff --git a/docs/uptime/images/snapshot-view.png b/docs/uptime/images/snapshot-view.png
index 020396d0f3e4c..1fce2e9592c14 100644
Binary files a/docs/uptime/images/snapshot-view.png and b/docs/uptime/images/snapshot-view.png differ
diff --git a/docs/uptime/images/status-bar.png b/docs/uptime/images/status-bar.png
index e0e9b27555900..8d242789cdccd 100644
Binary files a/docs/uptime/images/status-bar.png and b/docs/uptime/images/status-bar.png differ
diff --git a/docs/uptime/index.asciidoc b/docs/uptime/index.asciidoc
index 785b9f818f5bf..a355f8ecf4843 100644
--- a/docs/uptime/index.asciidoc
+++ b/docs/uptime/index.asciidoc
@@ -12,8 +12,10 @@ To get started with Elastic Uptime, refer to {uptime-guide}/install-uptime.html[
* <>
* <>
+* <>
--
include::overview.asciidoc[]
include::monitor.asciidoc[]
+include::settings.asciidoc[]
diff --git a/docs/uptime/monitor.asciidoc b/docs/uptime/monitor.asciidoc
index d54fd02c7c069..8a4be1f11a721 100644
--- a/docs/uptime/monitor.asciidoc
+++ b/docs/uptime/monitor.asciidoc
@@ -5,21 +5,24 @@
The Monitor page will help you get further insight into the performance
of a specific network endpoint. You'll see a detailed visualization of
the monitor's request duration over time, as well as the `up`/`down`
-status over time.
+status over time. You can also also detect anomalies in response time data
+by configuring Machine Learning jobs on this page.
[float]
-=== Status bar
+=== Status panel
[role="screenshot"]
image::uptime/images/status-bar.png[Status bar]
-The Status bar displays a quick summary of the latest information
+The Status panel displays a quick summary of the latest information
regarding your monitor. You can view its latest status, click a link to
visit the targeted URL, see its most recent request duration, and determine the
amount of time that has elapsed since the last check.
-You can use the Status bar to get a quick summary of current performance,
-beyond simply knowing if the monitor is `up` or `down`.
+When two Heartbeat instances are configured in different geographic locations
+the map will show each location as a pinpoint on the map, along with the
+amount of time elapsed since data was last received from that location.
+
[float]
=== Monitor charts
@@ -32,12 +35,14 @@ date range. These charts can help you gain insight into how quickly requests are
by the targeted endpoint, and give you a sense of how frequently a host or endpoint
was down in your selected timespan.
-The first chart displays request duration information for your monitor.
+The Monitor duration chart displays request duration information for your monitor.
The area surrounding the line is the range of request time for the corresponding
-bucket. The line is the average time.
+bucket. The line is the average time. Anomaly detection using Machine Learning
+can be configured in the upper right hand of this panel. When response times change
+in an unexpected way the time range in which they occurred will be given filled with a color.
-Next, is a graphical representation of the check statuses over time. Hover over
-the charts to display crosshairs with more specific numeric data.
+The pings over time chart is a graphical representation of the check statuses over time.
+Hover over the charts to display crosshairs with more specific numeric data.
[role="screenshot"]
image::uptime/images/crosshair-example.png[Chart crosshair]
@@ -49,6 +54,6 @@ image::uptime/images/crosshair-example.png[Chart crosshair]
image::uptime/images/check-history.png[Check history view]
The Check history displays the total count of this monitor's checks for the selected
-date range. You can additionally filter the checks by `status` to help find recent problems
+date range. You can additionally filter the checks by status and location to help find recent problems
on a per-check basis. This table can help you gain some insight into more granular details
about recent individual data points Heartbeat is logging about your host or endpoint.
diff --git a/docs/uptime/overview.asciidoc b/docs/uptime/overview.asciidoc
index 098ce12a56991..71c09c968e512 100644
--- a/docs/uptime/overview.asciidoc
+++ b/docs/uptime/overview.asciidoc
@@ -21,12 +21,12 @@ This control allows you to use automated filter options, as well as input custom
text to select specific monitors by field, URL, ID, and other attributes.
[float]
-=== Snapshot view
+=== Snapshot panel
[role="screenshot"]
image::uptime/images/snapshot-view.png[Snapshot view]
-This view is intended to quickly give you a sense of the overall
+This panel is intended to quickly give you a sense of the overall
status of the environment you're monitoring, or a subset of those monitors.
Here, you can see the total number of detected monitors within the selected
Uptime date range. In addition to the total, the counts for the number of monitors
@@ -49,6 +49,17 @@ way to navigate to a more in-depth visualization for interesting hosts or endpoi
This table includes information like the most recent status, when the monitor was last checked, its
ID and URL, its IP address, and a dedicated sparkline showing its check status over time.
+[float]
+=== Creating and managing alerts
+
+[role="screenshot"]
+image::uptime/images/alert-flyout.png[Create alert flyout]
+
+To receive alerts when a monitor goes down, use the alerting menu at the top of the
+overview page. Use a query in the alert flyout to determine which monitors to check
+with your alert. If you already have a query in the overview page search bar it will
+be carried over into this box.
+
[float]
=== Observability integrations
@@ -60,14 +71,3 @@ Docker related host information, it will provide links to open the Metrics app o
for this host. Additionally, this feature supplies links to simply filter the other views on the host's
IP address, to help you quickly determine if these other solutions contain data relevant to your current
interest.
-
-[float]
-=== Error list
-
-[role="screenshot"]
-image::uptime/images/error-list.png[Error list]
-
-The Error list displays aggregations of errors that Heartbeat has logged. Errors are
-displayed by Error type, monitor ID, and message. Clicking a monitor's ID will take you
-to the corresponding Monitor view, which can provide you richer information about the individual
-data points that are resulting in the displayed errors.
diff --git a/docs/uptime/settings.asciidoc b/docs/uptime/settings.asciidoc
new file mode 100644
index 0000000000000..55da6e802bec6
--- /dev/null
+++ b/docs/uptime/settings.asciidoc
@@ -0,0 +1,27 @@
+[role="xpack"]
+[[uptime-settings]]
+
+== Settings
+
+[role="screenshot"]
+image::uptime/images/settings.png[Filter bar]
+
+The Uptime settings page lets you change which Heartbeat indices are displayed
+by the uptime app. Users must have the 'all' permission to modify items on this page.
+Uptime settings apply to the current space only. Use different settings in different
+spaces to segment different uptime use cases and domains.
+
+As an example, imagine your organization has one team for internal IT services, and another
+for public services. Each team operates independently and is only responsible for its
+own services. In this scenario, you might set up separate Heartbeat instances for each team,
+writing out to index patterns named `it-heartbeat-\*`, and `external-heartbeat-\*`. You would
+create separate roles and users for each in Elasticsearch, each with access to their own spaces,
+named `it` and `external` respectively. Within each space you would navigate to the settings page
+and set the correct index pattern to match only the indices that space is allowed to access.
+
+Note that the pattern set here only restricts what the Uptime app shows. Users may still be able
+to manually query Elasticsearch for data outside this pattern!
+
+See the <>
+and {heartbeat-ref}/securing-heartbeat.html[Heartbeat security]
+docs for more information.
diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc
index fa34802abe2a9..a4ba320e826b1 100644
--- a/docs/user/management.asciidoc
+++ b/docs/user/management.asciidoc
@@ -13,7 +13,7 @@ indices, clusters, licenses, UI settings, index patterns, spaces, and more.
[cols="50, 50"]
|===
-a| <>
+a| <>
Replicate indices on a remote cluster and copy them to a follower index on a local cluster.
This is important for
@@ -85,7 +85,8 @@ set the timespan for notification messages, and much more.
| <>
-Centrally manage your alerts from across {kib}. Create and manage re-usable connectors for triggering actions.
+Centrally manage your alerts across {kib}. Create and manage reusable
+connectors for triggering actions.
| <>
@@ -125,6 +126,8 @@ include::{kib-repo-dir}/management/alerting/connector-management.asciidoc[]
include::{kib-repo-dir}/management/managing-beats.asciidoc[]
+include::{kib-repo-dir}/management/managing-ccr.asciidoc[]
+
include::{kib-repo-dir}/management/index-lifecycle-policies/intro-to-lifecycle-policies.asciidoc[]
include::{kib-repo-dir}/management/index-lifecycle-policies/create-policy.asciidoc[]
diff --git a/examples/embeddable_examples/server/todo_saved_object.ts b/examples/embeddable_examples/server/todo_saved_object.ts
index 0f67c53cfa3e1..58da2014de498 100644
--- a/examples/embeddable_examples/server/todo_saved_object.ts
+++ b/examples/embeddable_examples/server/todo_saved_object.ts
@@ -22,7 +22,7 @@ import { SavedObjectsType } from 'kibana/server';
export const todoSavedObject: SavedObjectsType = {
name: 'todo',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
title: {
diff --git a/package.json b/package.json
index c60cf5234c9f7..8a45ea27d3168 100644
--- a/package.json
+++ b/package.json
@@ -120,7 +120,7 @@
"@babel/core": "^7.9.0",
"@babel/register": "^7.9.0",
"@elastic/apm-rum": "^4.6.0",
- "@elastic/charts": "18.2.2",
+ "@elastic/charts": "18.3.0",
"@elastic/datemath": "5.0.3",
"@elastic/ems-client": "7.8.0",
"@elastic/eui": "21.0.1",
@@ -377,7 +377,7 @@
"@types/recompose": "^0.30.6",
"@types/redux-actions": "^2.6.1",
"@types/request": "^2.48.2",
- "@types/selenium-webdriver": "^4.0.5",
+ "@types/selenium-webdriver": "^4.0.9",
"@types/semver": "^5.5.0",
"@types/sinon": "^7.0.13",
"@types/strip-ansi": "^3.0.0",
@@ -463,6 +463,7 @@
"load-grunt-config": "^3.0.1",
"mocha": "^7.1.1",
"mock-http-server": "1.3.0",
+ "ms-chromium-edge-driver": "^0.2.3",
"multistream": "^2.1.1",
"murmurhash3js": "3.0.1",
"mutation-observer": "^1.0.3",
@@ -481,7 +482,7 @@
"react-textarea-autosize": "^7.1.2",
"regenerate": "^1.4.0",
"sass-lint": "^1.12.1",
- "selenium-webdriver": "^4.0.0-alpha.5",
+ "selenium-webdriver": "^4.0.0-alpha.7",
"simple-git": "1.116.0",
"simplebar-react": "^2.1.0",
"sinon": "^7.4.2",
diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts
index 66f17ab579ec3..f4b91d154cbb8 100644
--- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts
+++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts
@@ -136,7 +136,7 @@ export const schema = Joi.object()
browser: Joi.object()
.keys({
type: Joi.string()
- .valid('chrome', 'firefox', 'ie')
+ .valid('chrome', 'firefox', 'ie', 'msedge')
.default('chrome'),
logPollingMs: Joi.number().default(100),
diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json
index 7c5d6a62a11ca..c8614b1df9d5d 100644
--- a/packages/kbn-ui-shared-deps/package.json
+++ b/packages/kbn-ui-shared-deps/package.json
@@ -9,7 +9,7 @@
"kbn:watch": "node scripts/build --watch"
},
"dependencies": {
- "@elastic/charts": "18.2.2",
+ "@elastic/charts": "18.3.0",
"@elastic/eui": "21.0.1",
"@kbn/i18n": "1.0.0",
"abortcontroller-polyfill": "^1.4.0",
diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md
index 368d1f47e9c3f..80f12dd78214d 100644
--- a/src/core/MIGRATION.md
+++ b/src/core/MIGRATION.md
@@ -1252,26 +1252,27 @@ import { npStart: { plugins } } from 'ui/new_platform';
In server code, `core` can be accessed from either `server.newPlatform` or `kbnServer.newPlatform`. There are not currently very many services available on the server-side:
-| Legacy Platform | New Platform | Notes |
-| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
-| `server.config()` | [`initializerContext.config.create()`](/docs/development/core/server/kibana-plugin-core-server.plugininitializercontext.config.md) | Must also define schema. See _[how to configure plugin](#configure-plugin)_ |
-| `server.route` | [`core.http.createRouter`](/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.createrouter.md) | [Examples](./MIGRATION_EXAMPLES.md#route-registration) |
-| `server.renderApp()` / `server.renderAppWithDefaultConfig()` | [`context.rendering.render()`](/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.render.md) | [Examples](./MIGRATION_EXAMPLES.md#render-html-content) |
-| `request.getBasePath()` | [`core.http.basePath.get`](/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.basepath.md) | |
+| Legacy Platform | New Platform | Notes |
+| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
+| `server.config()` | [`initializerContext.config.create()`](/docs/development/core/server/kibana-plugin-core-server.plugininitializercontext.config.md) | Must also define schema. See _[how to configure plugin](#configure-plugin)_ |
+| `server.route` | [`core.http.createRouter`](/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.createrouter.md) | [Examples](./MIGRATION_EXAMPLES.md#route-registration) |
+| `server.renderApp()` | [`response.renderCoreApp()`](docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md) | [Examples](./MIGRATION_EXAMPLES.md#render-html-content) |
+| `server.renderAppWithDefaultConfig()` | [`response.renderAnonymousCoreApp()`](docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md) | [Examples](./MIGRATION_EXAMPLES.md#render-html-content) |
+| `request.getBasePath()` | [`core.http.basePath.get`](/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.basepath.md) | |
| `server.plugins.elasticsearch.getCluster('data')` | [`context.core.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-core-server.iscopedclusterclient.md) | |
| `server.plugins.elasticsearch.getCluster('admin')` | [`context.core.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-core-server.iscopedclusterclient.md) | |
-| `server.plugins.elasticsearch.createCluster(...)` | [`core.elasticsearch.legacy.createClient`](/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.legacy.md) | |
-| `server.savedObjects.setScopedSavedObjectsClientFactory` | [`core.savedObjects.setClientFactoryProvider`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.setclientfactoryprovider.md) | |
-| `server.savedObjects.addScopedSavedObjectsClientWrapperFactory` | [`core.savedObjects.addClientWrapper`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.addclientwrapper.md) | |
+| `server.plugins.elasticsearch.createCluster(...)` | [`core.elasticsearch.legacy.createClient`](/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.legacy.md) | |
+| `server.savedObjects.setScopedSavedObjectsClientFactory` | [`core.savedObjects.setClientFactoryProvider`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.setclientfactoryprovider.md) | |
+| `server.savedObjects.addScopedSavedObjectsClientWrapperFactory` | [`core.savedObjects.addClientWrapper`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.addclientwrapper.md) | |
| `server.savedObjects.getSavedObjectsRepository` | [`core.savedObjects.createInternalRepository`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicestart.createinternalrepository.md) [`core.savedObjects.createScopedRepository`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicestart.createscopedrepository.md) | |
-| `server.savedObjects.getScopedSavedObjectsClient` | [`core.savedObjects.getScopedClient`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicestart.getscopedclient.md) | |
-| `request.getSavedObjectsClient` | [`context.core.savedObjects.client`](/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md) | |
+| `server.savedObjects.getScopedSavedObjectsClient` | [`core.savedObjects.getScopedClient`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicestart.getscopedclient.md) | |
+| `request.getSavedObjectsClient` | [`context.core.savedObjects.client`](/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md) | |
| `request.getUiSettingsService` | [`context.core.uiSettings.client`](/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md) | |
-| `kibana.Plugin.deprecations` | [Handle plugin configuration deprecations](#handle-plugin-config-deprecations) and [`PluginConfigDescriptor.deprecations`](docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md) | Deprecations from New Platform are not applied to legacy configuration |
-| `kibana.Plugin.savedObjectSchemas` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) |
-| `kibana.Plugin.mappings` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) |
-| `kibana.Plugin.migrations` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) |
-| `kibana.Plugin.savedObjectsManagement` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) |
+| `kibana.Plugin.deprecations` | [Handle plugin configuration deprecations](#handle-plugin-config-deprecations) and [`PluginConfigDescriptor.deprecations`](docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md) | Deprecations from New Platform are not applied to legacy configuration |
+| `kibana.Plugin.savedObjectSchemas` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) |
+| `kibana.Plugin.mappings` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) |
+| `kibana.Plugin.migrations` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) |
+| `kibana.Plugin.savedObjectsManagement` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) |
_See also: [Server's CoreSetup API Docs](/docs/development/core/server/kibana-plugin-core-server.coresetup.md)_
@@ -1494,8 +1495,9 @@ The above example looks in the new platform as
```
The [request handler context](/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md) exposed the next scoped **core** services:
-| Legacy Platform | New Platform |
-| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------|
+
+| Legacy Platform | New Platform |
+| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `request.getSavedObjectsClient` | [`context.savedObjects.client`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md) |
| `server.plugins.elasticsearch.getCluster('admin')` | [`context.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-core-server.iscopedclusterclient.md) |
| `server.plugins.elasticsearch.getCluster('data')` | [`context.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-core-server.iscopedclusterclient.md) |
diff --git a/src/core/MIGRATION_EXAMPLES.md b/src/core/MIGRATION_EXAMPLES.md
index 37d0b9297ed3c..8c5fe4875aaea 100644
--- a/src/core/MIGRATION_EXAMPLES.md
+++ b/src/core/MIGRATION_EXAMPLES.md
@@ -700,21 +700,15 @@ application.register({
## Render HTML Content
You can return a blank HTML page bootstrapped with the core application bundle from an HTTP route handler
-via the `rendering` context. You may wish to do this if you are rendering a chromeless application with a
+via the `httpResources` service. You may wish to do this if you are rendering a chromeless application with a
custom application route or have other custom rendering needs.
-```ts
-router.get(
+```typescript
+httpResources.register(
{ path: '/chromeless', validate: false },
(context, request, response) => {
- const { http, rendering } = context.core;
-
- return response.ok({
- body: await rendering.render(), // generates an HTML document
- headers: {
- 'content-security-policy': http.csp.header,
- },
- });
+ //... some logic
+ return response.renderCoreApp();
}
);
```
@@ -724,18 +718,12 @@ comprises all UI Settings that are *user provided*, then injected into the page.
You may wish to exclude fetching this data if not authorized or to slim the page
size.
-```ts
-router.get(
- { path: '/', validate: false },
+```typescript
+httpResources.register(
+ { path: '/', validate: false, options: { authRequired: false } },
(context, request, response) => {
- const { http, rendering } = context.core;
-
- return response.ok({
- body: await rendering.render({ includeUserSettings: false }),
- headers: {
- 'content-security-policy': http.csp.header,
- },
- });
+ //... some logic
+ return response.renderAnonymousCoreApp();
}
);
```
diff --git a/src/core/server/http/index.ts b/src/core/server/http/index.ts
index a75eb04fa0120..ca9dfde2e71dc 100644
--- a/src/core/server/http/index.ts
+++ b/src/core/server/http/index.ts
@@ -38,6 +38,7 @@ export {
LifecycleResponseFactory,
RedirectResponseOptions,
RequestHandler,
+ RequestHandlerWrapper,
ResponseError,
ResponseErrorAttributes,
ResponseHeaders,
diff --git a/src/core/server/http/lifecycle/on_pre_response.ts b/src/core/server/http/lifecycle/on_pre_response.ts
index 50d3d7b47bf8d..050881472bc80 100644
--- a/src/core/server/http/lifecycle/on_pre_response.ts
+++ b/src/core/server/http/lifecycle/on_pre_response.ts
@@ -148,7 +148,7 @@ function findHeadersIntersection(
log: Logger
) {
Object.keys(headers).forEach(headerName => {
- if (responseHeaders[headerName] !== undefined) {
+ if (Reflect.has(responseHeaders, headerName)) {
log.warn(`onPreResponseHandler rewrote a response header [${headerName}].`);
}
});
diff --git a/src/core/server/http/router/error_wrapper.ts b/src/core/server/http/router/error_wrapper.ts
index 8f895753c38c3..af99812eff4b3 100644
--- a/src/core/server/http/router/error_wrapper.ts
+++ b/src/core/server/http/router/error_wrapper.ts
@@ -18,20 +18,10 @@
*/
import Boom from 'boom';
-import { KibanaRequest } from './request';
-import { KibanaResponseFactory } from './response';
-import { RequestHandler } from './router';
-import { RequestHandlerContext } from '../../../server';
-import { RouteMethod } from './route';
+import { RequestHandlerWrapper } from './router';
-export const wrapErrors = (
- handler: RequestHandler
-): RequestHandler
=> {
- return async (
- context: RequestHandlerContext,
- request: KibanaRequest
,
- response: KibanaResponseFactory
- ) => {
+export const wrapErrors: RequestHandlerWrapper = handler => {
+ return async (context, request, response) => {
try {
return await handler(context, request, response);
} catch (e) {
diff --git a/src/core/server/http/router/headers.ts b/src/core/server/http/router/headers.ts
index 19eaee5081996..b79cc0d325f1e 100644
--- a/src/core/server/http/router/headers.ts
+++ b/src/core/server/http/router/headers.ts
@@ -56,9 +56,9 @@ export type Headers = { [header in KnownHeaders]?: string | string[] | undefined
* Http response headers to set.
* @public
*/
-export type ResponseHeaders = { [header in KnownHeaders]?: string | string[] } & {
- [header: string]: string | string[];
-};
+export type ResponseHeaders =
+ | Record
+ | Record;
const normalizeHeaderField = (field: string) => field.trim().toLowerCase();
diff --git a/src/core/server/http/router/index.ts b/src/core/server/http/router/index.ts
index d254f391ca5e4..83ceff4a25d86 100644
--- a/src/core/server/http/router/index.ts
+++ b/src/core/server/http/router/index.ts
@@ -18,7 +18,7 @@
*/
export { Headers, filterHeaders, ResponseHeaders, KnownHeaders } from './headers';
-export { Router, RequestHandler, IRouter, RouteRegistrar } from './router';
+export { Router, RequestHandler, RequestHandlerWrapper, IRouter, RouteRegistrar } from './router';
export {
KibanaRequest,
KibanaRequestEvents,
diff --git a/src/core/server/http/router/router.ts b/src/core/server/http/router/router.ts
index bb56ee3727d1a..b4e7fc2a989b6 100644
--- a/src/core/server/http/router/router.ts
+++ b/src/core/server/http/router/router.ts
@@ -98,7 +98,7 @@ export interface IRouter {
* Wrap a router handler to catch and converts legacy boom errors to proper custom errors.
* @param handler {@link RequestHandler} - a route handler to wrap
*/
- handleLegacyErrors: (handler: RequestHandler
) => RequestHandler
;
+ handleLegacyErrors: RequestHandlerWrapper;
/**
* Returns all routes registered with this router.
@@ -237,9 +237,7 @@ export class Router implements IRouter {
return [...this.routes];
}
- public handleLegacyErrors
(handler: RequestHandler
): RequestHandler
{
- return wrapErrors(handler);
- }
+ public handleLegacyErrors = wrapErrors;
private async handle
({
routeSchemas,
@@ -316,9 +314,33 @@ export type RequestHandler<
P = unknown,
Q = unknown,
B = unknown,
- Method extends RouteMethod = any
+ Method extends RouteMethod = any,
+ ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory
> = (
context: RequestHandlerContext,
request: KibanaRequest
,
- response: KibanaResponseFactory
+ response: ResponseFactory
) => IKibanaResponse | Promise>;
+
+/**
+ * Type-safe wrapper for {@link RequestHandler} function.
+ * @example
+ * ```typescript
+ * export const wrapper: RequestHandlerWrapper = handler => {
+ * return async (context, request, response) => {
+ * // do some logic
+ * ...
+ * };
+ * }
+ * ```
+ * @public
+ */
+export type RequestHandlerWrapper = <
+ P,
+ Q,
+ B,
+ Method extends RouteMethod = any,
+ ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory
+>(
+ handler: RequestHandler
+) => RequestHandler
;
diff --git a/src/core/server/http_resources/http_resources_service.mock.ts b/src/core/server/http_resources/http_resources_service.mock.ts
new file mode 100644
index 0000000000000..4536b0898cad9
--- /dev/null
+++ b/src/core/server/http_resources/http_resources_service.mock.ts
@@ -0,0 +1,50 @@
+/*
+ * 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 { httpServerMock } from '../http/http_server.mocks';
+import { HttpResources, HttpResourcesServiceToolkit } from './types';
+
+const createHttpResourcesMock = (): jest.Mocked => ({
+ register: jest.fn(),
+});
+
+function createInternalHttpResourcesSetup() {
+ return {
+ createRegistrar: createHttpResourcesMock,
+ };
+}
+
+function createHttpResourcesResponseFactory() {
+ const mocked: jest.Mocked = {
+ renderCoreApp: jest.fn(),
+ renderAnonymousCoreApp: jest.fn(),
+ renderHtml: jest.fn(),
+ renderJs: jest.fn(),
+ };
+
+ return {
+ ...httpServerMock.createResponseFactory(),
+ ...mocked,
+ };
+}
+
+export const httpResourcesMock = {
+ createRegistrar: createHttpResourcesMock,
+ createSetupContract: createInternalHttpResourcesSetup,
+ createResponseFactory: createHttpResourcesResponseFactory,
+};
diff --git a/src/core/server/http_resources/http_resources_service.test.ts b/src/core/server/http_resources/http_resources_service.test.ts
new file mode 100644
index 0000000000000..e6f129ba12d78
--- /dev/null
+++ b/src/core/server/http_resources/http_resources_service.test.ts
@@ -0,0 +1,258 @@
+/*
+ * 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 { IRouter, RouteConfig } from '../http';
+
+import { coreMock } from '../mocks';
+import { mockCoreContext } from '../core_context.mock';
+import { httpServiceMock } from '../http/http_service.mock';
+import { httpServerMock } from '../http/http_server.mocks';
+import { renderingMock } from '../rendering/rendering_service.mock';
+import { HttpResourcesService, SetupDeps } from './http_resources_service';
+import { httpResourcesMock } from './http_resources_service.mock';
+
+const coreContext = mockCoreContext.create();
+
+describe('HttpResources service', () => {
+ let service: HttpResourcesService;
+ let setupDeps: SetupDeps;
+ let router: jest.Mocked;
+ const kibanaRequest = httpServerMock.createKibanaRequest();
+ const context = { core: coreMock.createRequestHandlerContext() };
+ describe('#createRegistrar', () => {
+ beforeEach(() => {
+ setupDeps = {
+ http: httpServiceMock.createSetupContract(),
+ rendering: renderingMock.createSetupContract(),
+ };
+ service = new HttpResourcesService(coreContext);
+ router = httpServiceMock.createRouter();
+ });
+
+ describe('register', () => {
+ describe('renderCoreApp', () => {
+ it('formats successful response', async () => {
+ const routeConfig: RouteConfig = { path: '/', validate: false };
+ const { createRegistrar } = await service.setup(setupDeps);
+ const { register } = createRegistrar(router);
+ register(routeConfig, async (ctx, req, res) => {
+ return res.renderCoreApp();
+ });
+ const [[, routeHandler]] = router.get.mock.calls;
+
+ const responseFactory = httpResourcesMock.createResponseFactory();
+ await routeHandler(context, kibanaRequest, responseFactory);
+ expect(setupDeps.rendering.render).toHaveBeenCalledWith(
+ kibanaRequest,
+ context.core.uiSettings.client,
+ {
+ includeUserSettings: true,
+ }
+ );
+ });
+
+ it('can attach headers, except the CSP header', async () => {
+ const routeConfig: RouteConfig = { path: '/', validate: false };
+ const { createRegistrar } = await service.setup(setupDeps);
+ const { register } = createRegistrar(router);
+ register(routeConfig, async (ctx, req, res) => {
+ return res.renderCoreApp({
+ headers: {
+ 'content-security-policy': "script-src 'unsafe-eval'",
+ 'x-kibana': '42',
+ },
+ });
+ });
+
+ const [[, routeHandler]] = router.get.mock.calls;
+
+ const responseFactory = httpResourcesMock.createResponseFactory();
+ await routeHandler(context, kibanaRequest, responseFactory);
+
+ expect(responseFactory.ok).toHaveBeenCalledWith({
+ body: ' ',
+ headers: {
+ 'x-kibana': '42',
+ 'content-security-policy':
+ "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
+ },
+ });
+ });
+ });
+ describe('renderAnonymousCoreApp', () => {
+ it('formats successful response', async () => {
+ const routeConfig: RouteConfig = { path: '/', validate: false };
+ const { createRegistrar } = await service.setup(setupDeps);
+ const { register } = createRegistrar(router);
+ register(routeConfig, async (ctx, req, res) => {
+ return res.renderAnonymousCoreApp();
+ });
+ const [[, routeHandler]] = router.get.mock.calls;
+
+ const responseFactory = httpResourcesMock.createResponseFactory();
+ await routeHandler(context, kibanaRequest, responseFactory);
+ expect(setupDeps.rendering.render).toHaveBeenCalledWith(
+ kibanaRequest,
+ context.core.uiSettings.client,
+ {
+ includeUserSettings: false,
+ }
+ );
+ });
+
+ it('can attach headers, except the CSP header', async () => {
+ const routeConfig: RouteConfig = { path: '/', validate: false };
+ const { createRegistrar } = await service.setup(setupDeps);
+ const { register } = createRegistrar(router);
+ register(routeConfig, async (ctx, req, res) => {
+ return res.renderAnonymousCoreApp({
+ headers: {
+ 'content-security-policy': "script-src 'unsafe-eval'",
+ 'x-kibana': '42',
+ },
+ });
+ });
+
+ const [[, routeHandler]] = router.get.mock.calls;
+
+ const responseFactory = httpResourcesMock.createResponseFactory();
+ await routeHandler(context, kibanaRequest, responseFactory);
+
+ expect(responseFactory.ok).toHaveBeenCalledWith({
+ body: ' ',
+ headers: {
+ 'x-kibana': '42',
+ 'content-security-policy':
+ "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
+ },
+ });
+ });
+ });
+ describe('renderHtml', () => {
+ it('formats successful response', async () => {
+ const htmlBody = ' ';
+ const routeConfig: RouteConfig = { path: '/', validate: false };
+ const { createRegistrar } = await service.setup(setupDeps);
+ const { register } = createRegistrar(router);
+ register(routeConfig, async (ctx, req, res) => {
+ return res.renderHtml({ body: htmlBody });
+ });
+ const [[, routeHandler]] = router.get.mock.calls;
+
+ const responseFactory = httpResourcesMock.createResponseFactory();
+ await routeHandler(context, kibanaRequest, responseFactory);
+ expect(responseFactory.ok).toHaveBeenCalledWith({
+ body: htmlBody,
+ headers: {
+ 'content-type': 'text/html',
+ 'content-security-policy':
+ "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
+ },
+ });
+ });
+
+ it('can attach headers, except the CSP & "content-type" headers', async () => {
+ const htmlBody = ' ';
+ const routeConfig: RouteConfig = { path: '/', validate: false };
+ const { createRegistrar } = await service.setup(setupDeps);
+ const { register } = createRegistrar(router);
+ register(routeConfig, async (ctx, req, res) => {
+ return res.renderHtml({
+ body: htmlBody,
+ headers: {
+ 'content-type': 'text/html5',
+ 'content-security-policy': "script-src 'unsafe-eval'",
+ 'x-kibana': '42',
+ },
+ });
+ });
+
+ const [[, routeHandler]] = router.get.mock.calls;
+
+ const responseFactory = httpResourcesMock.createResponseFactory();
+ await routeHandler(context, kibanaRequest, responseFactory);
+
+ expect(responseFactory.ok).toHaveBeenCalledWith({
+ body: htmlBody,
+ headers: {
+ 'content-type': 'text/html',
+ 'x-kibana': '42',
+ 'content-security-policy':
+ "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
+ },
+ });
+ });
+ });
+ describe('renderJs', () => {
+ it('formats successful response', async () => {
+ const jsBody = 'alert(1);';
+ const routeConfig: RouteConfig = { path: '/', validate: false };
+ const { createRegistrar } = await service.setup(setupDeps);
+ const { register } = createRegistrar(router);
+ register(routeConfig, async (ctx, req, res) => {
+ return res.renderJs({ body: jsBody });
+ });
+ const [[, routeHandler]] = router.get.mock.calls;
+
+ const responseFactory = httpResourcesMock.createResponseFactory();
+ await routeHandler(context, kibanaRequest, responseFactory);
+ expect(responseFactory.ok).toHaveBeenCalledWith({
+ body: jsBody,
+ headers: {
+ 'content-type': 'text/javascript',
+ 'content-security-policy':
+ "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
+ },
+ });
+ });
+
+ it('can attach headers, except the CSP & "content-type" headers', async () => {
+ const jsBody = 'alert(1);';
+ const routeConfig: RouteConfig = { path: '/', validate: false };
+ const { createRegistrar } = await service.setup(setupDeps);
+ const { register } = createRegistrar(router);
+ register(routeConfig, async (ctx, req, res) => {
+ return res.renderJs({
+ body: jsBody,
+ headers: {
+ 'content-type': 'text/html',
+ 'content-security-policy': "script-src 'unsafe-eval'",
+ 'x-kibana': '42',
+ },
+ });
+ });
+
+ const [[, routeHandler]] = router.get.mock.calls;
+
+ const responseFactory = httpResourcesMock.createResponseFactory();
+ await routeHandler(context, kibanaRequest, responseFactory);
+
+ expect(responseFactory.ok).toHaveBeenCalledWith({
+ body: jsBody,
+ headers: {
+ 'content-type': 'text/javascript',
+ 'x-kibana': '42',
+ 'content-security-policy':
+ "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
+ },
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/src/core/server/http_resources/http_resources_service.ts b/src/core/server/http_resources/http_resources_service.ts
new file mode 100644
index 0000000000000..bc79ad68f4099
--- /dev/null
+++ b/src/core/server/http_resources/http_resources_service.ts
@@ -0,0 +1,130 @@
+/*
+ * 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 { RequestHandlerContext } from 'src/core/server';
+
+import { CoreContext } from '../core_context';
+import {
+ IRouter,
+ RouteConfig,
+ InternalHttpServiceSetup,
+ KibanaRequest,
+ KibanaResponseFactory,
+} from '../http';
+
+import { Logger } from '../logging';
+import { InternalRenderingServiceSetup } from '../rendering';
+import { CoreService } from '../../types';
+
+import {
+ InternalHttpResourcesSetup,
+ HttpResources,
+ HttpResourcesResponseOptions,
+ HttpResourcesRenderOptions,
+ HttpResourcesRequestHandler,
+ HttpResourcesServiceToolkit,
+} from './types';
+
+export interface SetupDeps {
+ http: InternalHttpServiceSetup;
+ rendering: InternalRenderingServiceSetup;
+}
+
+export class HttpResourcesService implements CoreService {
+ private readonly logger: Logger;
+ constructor(core: CoreContext) {
+ this.logger = core.logger.get('http-resources');
+ }
+
+ setup(deps: SetupDeps) {
+ this.logger.debug('setting up HttpResourcesService');
+ return {
+ createRegistrar: this.createRegistrar.bind(this, deps),
+ };
+ }
+
+ start() {}
+ stop() {}
+
+ private createRegistrar(deps: SetupDeps, router: IRouter): HttpResources {
+ return {
+ register: (
+ route: RouteConfig
,
+ handler: HttpResourcesRequestHandler
+ ) => {
+ return router.get
(route, (context, request, response) => {
+ return handler(context, request, {
+ ...response,
+ ...this.createResponseToolkit(deps, context, request, response),
+ });
+ });
+ },
+ };
+ }
+
+ private createResponseToolkit(
+ deps: SetupDeps,
+ context: RequestHandlerContext,
+ request: KibanaRequest,
+ response: KibanaResponseFactory
+ ): HttpResourcesServiceToolkit {
+ const cspHeader = deps.http.csp.header;
+ return {
+ async renderCoreApp(options: HttpResourcesRenderOptions = {}) {
+ const body = await deps.rendering.render(request, context.core.uiSettings.client, {
+ includeUserSettings: true,
+ });
+
+ return response.ok({
+ body,
+ headers: { ...options.headers, 'content-security-policy': cspHeader },
+ });
+ },
+ async renderAnonymousCoreApp(options: HttpResourcesRenderOptions = {}) {
+ const body = await deps.rendering.render(request, context.core.uiSettings.client, {
+ includeUserSettings: false,
+ });
+
+ return response.ok({
+ body,
+ headers: { ...options.headers, 'content-security-policy': cspHeader },
+ });
+ },
+ renderHtml(options: HttpResourcesResponseOptions) {
+ return response.ok({
+ body: options.body,
+ headers: {
+ ...options.headers,
+ 'content-type': 'text/html',
+ 'content-security-policy': cspHeader,
+ },
+ });
+ },
+ renderJs(options: HttpResourcesResponseOptions) {
+ return response.ok({
+ body: options.body,
+ headers: {
+ ...options.headers,
+ 'content-type': 'text/javascript',
+ 'content-security-policy': cspHeader,
+ },
+ });
+ },
+ };
+ }
+}
diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts b/src/core/server/http_resources/index.ts
similarity index 70%
rename from src/legacy/core_plugins/kibana/public/visualize/legacy.ts
rename to src/core/server/http_resources/index.ts
index 4ef2c93689714..b373c6a9efa89 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts
+++ b/src/core/server/http_resources/index.ts
@@ -17,12 +17,13 @@
* under the License.
*/
-import { PluginInitializerContext } from 'kibana/public';
-import { npSetup, npStart } from 'ui/new_platform';
-import { plugin } from './index';
+export { HttpResourcesService } from './http_resources_service';
-const instance = plugin({
- env: npSetup.plugins.kibanaLegacy.env,
-} as PluginInitializerContext);
-instance.setup(npSetup.core, npSetup.plugins);
-instance.start(npStart.core, npStart.plugins);
+export {
+ HttpResourcesRenderOptions,
+ HttpResourcesResponseOptions,
+ HttpResourcesServiceToolkit,
+ HttpResourcesRequestHandler,
+ HttpResources,
+ InternalHttpResourcesSetup,
+} from './types';
diff --git a/src/core/server/http_resources/integration_tests/http_resources_service.test.ts b/src/core/server/http_resources/integration_tests/http_resources_service.test.ts
new file mode 100644
index 0000000000000..0a5daa02e17e9
--- /dev/null
+++ b/src/core/server/http_resources/integration_tests/http_resources_service.test.ts
@@ -0,0 +1,203 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+import * as kbnTestServer from '../../../../test_utils/kbn_server';
+
+describe('http resources service', () => {
+ describe('register', () => {
+ let root: ReturnType;
+ const defaultCspRules = "script-src 'self'";
+ beforeEach(async () => {
+ root = kbnTestServer.createRoot({
+ csp: {
+ rules: [defaultCspRules],
+ },
+ });
+ }, 30000);
+
+ afterEach(async () => {
+ await root.shutdown();
+ });
+
+ describe('renderAnonymousCoreApp', () => {
+ it('renders core application', async () => {
+ const { http, httpResources } = await root.setup();
+
+ const router = http.createRouter('');
+ const resources = httpResources.createRegistrar(router);
+ resources.register({ path: '/render-core', validate: false }, (context, req, res) =>
+ res.renderAnonymousCoreApp()
+ );
+
+ await root.start();
+ const response = await kbnTestServer.request.get(root, '/render-core').expect(200);
+
+ expect(response.text.length).toBeGreaterThan(0);
+ });
+
+ it('attaches CSP header', async () => {
+ const { http, httpResources } = await root.setup();
+
+ const router = http.createRouter('');
+ const resources = httpResources.createRegistrar(router);
+ resources.register({ path: '/render-core', validate: false }, (context, req, res) =>
+ res.renderAnonymousCoreApp()
+ );
+
+ await root.start();
+ const response = await kbnTestServer.request.get(root, '/render-core').expect(200);
+
+ expect(response.header['content-security-policy']).toBe(defaultCspRules);
+ });
+
+ it('can attach headers, except the CSP header', async () => {
+ const { http, httpResources } = await root.setup();
+
+ const router = http.createRouter('');
+ const resources = httpResources.createRegistrar(router);
+ resources.register({ path: '/render-core', validate: false }, (context, req, res) =>
+ res.renderAnonymousCoreApp({
+ headers: {
+ 'content-security-policy': "script-src 'unsafe-eval'",
+ 'x-kibana': '42',
+ },
+ })
+ );
+
+ await root.start();
+ const response = await kbnTestServer.request.get(root, '/render-core').expect(200);
+
+ expect(response.header['content-security-policy']).toBe(defaultCspRules);
+ expect(response.header['x-kibana']).toBe('42');
+ });
+ });
+
+ describe('custom renders', () => {
+ it('renders html', async () => {
+ const { http, httpResources } = await root.setup();
+
+ const router = http.createRouter('');
+ const resources = httpResources.createRegistrar(router);
+ const htmlBody = `
+
+
+
+ HTML body
+
+
+ `;
+ resources.register({ path: '/render-html', validate: false }, (context, req, res) =>
+ res.renderHtml({ body: htmlBody })
+ );
+
+ await root.start();
+ const response = await kbnTestServer.request.get(root, '/render-html').expect(200);
+
+ expect(response.text).toBe(htmlBody);
+ expect(response.header['content-type']).toBe('text/html; charset=utf-8');
+ });
+
+ it('renders javascript', async () => {
+ const { http, httpResources } = await root.setup();
+
+ const router = http.createRouter('');
+ const resources = httpResources.createRegistrar(router);
+ const jsBody = 'window.alert("from js body");';
+ resources.register({ path: '/render-js', validate: false }, (context, req, res) =>
+ res.renderJs({ body: jsBody })
+ );
+
+ await root.start();
+ const response = await kbnTestServer.request.get(root, '/render-js').expect(200);
+
+ expect(response.text).toBe(jsBody);
+ expect(response.header['content-type']).toBe('text/javascript; charset=utf-8');
+ });
+
+ it('attaches CSP header', async () => {
+ const { http, httpResources } = await root.setup();
+
+ const router = http.createRouter('');
+ const resources = httpResources.createRegistrar(router);
+ const htmlBody = `
+
+
+
+ HTML body
+
+
+ `;
+ resources.register({ path: '/render-html', validate: false }, (context, req, res) =>
+ res.renderHtml({ body: htmlBody })
+ );
+
+ await root.start();
+ const response = await kbnTestServer.request.get(root, '/render-html').expect(200);
+
+ expect(response.header['content-security-policy']).toBe(defaultCspRules);
+ });
+
+ it('can attach headers, except the CSP & "content-type" headers', async () => {
+ const { http, httpResources } = await root.setup();
+
+ const router = http.createRouter('');
+ const resources = httpResources.createRegistrar(router);
+ resources.register({ path: '/render-core', validate: false }, (context, req, res) =>
+ res.renderHtml({
+ body: 'Hi
',
+ headers: {
+ 'content-security-policy': "script-src 'unsafe-eval'",
+ 'content-type': 'text/html',
+ 'x-kibana': '42',
+ },
+ })
+ );
+
+ await root.start();
+ const response = await kbnTestServer.request.get(root, '/render-core').expect(200);
+
+ expect(response.header['content-security-policy']).toBe(defaultCspRules);
+ expect(response.header['x-kibana']).toBe('42');
+ });
+
+ it('can adjust route config', async () => {
+ const { http, httpResources } = await root.setup();
+
+ const router = http.createRouter('');
+ const resources = httpResources.createRegistrar(router);
+ const validate = {
+ params: schema.object({
+ id: schema.string(),
+ }),
+ };
+
+ resources.register({ path: '/render-js-with-param/{id}', validate }, (context, req, res) =>
+ res.renderJs({ body: `window.alert(${req.params.id});` })
+ );
+
+ await root.start();
+ const response = await kbnTestServer.request
+ .get(root, '/render-js-with-param/42')
+ .expect(200);
+
+ expect(response.text).toBe('window.alert(42);');
+ });
+ });
+ });
+});
diff --git a/src/core/server/http_resources/types.ts b/src/core/server/http_resources/types.ts
new file mode 100644
index 0000000000000..d761e2def1023
--- /dev/null
+++ b/src/core/server/http_resources/types.ts
@@ -0,0 +1,116 @@
+/*
+ * 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 {
+ IRouter,
+ RouteConfig,
+ IKibanaResponse,
+ ResponseHeaders,
+ HttpResponseOptions,
+ KibanaResponseFactory,
+ RequestHandler,
+} from '../http';
+
+/**
+ * Allows to configure HTTP response parameters
+ * @public
+ */
+export interface HttpResourcesRenderOptions {
+ /**
+ * HTTP Headers with additional information about response.
+ * @remarks
+ * All HTML pages are already pre-configured with `content-security-policy` header that cannot be overridden.
+ * */
+ headers?: ResponseHeaders;
+}
+
+/**
+ * HTTP Resources response parameters
+ * @public
+ */
+export type HttpResourcesResponseOptions = HttpResponseOptions;
+
+/**
+ * Extended set of {@link KibanaResponseFactory} helpers used to respond with HTML or JS resource.
+ * @public
+ */
+export interface HttpResourcesServiceToolkit {
+ /** To respond with HTML page bootstrapping Kibana application. */
+ renderCoreApp: (options?: HttpResourcesRenderOptions) => Promise;
+ /** To respond with HTML page bootstrapping Kibana application without retrieving user-specific information. */
+ renderAnonymousCoreApp: (options?: HttpResourcesRenderOptions) => Promise;
+ /** To respond with a custom HTML page. */
+ renderHtml: (options: HttpResourcesResponseOptions) => IKibanaResponse;
+ /** To respond with a custom JS script file. */
+ renderJs: (options: HttpResourcesResponseOptions) => IKibanaResponse;
+}
+
+/**
+ * Extended version of {@link RequestHandler} having access to {@link HttpResourcesServiceToolkit}
+ * to respond with HTML or JS resources.
+ * @param context {@link RequestHandlerContext} - the core context exposed for this request.
+ * @param request {@link KibanaRequest} - object containing information about requested resource,
+ * such as path, method, headers, parameters, query, body, etc.
+ * @param response {@link KibanaResponseFactory} {@libk HttpResourcesServiceToolkit} - a set of helper functions used to respond to a request.
+ *
+ * @example
+ * ```typescript
+ * httpResources.register({
+ * path: '/login',
+ * validate: {
+ * params: schema.object({ id: schema.string() }),
+ * },
+ * },
+ * async (context, request, response) => {
+ * //..
+ * return response.renderCoreApp();
+ * });
+ * @public
+ */
+export type HttpResourcesRequestHandler = RequestHandler<
+ P,
+ Q,
+ B,
+ 'get',
+ KibanaResponseFactory & HttpResourcesServiceToolkit
+>;
+
+/**
+ * Allows to configure HTTP response parameters
+ * @internal
+ */
+export interface InternalHttpResourcesSetup {
+ createRegistrar(router: IRouter): HttpResources;
+}
+
+/**
+ * HttpResources service is responsible for serving static & dynamic assets for Kibana application via HTTP.
+ * Provides API allowing plug-ins to respond with:
+ * - a pre-configured HTML page bootstrapping Kibana client app
+ * - custom HTML page
+ * - custom JS script file.
+ * @public
+ */
+export interface HttpResources {
+ /** To register a route handler executing passed function to form response. */
+ register:
(
+ route: RouteConfig
,
+ handler: HttpResourcesRequestHandler
+ ) => void;
+}
diff --git a/src/core/server/index.ts b/src/core/server/index.ts
index 039988fa08968..ef57fae159d7e 100644
--- a/src/core/server/index.ts
+++ b/src/core/server/index.ts
@@ -47,7 +47,8 @@ import {
} from './elasticsearch';
import { HttpServiceSetup } from './http';
-import { IScopedRenderingClient } from './rendering';
+import { HttpResources } from './http_resources';
+
import { PluginsServiceSetup, PluginsServiceStart, PluginOpaqueId } from './plugins';
import { ContextSetup } from './context';
import { IUiSettingsClient, UiSettingsServiceSetup, UiSettingsServiceStart } from './ui_settings';
@@ -146,6 +147,7 @@ export {
OnPreResponseInfo,
RedirectResponseOptions,
RequestHandler,
+ RequestHandlerWrapper,
RequestHandlerContextContainer,
RequestHandlerContextProvider,
ResponseError,
@@ -175,7 +177,15 @@ export {
DestructiveRouteMethod,
SafeRouteMethod,
} from './http';
-export { RenderingServiceSetup, IRenderOptions } from './rendering';
+
+export {
+ HttpResourcesRenderOptions,
+ HttpResourcesResponseOptions,
+ HttpResourcesServiceToolkit,
+ HttpResourcesRequestHandler,
+} from './http_resources';
+
+export { IRenderOptions } from './rendering';
export { Logger, LoggerFactory, LogMeta, LogRecord, LogLevel } from './logging';
export {
@@ -313,8 +323,6 @@ export {
* Plugin specific context passed to a route handler.
*
* Provides the following clients and services:
- * - {@link IScopedRenderingClient | rendering} - Rendering client
- * which uses the data of the incoming request
* - {@link SavedObjectsClient | savedObjects.client} - Saved Objects client
* which uses the credentials of the incoming request
* - {@link ISavedObjectTypeRegistry | savedObjects.typeRegistry} - Type registry containing
@@ -330,7 +338,6 @@ export {
*/
export interface RequestHandlerContext {
core: {
- rendering: IScopedRenderingClient;
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
@@ -362,7 +369,10 @@ export interface CoreSetup {
getAuthHeaders: () => undefined,
} as any,
},
+ httpResources: httpResourcesMock.createSetupContract(),
savedObjects: savedObjectsServiceMock.createInternalSetupContract(),
plugins: {
initialized: true,
contracts: new Map([['plugin-id', 'plugin-value']]),
- uiPlugins: {
- public: new Map([['plugin-id', {} as DiscoveredPlugin]]),
- internal: new Map([
- [
- 'plugin-id',
- {
- publicTargetDir: 'path/to/target/public',
- publicAssetsDir: '/plugins/name/assets/',
- },
- ],
- ]),
- browserConfigs: new Map(),
- },
},
rendering: renderingServiceMock,
metrics: metricsServiceMock.createInternalSetupContract(),
@@ -110,6 +99,19 @@ beforeEach(() => {
status: statusServiceMock.createInternalSetupContract(),
},
plugins: { 'plugin-id': 'plugin-value' },
+ uiPlugins: {
+ public: new Map([['plugin-id', {} as DiscoveredPlugin]]),
+ internal: new Map([
+ [
+ 'plugin-id',
+ {
+ publicTargetDir: 'path/to/target/public',
+ publicAssetsDir: '/plugins/name/assets/',
+ },
+ ],
+ ]),
+ browserConfigs: new Map(),
+ },
};
startDeps = {
diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts
index f77230301ce02..fd3ebfe120599 100644
--- a/src/core/server/legacy/legacy_service.ts
+++ b/src/core/server/legacy/legacy_service.ts
@@ -269,6 +269,7 @@ export class LegacyService implements CoreService {
uiSettings: { asScopedToClient: startDeps.core.uiSettings.asScopedToClient },
};
+ const router = setupDeps.core.http.createRouter('', this.legacyId);
const coreSetup: CoreSetup = {
capabilities: setupDeps.core.capabilities,
context: setupDeps.core.context,
@@ -283,7 +284,8 @@ export class LegacyService implements CoreService {
null,
this.legacyId
),
- createRouter: () => setupDeps.core.http.createRouter('', this.legacyId),
+ createRouter: () => router,
+ resources: setupDeps.core.httpResources.createRegistrar(router),
registerOnPreAuth: setupDeps.core.http.registerOnPreAuth,
registerAuth: setupDeps.core.http.registerAuth,
registerOnPostAuth: setupDeps.core.http.registerOnPostAuth,
@@ -342,7 +344,7 @@ export class LegacyService implements CoreService {
},
hapiServer: setupDeps.core.http.server,
kibanaMigrator: startDeps.core.savedObjects.migrator,
- uiPlugins: setupDeps.core.plugins.uiPlugins,
+ uiPlugins: setupDeps.uiPlugins,
elasticsearch: setupDeps.core.elasticsearch,
rendering: setupDeps.core.rendering,
uiSettings: setupDeps.core.uiSettings,
diff --git a/src/core/server/legacy/types.ts b/src/core/server/legacy/types.ts
index 0c1a7730f92a7..38cb9e6835ce9 100644
--- a/src/core/server/legacy/types.ts
+++ b/src/core/server/legacy/types.ts
@@ -22,8 +22,8 @@ import { Server } from 'hapi';
import { ChromeNavLink } from '../../public';
import { KibanaRequest, LegacyRequest } from '../http';
import { InternalCoreSetup, InternalCoreStart } from '../internal_types';
-import { PluginsServiceSetup, PluginsServiceStart } from '../plugins';
-import { RenderingServiceSetup } from '../rendering';
+import { PluginsServiceSetup, PluginsServiceStart, UiPlugins } from '../plugins';
+import { InternalRenderingServiceSetup } from '../rendering';
import { SavedObjectsLegacyUiExports } from '../types';
/**
@@ -34,7 +34,7 @@ export type LegacyVars = Record;
type LegacyCoreSetup = InternalCoreSetup & {
plugins: PluginsServiceSetup;
- rendering: RenderingServiceSetup;
+ rendering: InternalRenderingServiceSetup;
};
type LegacyCoreStart = InternalCoreStart & { plugins: PluginsServiceStart };
@@ -173,6 +173,7 @@ export type LegacyUiExports = SavedObjectsLegacyUiExports & {
export interface LegacyServiceSetupDeps {
core: LegacyCoreSetup;
plugins: Record;
+ uiPlugins: UiPlugins;
}
/**
diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts
index faf73044cac4d..3b9a39db72278 100644
--- a/src/core/server/mocks.ts
+++ b/src/core/server/mocks.ts
@@ -23,10 +23,12 @@ import { CspConfig } from './csp';
import { loggingServiceMock } from './logging/logging_service.mock';
import { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock';
import { httpServiceMock } from './http/http_service.mock';
+import { httpResourcesMock } from './http_resources/http_resources_service.mock';
import { contextServiceMock } from './context/context_service.mock';
import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock';
import { savedObjectsClientMock } from './saved_objects/service/saved_objects_client.mock';
import { typeRegistryMock as savedObjectsTypeRegistryMock } from './saved_objects/saved_objects_type_registry.mock';
+import { renderingMock } from './rendering/rendering_service.mock';
import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
import { SharedGlobalConfig } from './plugins';
import { InternalCoreSetup, InternalCoreStart } from './internal_types';
@@ -36,6 +38,7 @@ import { uuidServiceMock } from './uuid/uuid_service.mock';
import { statusServiceMock } from './status/status_service.mock';
export { httpServerMock } from './http/http_server.mocks';
+export { httpResourcesMock } from './http_resources/http_resources_service.mock';
export { sessionStorageMock } from './http/cookie_session_storage.mocks';
export { configServiceMock } from './config/config_service.mock';
export { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock';
@@ -45,6 +48,7 @@ export { savedObjectsRepositoryMock } from './saved_objects/service/lib/reposito
export { typeRegistryMock as savedObjectsTypeRegistryMock } from './saved_objects/saved_objects_type_registry.mock';
export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
export { metricsServiceMock } from './metrics/metrics_service.mock';
+export { renderingMock } from './rendering/rendering_service.mock';
export function pluginInitializerContextConfigMock(config: T) {
const globalConfig: SharedGlobalConfig = {
@@ -120,6 +124,7 @@ function createCoreSetupMock({
get: httpService.auth.get,
isAuthenticated: httpService.auth.isAuthenticated,
},
+ resources: httpResourcesMock.createRegistrar(),
getServerInfo: httpService.getServerInfo,
};
httpMock.createRouter.mockImplementation(() => httpService.createRouter(''));
@@ -167,6 +172,8 @@ function createInternalCoreSetupMock() {
savedObjects: savedObjectsServiceMock.createInternalSetupContract(),
status: statusServiceMock.createInternalSetupContract(),
uuid: uuidServiceMock.createSetupContract(),
+ httpResources: httpResourcesMock.createSetupContract(),
+ rendering: renderingMock.createSetupContract(),
uiSettings: uiSettingsServiceMock.createSetupContract(),
};
return setupDeps;
@@ -184,9 +191,6 @@ function createInternalCoreStartMock() {
function createCoreRequestHandlerContextMock() {
return {
- rendering: {
- render: jest.fn(),
- },
savedObjects: {
client: savedObjectsClientMock.create(),
typeRegistry: savedObjectsTypeRegistryMock.create(),
diff --git a/src/core/server/plugins/index.ts b/src/core/server/plugins/index.ts
index c7ef213c8f187..e480de750bb1a 100644
--- a/src/core/server/plugins/index.ts
+++ b/src/core/server/plugins/index.ts
@@ -17,7 +17,12 @@
* under the License.
*/
-export { PluginsService, PluginsServiceSetup, PluginsServiceStart } from './plugins_service';
+export {
+ PluginsService,
+ PluginsServiceSetup,
+ PluginsServiceStart,
+ UiPlugins,
+} from './plugins_service';
export { config } from './plugins_config';
/** @internal */
export { isNewPlatformPlugin } from './discovery';
diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts
index 61d97aea97459..ab18a9cbbc062 100644
--- a/src/core/server/plugins/plugin_context.ts
+++ b/src/core/server/plugins/plugin_context.ts
@@ -136,6 +136,8 @@ export function createPluginSetupContext(
deps: PluginsServiceSetupDeps,
plugin: PluginWrapper
): CoreSetup {
+ const router = deps.http.createRouter('', plugin.opaqueId);
+
return {
capabilities: {
registerProvider: deps.capabilities.registerProvider,
@@ -155,7 +157,8 @@ export function createPluginSetupContext(
null,
plugin.opaqueId
),
- createRouter: () => deps.http.createRouter('', plugin.opaqueId),
+ createRouter: () => router,
+ resources: deps.httpResources.createRegistrar(router),
registerOnPreAuth: deps.http.registerOnPreAuth,
registerAuth: deps.http.registerAuth,
registerOnPostAuth: deps.http.registerOnPostAuth,
diff --git a/src/core/server/plugins/plugins_service.mock.ts b/src/core/server/plugins/plugins_service.mock.ts
index 29e5b83b2e4c7..a40566767ddae 100644
--- a/src/core/server/plugins/plugins_service.mock.ts
+++ b/src/core/server/plugins/plugins_service.mock.ts
@@ -23,14 +23,10 @@ type PluginsServiceMock = jest.Mocked>;
const createSetupContractMock = (): PluginsServiceSetup => ({
contracts: new Map(),
- uiPlugins: {
- browserConfigs: new Map(),
- internal: new Map(),
- public: new Map(),
- },
initialized: true,
});
const createStartContractMock = () => ({ contracts: new Map() });
+
const createServiceMock = (): PluginsServiceMock => ({
discover: jest.fn(),
setup: jest.fn().mockResolvedValue(createSetupContractMock()),
@@ -38,8 +34,17 @@ const createServiceMock = (): PluginsServiceMock => ({
stop: jest.fn(),
});
+function createUiPlugins() {
+ return {
+ browserConfigs: new Map(),
+ internal: new Map(),
+ public: new Map(),
+ };
+}
+
export const pluginServiceMock = {
create: createServiceMock,
createSetupContract: createSetupContractMock,
createStartContract: createStartContractMock,
+ createUiPlugins,
};
diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts
index 14147ab9f2a8d..38fda12bd290f 100644
--- a/src/core/server/plugins/plugins_service.test.ts
+++ b/src/core/server/plugins/plugins_service.test.ts
@@ -120,6 +120,7 @@ describe('PluginsService', () => {
pluginsService = new PluginsService({ coreId, env, logger, configService });
[mockPluginSystem] = MockPluginsSystem.mock.instances as any;
+ mockPluginSystem.uiPlugins.mockReturnValue(new Map());
});
afterEach(() => {
@@ -202,7 +203,6 @@ describe('PluginsService', () => {
.mockImplementation(path => Promise.resolve(!path.includes('disabled')));
mockPluginSystem.setupPlugins.mockResolvedValue(new Map());
- mockPluginSystem.uiPlugins.mockReturnValue(new Map());
mockDiscover.mockReturnValue({
error$: from([]),
@@ -234,8 +234,6 @@ describe('PluginsService', () => {
const setup = await pluginsService.setup(setupDeps);
expect(setup.contracts).toBeInstanceOf(Map);
- expect(setup.uiPlugins.public).toBeInstanceOf(Map);
- expect(setup.uiPlugins.internal).toBeInstanceOf(Map);
expect(mockPluginSystem.addPlugin).not.toHaveBeenCalled();
expect(mockPluginSystem.setupPlugins).toHaveBeenCalledTimes(1);
expect(mockPluginSystem.setupPlugins).toHaveBeenCalledWith(setupDeps);
@@ -273,7 +271,8 @@ describe('PluginsService', () => {
plugin$: from([firstPlugin, secondPlugin]),
});
- await expect(pluginsService.discover()).resolves.toBeUndefined();
+ const { pluginTree } = await pluginsService.discover();
+ expect(pluginTree).toBeUndefined();
expect(mockDiscover).toHaveBeenCalledTimes(1);
expect(mockPluginSystem.addPlugin).toHaveBeenCalledTimes(2);
@@ -308,7 +307,8 @@ describe('PluginsService', () => {
plugin$: from([firstPlugin, secondPlugin, thirdPlugin, lastPlugin, missingDepsPlugin]),
});
- await expect(pluginsService.discover()).resolves.toBeUndefined();
+ const { pluginTree } = await pluginsService.discover();
+ expect(pluginTree).toBeUndefined();
expect(mockDiscover).toHaveBeenCalledTimes(1);
expect(mockPluginSystem.addPlugin).toHaveBeenCalledTimes(4);
@@ -466,12 +466,8 @@ describe('PluginsService', () => {
});
mockPluginSystem.uiPlugins.mockReturnValue(new Map([pluginToDiscoveredEntry(plugin)]));
- await pluginsService.discover();
- const {
- uiPlugins: { browserConfigs },
- } = await pluginsService.setup(setupDeps);
-
- const uiConfig$ = browserConfigs.get('plugin-with-expose');
+ const { uiPlugins } = await pluginsService.discover();
+ const uiConfig$ = uiPlugins.browserConfigs.get('plugin-with-expose');
expect(uiConfig$).toBeDefined();
const uiConfig = await uiConfig$!.pipe(take(1)).toPromise();
@@ -506,12 +502,8 @@ describe('PluginsService', () => {
});
mockPluginSystem.uiPlugins.mockReturnValue(new Map([pluginToDiscoveredEntry(plugin)]));
- await pluginsService.discover();
- const {
- uiPlugins: { browserConfigs },
- } = await pluginsService.setup(setupDeps);
-
- expect([...browserConfigs.entries()]).toHaveLength(0);
+ const { uiPlugins } = await pluginsService.discover();
+ expect([...uiPlugins.browserConfigs.entries()]).toHaveLength(0);
});
});
@@ -539,8 +531,7 @@ describe('PluginsService', () => {
describe('uiPlugins.internal', () => {
it('includes disabled plugins', async () => {
config$.next({ plugins: { initialize: true }, plugin1: { enabled: false } });
- await pluginsService.discover();
- const { uiPlugins } = await pluginsService.setup(setupDeps);
+ const { uiPlugins } = await pluginsService.discover();
expect(uiPlugins.internal).toMatchInlineSnapshot(`
Map {
"plugin-1" => Object {
diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts
index a0ecee47c675f..d7a348affe94f 100644
--- a/src/core/server/plugins/plugins_service.ts
+++ b/src/core/server/plugins/plugins_service.ts
@@ -39,23 +39,25 @@ export interface PluginsServiceSetup {
initialized: boolean;
/** Setup contracts returned by plugins. */
contracts: Map;
- uiPlugins: {
- /**
- * Paths to all discovered ui plugin entrypoints on the filesystem, even if
- * disabled.
- */
- internal: Map;
-
- /**
- * Information needed by client-side to load plugins and wire dependencies.
- */
- public: Map;
-
- /**
- * Configuration for plugins to be exposed to the client-side.
- */
- browserConfigs: Map>;
- };
+}
+
+/** @internal */
+export interface UiPlugins {
+ /**
+ * Paths to all discovered ui plugin entrypoints on the filesystem, even if
+ * disabled.
+ */
+ internal: Map;
+
+ /**
+ * Information needed by client-side to load plugins and wire dependencies.
+ */
+ public: Map;
+
+ /**
+ * Configuration for plugins to be exposed to the client-side.
+ */
+ browserConfigs: Map>;
}
/** @internal */
@@ -97,8 +99,17 @@ export class PluginsService implements CoreService;
-export const setupMock: jest.Mocked = {
+export const setupMock: jest.Mocked = {
render: jest.fn(),
};
export const mockSetup = jest.fn().mockResolvedValue(setupMock);
diff --git a/src/core/server/rendering/rendering_service.mock.ts b/src/core/server/rendering/rendering_service.mock.ts
new file mode 100644
index 0000000000000..7eba332512386
--- /dev/null
+++ b/src/core/server/rendering/rendering_service.mock.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 { InternalRenderingServiceSetup } from './types';
+
+function createRenderingSetup() {
+ const mocked: jest.Mocked = {
+ render: jest.fn().mockResolvedValue(' '),
+ };
+ return mocked;
+}
+
+export const renderingMock = {
+ createSetupContract: createRenderingSetup,
+};
diff --git a/src/core/server/rendering/rendering_service.test.ts b/src/core/server/rendering/rendering_service.test.ts
index 43ff4f633085c..d1c527aca4dba 100644
--- a/src/core/server/rendering/rendering_service.test.ts
+++ b/src/core/server/rendering/rendering_service.test.ts
@@ -22,7 +22,7 @@ import { load } from 'cheerio';
import { httpServerMock } from '../http/http_server.mocks';
import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock';
import { mockRenderingServiceParams, mockRenderingSetupDeps } from './__mocks__/params';
-import { RenderingServiceSetup } from './types';
+import { InternalRenderingServiceSetup } from './types';
import { RenderingService } from './rendering_service';
const INJECTED_METADATA = {
@@ -62,15 +62,9 @@ describe('RenderingService', () => {
});
describe('setup()', () => {
- it('creates instance of RenderingServiceSetup', async () => {
- const rendering = await service.setup(mockRenderingSetupDeps);
-
- expect(rendering.render).toBeInstanceOf(Function);
- });
-
describe('render()', () => {
let uiSettings: ReturnType;
- let render: RenderingServiceSetup['render'];
+ let render: InternalRenderingServiceSetup['render'];
beforeEach(async () => {
uiSettings = uiSettingsServiceMock.createClient();
@@ -78,6 +72,13 @@ describe('RenderingService', () => {
registered: { name: 'title' },
});
render = (await service.setup(mockRenderingSetupDeps)).render;
+ await service.start({
+ legacy: {
+ legacyInternals: {
+ getVars: () => ({}),
+ },
+ },
+ } as any);
});
it('renders "core" page', async () => {
diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx
index dbafd5806bd74..a02d85d22b2cb 100644
--- a/src/core/server/rendering/rendering_service.tsx
+++ b/src/core/server/rendering/rendering_service.tsx
@@ -23,41 +23,37 @@ import { take } from 'rxjs/operators';
import { i18n } from '@kbn/i18n';
+import { UiPlugins } from '../plugins';
import { CoreService } from '../../types';
import { CoreContext } from '../core_context';
import { Template } from './views';
+import { LegacyService } from '../legacy';
import {
IRenderOptions,
RenderingSetupDeps,
- RenderingServiceSetup,
+ InternalRenderingServiceSetup,
RenderingMetadata,
} from './types';
/** @internal */
-export class RenderingService implements CoreService {
+export class RenderingService implements CoreService {
+ private legacyInternals?: LegacyService['legacyInternals'];
constructor(private readonly coreContext: CoreContext) {}
public async setup({
http,
legacyPlugins,
- plugins,
- }: RenderingSetupDeps): Promise {
- async function getUiConfig(pluginId: string) {
- const browserConfig = plugins.uiPlugins.browserConfigs.get(pluginId);
-
- return ((await browserConfig?.pipe(take(1)).toPromise()) ?? {}) as Record;
- }
-
+ uiPlugins,
+ }: RenderingSetupDeps): Promise {
return {
render: async (
request,
uiSettings,
- {
- app = { getId: () => 'core' },
- includeUserSettings = true,
- vars = {},
- }: IRenderOptions = {}
+ { app = { getId: () => 'core' }, includeUserSettings = true, vars }: IRenderOptions = {}
) => {
+ if (!this.legacyInternals) {
+ throw new Error('Cannot render before "start"');
+ }
const { env } = this.coreContext;
const basePath = http.basePath.get(request);
const serverBasePath = http.basePath.serverBasePath;
@@ -87,12 +83,12 @@ export class RenderingService implements CoreService {
translationsUrl: `${basePath}/translations/${i18n.getLocale()}.json`,
},
csp: { warnLegacyBrowsers: http.csp.warnLegacyBrowsers },
- vars,
+ vars: vars ?? (await this.legacyInternals!.getVars('core', request)),
uiPlugins: await Promise.all(
- [...plugins.uiPlugins.public].map(async ([id, plugin]) => ({
+ [...uiPlugins.public].map(async ([id, plugin]) => ({
id,
plugin,
- config: await getUiConfig(id),
+ config: await this.getUiConfig(uiPlugins, id),
}))
),
legacyMetadata: {
@@ -116,7 +112,15 @@ export class RenderingService implements CoreService {
};
}
- public async start() {}
+ public async start({ legacy }: { legacy: LegacyService }) {
+ this.legacyInternals = legacy.legacyInternals;
+ }
public async stop() {}
+
+ private async getUiConfig(uiPlugins: UiPlugins, pluginId: string) {
+ const browserConfig = uiPlugins.browserConfigs.get(pluginId);
+
+ return ((await browserConfig?.pipe(take(1)).toPromise()) ?? {}) as Record;
+ }
}
diff --git a/src/core/server/rendering/types.ts b/src/core/server/rendering/types.ts
index cfaa23d491139..2a3be93055006 100644
--- a/src/core/server/rendering/types.ts
+++ b/src/core/server/rendering/types.ts
@@ -23,7 +23,7 @@ import { Env } from '../config';
import { ICspConfig } from '../csp';
import { InternalHttpServiceSetup, KibanaRequest, LegacyRequest } from '../http';
import { LegacyNavLink, LegacyServiceDiscoverPlugins } from '../legacy';
-import { PluginsServiceSetup, DiscoveredPlugin } from '../plugins';
+import { UiPlugins, DiscoveredPlugin } from '../plugins';
import { IUiSettingsClient, UserProvidedValues } from '../ui_settings';
/** @internal */
@@ -75,7 +75,7 @@ export interface RenderingMetadata {
export interface RenderingSetupDeps {
http: InternalHttpServiceSetup;
legacyPlugins: LegacyServiceDiscoverPlugins;
- plugins: PluginsServiceSetup;
+ uiPlugins: UiPlugins;
}
/** @public */
@@ -102,31 +102,8 @@ export interface IRenderOptions {
vars?: Record;
}
-/** @public */
-export interface IScopedRenderingClient {
- /**
- * Generate a `KibanaResponse` which renders an HTML page bootstrapped
- * with the `core` bundle. Intended as a response body for HTTP route handlers.
- *
- * @example
- * ```ts
- * router.get(
- * { path: '/', validate: false },
- * (context, request, response) =>
- * response.ok({
- * body: await context.core.rendering.render(),
- * headers: {
- * 'content-security-policy': context.core.http.csp.header,
- * },
- * })
- * );
- * ```
- */
- render(options?: Pick): Promise;
-}
-
/** @internal */
-export interface RenderingServiceSetup {
+export interface InternalRenderingServiceSetup {
/**
* Generate a `KibanaResponse` which renders an HTML page bootstrapped
* with the `core` bundle or the ID of another specified legacy bundle.
diff --git a/src/core/server/saved_objects/migrations/core/build_index_map.test.ts b/src/core/server/saved_objects/migrations/core/build_index_map.test.ts
index 44add4e977006..2c710d4eaa079 100644
--- a/src/core/server/saved_objects/migrations/core/build_index_map.test.ts
+++ b/src/core/server/saved_objects/migrations/core/build_index_map.test.ts
@@ -26,7 +26,7 @@ const createRegistry = (...types: Array>) => {
types.forEach(type =>
registry.registerType({
name: 'unknown',
- namespaceAgnostic: false,
+ namespaceType: 'single',
hidden: false,
mappings: { properties: {} },
migrations: {},
@@ -41,7 +41,7 @@ test('mappings without index pattern goes to default index', () => {
kibanaIndexName: '.kibana',
registry: createRegistry({
name: 'type1',
- namespaceAgnostic: false,
+ namespaceType: 'single',
}),
indexMap: {
type1: {
@@ -73,7 +73,7 @@ test(`mappings with custom index pattern doesn't go to default index`, () => {
kibanaIndexName: '.kibana',
registry: createRegistry({
name: 'type1',
- namespaceAgnostic: false,
+ namespaceType: 'single',
indexPattern: '.other_kibana',
}),
indexMap: {
@@ -106,7 +106,7 @@ test('creating a script gets added to the index pattern', () => {
kibanaIndexName: '.kibana',
registry: createRegistry({
name: 'type1',
- namespaceAgnostic: false,
+ namespaceType: 'single',
indexPattern: '.other_kibana',
convertToAliasScript: `ctx._id = ctx._source.type + ':' + ctx._id`,
}),
@@ -141,12 +141,12 @@ test('throws when two scripts are defined for an index pattern', () => {
const registry = createRegistry(
{
name: 'type1',
- namespaceAgnostic: false,
+ namespaceType: 'single',
convertToAliasScript: `ctx._id = ctx._source.type + ':' + ctx._id`,
},
{
name: 'type2',
- namespaceAgnostic: false,
+ namespaceType: 'single',
convertToAliasScript: `ctx._id = ctx._source.type + ':' + ctx._id`,
}
);
diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts
index ef3f546b5e574..64270c677ff20 100644
--- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts
+++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts
@@ -32,7 +32,7 @@ const createRegistry = (...types: Array>) => {
types.forEach(type =>
registry.registerType({
name: 'unknown',
- namespaceAgnostic: false,
+ namespaceType: 'single',
hidden: false,
mappings: { properties: {} },
migrations: {},
diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts
index 257b32c1e4c23..3f5c0c3876615 100644
--- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts
+++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts
@@ -27,7 +27,7 @@ const defaultSavedObjectTypes: SavedObjectsType[] = [
{
name: 'testtype',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
mappings: {
properties: {
name: { type: 'keyword' },
diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts
index 336eeff99f47b..cda0e86f15bdf 100644
--- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts
+++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts
@@ -28,8 +28,8 @@ const createRegistry = (types: Array>) => {
types.forEach(type =>
registry.registerType({
name: 'unknown',
- namespaceAgnostic: false,
hidden: false,
+ namespaceType: 'single',
mappings: { properties: {} },
migrations: {},
...type,
@@ -120,7 +120,7 @@ function mockOptions(): KibanaMigratorOptions {
{
name: 'testtype',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
mappings: {
properties: {
name: { type: 'keyword' },
@@ -131,7 +131,7 @@ function mockOptions(): KibanaMigratorOptions {
{
name: 'testtype2',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
indexPattern: 'other-index',
mappings: {
properties: {
diff --git a/src/core/server/saved_objects/routes/integration_tests/test_utils.ts b/src/core/server/saved_objects/routes/integration_tests/test_utils.ts
index 82a889f75d3c1..23e0285201dc7 100644
--- a/src/core/server/saved_objects/routes/integration_tests/test_utils.ts
+++ b/src/core/server/saved_objects/routes/integration_tests/test_utils.ts
@@ -49,7 +49,7 @@ export const createExportableType = (name: string): SavedObjectsType => {
return {
name,
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
mappings: {
properties: {},
},
diff --git a/src/core/server/saved_objects/saved_objects_type_registry.test.ts b/src/core/server/saved_objects/saved_objects_type_registry.test.ts
index 84337474f3ee3..f82822f90f489 100644
--- a/src/core/server/saved_objects/saved_objects_type_registry.test.ts
+++ b/src/core/server/saved_objects/saved_objects_type_registry.test.ts
@@ -183,14 +183,6 @@ describe('SavedObjectTypeRegistry', () => {
expectResult(false, { namespaceType: 'single' });
expectResult(false, { namespaceType: undefined });
});
-
- // deprecated test cases
- it(`returns true when namespaceAgnostic is true`, () => {
- expectResult(true, { namespaceAgnostic: true, namespaceType: 'agnostic' });
- expectResult(true, { namespaceAgnostic: true, namespaceType: 'multiple' });
- expectResult(true, { namespaceAgnostic: true, namespaceType: 'single' });
- expectResult(true, { namespaceAgnostic: true, namespaceType: undefined });
- });
});
describe('#isSingleNamespace', () => {
@@ -213,14 +205,6 @@ describe('SavedObjectTypeRegistry', () => {
expectResult(false, { namespaceType: 'agnostic' });
expectResult(false, { namespaceType: 'multiple' });
});
-
- // deprecated test cases
- it(`returns false when namespaceAgnostic is true`, () => {
- expectResult(false, { namespaceAgnostic: true, namespaceType: 'agnostic' });
- expectResult(false, { namespaceAgnostic: true, namespaceType: 'multiple' });
- expectResult(false, { namespaceAgnostic: true, namespaceType: 'single' });
- expectResult(false, { namespaceAgnostic: true, namespaceType: undefined });
- });
});
describe('#isMultiNamespace', () => {
@@ -243,14 +227,6 @@ describe('SavedObjectTypeRegistry', () => {
expectResult(false, { namespaceType: 'single' });
expectResult(false, { namespaceType: undefined });
});
-
- // deprecated test cases
- it(`returns false when namespaceAgnostic is true`, () => {
- expectResult(false, { namespaceAgnostic: true, namespaceType: 'agnostic' });
- expectResult(false, { namespaceAgnostic: true, namespaceType: 'multiple' });
- expectResult(false, { namespaceAgnostic: true, namespaceType: 'single' });
- expectResult(false, { namespaceAgnostic: true, namespaceType: undefined });
- });
});
describe('#isHidden', () => {
diff --git a/src/core/server/saved_objects/saved_objects_type_registry.ts b/src/core/server/saved_objects/saved_objects_type_registry.ts
index be3fdb86a994c..740313a53d1e2 100644
--- a/src/core/server/saved_objects/saved_objects_type_registry.ts
+++ b/src/core/server/saved_objects/saved_objects_type_registry.ts
@@ -72,11 +72,7 @@ export class SavedObjectTypeRegistry {
* resolves to `false` if the type is not registered
*/
public isNamespaceAgnostic(type: string) {
- return (
- this.types.get(type)?.namespaceType === 'agnostic' ||
- this.types.get(type)?.namespaceAgnostic ||
- false
- );
+ return this.types.get(type)?.namespaceType === 'agnostic';
}
/**
@@ -84,6 +80,7 @@ export class SavedObjectTypeRegistry {
* resolves to `true` if the type is not registered
*/
public isSingleNamespace(type: string) {
+ // in the case we somehow registered a type with an invalid `namespaceType`, treat it as single-namespace
return !this.isNamespaceAgnostic(type) && !this.isMultiNamespace(type);
}
@@ -92,7 +89,7 @@ export class SavedObjectTypeRegistry {
* resolves to `false` if the type is not registered
*/
public isMultiNamespace(type: string) {
- return !this.isNamespaceAgnostic(type) && this.types.get(type)?.namespaceType === 'multiple';
+ return this.types.get(type)?.namespaceType === 'multiple';
}
/**
diff --git a/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts b/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts
index a6b580e9b3461..ea881805e1ae6 100644
--- a/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts
+++ b/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts
@@ -32,7 +32,7 @@ describe('SavedObjectsRepository#createRepository', () => {
typeRegistry.registerType({
name: 'nsAgnosticType',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
name: { type: 'keyword' },
@@ -44,7 +44,7 @@ describe('SavedObjectsRepository#createRepository', () => {
typeRegistry.registerType({
name: 'nsType',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
indexPattern: 'beats',
mappings: {
properties: {
@@ -56,7 +56,7 @@ describe('SavedObjectsRepository#createRepository', () => {
typeRegistry.registerType({
name: 'hiddenType',
hidden: true,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
name: { type: 'keyword' },
diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts
index 9efc82603b179..b50c6dc9a1abf 100644
--- a/src/core/server/saved_objects/types.ts
+++ b/src/core/server/saved_objects/types.ts
@@ -202,15 +202,10 @@ export interface SavedObjectsType {
* See {@link SavedObjectsServiceStart.createInternalRepository | createInternalRepository}.
*/
hidden: boolean;
- /**
- * Is the type global (true), or not (false).
- * @deprecated Use `namespaceType` instead.
- */
- namespaceAgnostic?: boolean;
/**
* The {@link SavedObjectsNamespaceType | namespace type} for the type.
*/
- namespaceType?: SavedObjectsNamespaceType;
+ namespaceType: SavedObjectsNamespaceType;
/**
* If defined, the type instances will be stored in the given index instead of the default one.
*/
diff --git a/src/core/server/saved_objects/utils.test.ts b/src/core/server/saved_objects/utils.test.ts
index 64bdf1771decc..033aeea7c018d 100644
--- a/src/core/server/saved_objects/utils.test.ts
+++ b/src/core/server/saved_objects/utils.test.ts
@@ -421,14 +421,6 @@ describe('convertTypesToLegacySchema', () => {
namespaceType: 'multiple',
mappings: { properties: {} },
},
- // deprecated test case
- {
- name: 'typeD',
- hidden: false,
- namespaceAgnostic: true,
- namespaceType: 'multiple', // if namespaceAgnostic and namespaceType are both set, namespaceAgnostic takes precedence
- mappings: { properties: {} },
- },
];
expect(convertTypesToLegacySchema(types)).toEqual({
typeA: {
@@ -448,12 +440,6 @@ describe('convertTypesToLegacySchema', () => {
isNamespaceAgnostic: false,
multiNamespace: true,
},
- // deprecated test case
- typeD: {
- hidden: false,
- isNamespaceAgnostic: true,
- multiNamespace: false,
- },
});
});
});
diff --git a/src/core/server/saved_objects/utils.ts b/src/core/server/saved_objects/utils.ts
index 5348963812629..af7c08d1fbfcc 100644
--- a/src/core/server/saved_objects/utils.ts
+++ b/src/core/server/saved_objects/utils.ts
@@ -82,8 +82,8 @@ export const convertTypesToLegacySchema = (
return {
...schema,
[type.name]: {
- isNamespaceAgnostic: type.namespaceAgnostic || type.namespaceType === 'agnostic',
- multiNamespace: !type.namespaceAgnostic && type.namespaceType === 'multiple',
+ isNamespaceAgnostic: type.namespaceType === 'agnostic',
+ multiNamespace: type.namespaceType === 'multiple',
hidden: type.hidden,
indexPattern: type.indexPattern,
convertToAliasScript: type.convertToAliasScript,
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index 37051da4b17da..dd08df06c73d2 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -632,7 +632,9 @@ export interface CoreSetup;
// (undocumented)
- http: HttpServiceSetup;
+ http: HttpServiceSetup & {
+ resources: HttpResources;
+ };
// (undocumented)
metrics: MetricsServiceSetup;
// (undocumented)
@@ -861,6 +863,30 @@ export type Headers = {
[header: string]: string | string[] | undefined;
};
+// @public
+export interface HttpResources {
+ register: (route: RouteConfig
, handler: HttpResourcesRequestHandler
) => void;
+}
+
+// @public
+export interface HttpResourcesRenderOptions {
+ headers?: ResponseHeaders;
+}
+
+// @public
+export type HttpResourcesRequestHandler
= RequestHandler
;
+
+// @public
+export type HttpResourcesResponseOptions = HttpResponseOptions;
+
+// @public
+export interface HttpResourcesServiceToolkit {
+ renderAnonymousCoreApp: (options?: HttpResourcesRenderOptions) => Promise;
+ renderCoreApp: (options?: HttpResourcesRenderOptions) => Promise;
+ renderHtml: (options: HttpResourcesResponseOptions) => IKibanaResponse;
+ renderJs: (options: HttpResourcesResponseOptions) => IKibanaResponse;
+}
+
// @public
export interface HttpResponseOptions {
body?: HttpResponsePayload;
@@ -989,7 +1015,7 @@ export interface IRouter {
//
// @internal
getRoutes: () => RouterRoute[];
- handleLegacyErrors: (handler: RequestHandler
) => RequestHandler
;
+ handleLegacyErrors: RequestHandlerWrapper;
patch: RouteRegistrar<'patch'>;
post: RouteRegistrar<'post'>;
put: RouteRegistrar<'put'>;
@@ -1008,11 +1034,6 @@ export type ISavedObjectTypeRegistry = Omit;
-// @public (undocumented)
-export interface IScopedRenderingClient {
- render(options?: Pick): Promise;
-}
-
// @public
export interface IUiSettingsClient {
get: (key: string) => Promise;
@@ -1150,6 +1171,10 @@ export interface LegacyServiceSetupDeps {
core: LegacyCoreSetup;
// (undocumented)
plugins: Record;
+ // Warning: (ae-forgotten-export) The symbol "UiPlugins" needs to be exported by the entry point index.d.ts
+ //
+ // (undocumented)
+ uiPlugins: UiPlugins;
}
// @public @deprecated (undocumented)
@@ -1466,12 +1491,6 @@ export type PluginOpaqueId = symbol;
export interface PluginsServiceSetup {
contracts: Map;
initialized: boolean;
- // (undocumented)
- uiPlugins: {
- internal: Map;
- public: Map;
- browserConfigs: Map>;
- };
}
// @internal (undocumented)
@@ -1496,19 +1515,13 @@ export type RedirectResponseOptions = HttpResponseOptions & {
};
};
-// @internal (undocumented)
-export interface RenderingServiceSetup {
- render(request: R, uiSettings: IUiSettingsClient, options?: IRenderOptions): Promise;
-}
-
// @public
-export type RequestHandler = (context: RequestHandlerContext, request: KibanaRequest
, response: KibanaResponseFactory) => IKibanaResponse | Promise>;
+export type RequestHandler = (context: RequestHandlerContext, request: KibanaRequest
, response: ResponseFactory) => IKibanaResponse | Promise>;
// @public
export interface RequestHandlerContext {
// (undocumented)
core: {
- rendering: IScopedRenderingClient;
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
@@ -1529,6 +1542,9 @@ export type RequestHandlerContextContainer = IContextContainer = IContextProvider, TContextName>;
+// @public
+export type RequestHandlerWrapper = (handler: RequestHandler
) => RequestHandler
;
+
// @public
export function resolveSavedObjectsImportErrors({ readStream, objectLimit, retries, savedObjectsClient, supportedTypes, namespace, }: SavedObjectsResolveImportErrorsOptions): Promise;
@@ -1542,11 +1558,7 @@ export type ResponseError = string | Error | {
export type ResponseErrorAttributes = Record;
// @public
-export type ResponseHeaders = {
- [header in KnownHeaders]?: string | string[];
-} & {
- [header: string]: string | string[];
-};
+export type ResponseHeaders = Record | Record;
// @public
export interface RouteConfig {
@@ -2251,9 +2263,7 @@ export interface SavedObjectsType {
mappings: SavedObjectsTypeMappingDefinition;
migrations?: SavedObjectMigrationMap;
name: string;
- // @deprecated
- namespaceAgnostic?: boolean;
- namespaceType?: SavedObjectsNamespaceType;
+ namespaceType: SavedObjectsNamespaceType;
}
// @public
@@ -2463,7 +2473,6 @@ export const validBodyOutput: readonly ["data", "stream"];
// src/core/server/legacy/types.ts:164:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts
// src/core/server/legacy/types.ts:165:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts
// src/core/server/legacy/types.ts:166:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts
-// src/core/server/plugins/plugins_service.ts:47:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:230:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:230:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:232:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts
diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts
index 24c41d511180a..1e3e1638cf2a0 100644
--- a/src/core/server/server.test.ts
+++ b/src/core/server/server.test.ts
@@ -46,7 +46,10 @@ const rawConfigService = rawConfigServiceMock.create({});
beforeEach(() => {
mockConfigService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true }));
- mockPluginsService.discover.mockResolvedValue(new Map());
+ mockPluginsService.discover.mockResolvedValue({
+ pluginTree: new Map(),
+ uiPlugins: { internal: new Map(), public: new Map(), browserConfigs: new Map() },
+ });
});
afterEach(() => {
@@ -88,7 +91,10 @@ test('injects legacy dependency to context#setup()', async () => {
[pluginA, []],
[pluginB, [pluginA]],
]);
- mockPluginsService.discover.mockResolvedValue(pluginDependencies);
+ mockPluginsService.discover.mockResolvedValue({
+ pluginTree: pluginDependencies,
+ uiPlugins: { internal: new Map(), public: new Map(), browserConfigs: new Map() },
+ });
await server.setup();
diff --git a/src/core/server/server.ts b/src/core/server/server.ts
index 684f50a5666e1..d4c0ebcfb7cf2 100644
--- a/src/core/server/server.ts
+++ b/src/core/server/server.ts
@@ -29,7 +29,8 @@ import {
import { CoreApp } from './core_app';
import { ElasticsearchService } from './elasticsearch';
import { HttpService } from './http';
-import { RenderingService, RenderingServiceSetup } from './rendering';
+import { HttpResourcesService } from './http_resources';
+import { RenderingService } from './rendering';
import { LegacyService, ensureValidConfiguration } from './legacy';
import { Logger, LoggerFactory } from './logging';
import { UiSettingsService } from './ui_settings';
@@ -71,6 +72,7 @@ export class Server {
private readonly uiSettings: UiSettingsService;
private readonly uuid: UuidService;
private readonly metrics: MetricsService;
+ private readonly httpResources: HttpResourcesService;
private readonly status: StatusService;
private readonly coreApp: CoreApp;
@@ -99,13 +101,14 @@ export class Server {
this.metrics = new MetricsService(core);
this.status = new StatusService(core);
this.coreApp = new CoreApp(core);
+ this.httpResources = new HttpResourcesService(core);
}
public async setup() {
this.log.debug('setting up server');
// Discover any plugins before continuing. This allows other systems to utilize the plugin dependency graph.
- const pluginDependencies = await this.plugins.discover();
+ const { pluginTree, uiPlugins } = await this.plugins.discover();
const legacyPlugins = await this.legacy.discoverPlugins();
// Immediately terminate in case of invalid configuration
@@ -117,10 +120,7 @@ export class Server {
// 1) Can access context from any NP plugin
// 2) Can register context providers that will only be available to other legacy plugins and will not leak into
// New Platform plugins.
- pluginDependencies: new Map([
- ...pluginDependencies,
- [this.legacy.legacyId, [...pluginDependencies.keys()]],
- ]),
+ pluginDependencies: new Map([...pluginTree, [this.legacy.legacyId, [...pluginTree.keys()]]]),
});
const uuidSetup = await this.uuid.setup();
@@ -148,6 +148,17 @@ export class Server {
const metricsSetup = await this.metrics.setup({ http: httpSetup });
+ const renderingSetup = await this.rendering.setup({
+ http: httpSetup,
+ legacyPlugins,
+ uiPlugins,
+ });
+
+ const httpResourcesSetup = this.httpResources.setup({
+ http: httpSetup,
+ rendering: renderingSetup,
+ });
+
const statusSetup = this.status.setup({
elasticsearch: elasticsearchServiceSetup,
savedObjects: savedObjectsSetup,
@@ -158,28 +169,25 @@ export class Server {
context: contextServiceSetup,
elasticsearch: elasticsearchServiceSetup,
http: httpSetup,
- metrics: metricsSetup,
savedObjects: savedObjectsSetup,
status: statusSetup,
uiSettings: uiSettingsSetup,
uuid: uuidSetup,
+ metrics: metricsSetup,
+ rendering: renderingSetup,
+ httpResources: httpResourcesSetup,
};
const pluginsSetup = await this.plugins.setup(coreSetup);
this.pluginsInitialized = pluginsSetup.initialized;
- const renderingSetup = await this.rendering.setup({
- http: httpSetup,
- legacyPlugins,
- plugins: pluginsSetup,
- });
-
await this.legacy.setup({
core: { ...coreSetup, plugins: pluginsSetup, rendering: renderingSetup },
plugins: mapToObject(pluginsSetup.contracts),
+ uiPlugins,
});
- this.registerCoreContext(coreSetup, renderingSetup);
+ this.registerCoreContext(coreSetup);
this.coreApp.setup(coreSetup);
return coreSetup;
@@ -212,7 +220,9 @@ export class Server {
});
await this.http.start();
- await this.rendering.start();
+ await this.rendering.start({
+ legacy: this.legacy,
+ });
await this.metrics.start();
return this.coreStart;
@@ -232,7 +242,7 @@ export class Server {
await this.status.stop();
}
- private registerCoreContext(coreSetup: InternalCoreSetup, rendering: RenderingServiceSetup) {
+ private registerCoreContext(coreSetup: InternalCoreSetup) {
coreSetup.http.registerRouteHandlerContext(
coreId,
'core',
@@ -241,13 +251,6 @@ export class Server {
const uiSettingsClient = coreSetup.uiSettings.asScopedToClient(savedObjectsClient);
return {
- rendering: {
- render: async (options = {}) =>
- rendering.render(req, uiSettingsClient, {
- ...options,
- vars: await this.legacy.legacyInternals!.getVars('core', req),
- }),
- },
savedObjects: {
client: savedObjectsClient,
typeRegistry: this.coreStart!.savedObjects.getTypeRegistry(),
diff --git a/src/core/server/ui_settings/saved_objects/ui_settings.ts b/src/core/server/ui_settings/saved_objects/ui_settings.ts
index 031315bec0dab..1bea65ddee924 100644
--- a/src/core/server/ui_settings/saved_objects/ui_settings.ts
+++ b/src/core/server/ui_settings/saved_objects/ui_settings.ts
@@ -22,7 +22,7 @@ import { SavedObjectsType } from '../../saved_objects';
export const uiSettingsType: SavedObjectsType = {
name: 'config',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
mappings: {
// we don't want to allow `true` in the public `SavedObjectsTypeMappingDefinition` type, however
// this is needed for the config that is kinda a special type. To avoid adding additional internal types
diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js
index 1d643418997f5..989583742acd0 100644
--- a/src/legacy/core_plugins/kibana/index.js
+++ b/src/legacy/core_plugins/kibana/index.js
@@ -54,11 +54,7 @@ export default function(kibana) {
},
uiExports: {
- hacks: [
- 'plugins/kibana/discover/legacy',
- 'plugins/kibana/dev_tools',
- 'plugins/kibana/visualize/legacy',
- ],
+ hacks: ['plugins/kibana/discover/legacy', 'plugins/kibana/dev_tools'],
app: {
id: 'kibana',
title: 'Kibana',
diff --git a/src/legacy/core_plugins/kibana/public/index.scss b/src/legacy/core_plugins/kibana/public/index.scss
index fbfb0a06fabcf..d49c59970f521 100644
--- a/src/legacy/core_plugins/kibana/public/index.scss
+++ b/src/legacy/core_plugins/kibana/public/index.scss
@@ -13,8 +13,10 @@
// Discover styles
@import 'discover/index';
-// Visualize styles
-@import './visualize/index';
+// Visualization styles are imported here for running karma Browser tests
+// should be somehow included through the "visualizations" plugin initialization
+@import '../../../../plugins/visualizations/public/index';
+
// Has to come after visualize because of some
// bad cascading in the Editor layout
@import '../../../../plugins/maps_legacy/public/index';
diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js
index 0a026a5e0c310..20c46765dcb30 100644
--- a/src/legacy/core_plugins/kibana/public/kibana.js
+++ b/src/legacy/core_plugins/kibana/public/kibana.js
@@ -43,7 +43,6 @@ import 'uiExports/interpreter';
import 'ui/autoload/all';
import './discover/legacy';
-import './visualize/legacy';
import './management';
import './dev_tools';
import { showAppRedirectNotification } from '../../../../plugins/kibana_legacy/public';
diff --git a/src/legacy/core_plugins/kibana/public/visualize/_index.scss b/src/legacy/core_plugins/kibana/public/visualize/_index.scss
deleted file mode 100644
index 079d82936bb57..0000000000000
--- a/src/legacy/core_plugins/kibana/public/visualize/_index.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-// Visualize plugin styles
-@import 'np_ready/index';
-
-// should be removed while moving the visualize into NP
-@import '../../../../../plugins/vis_default_editor/public/index'
diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
deleted file mode 100644
index f6d73b987912d..0000000000000
--- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * The imports in this file are static functions and types which still live in legacy folders and are used
- * within dashboard. To consolidate them all in one place, they are re-exported from this file. Eventually
- * this list should become empty. Imports from the top level of shimmed or moved plugins can be imported
- * directly where they are needed.
- */
-
-export { DashboardConstants } from '../../../../../plugins/dashboard/public';
-export {
- VisSavedObject,
- VISUALIZE_EMBEDDABLE_TYPE,
-} from '../../../../../plugins/visualizations/public/';
-export {
- configureAppAngularModule,
- migrateLegacyQuery,
- subscribeWithScope,
-} from '../../../../../plugins/kibana_legacy/public';
diff --git a/src/legacy/core_plugins/vis_type_markdown/index.ts b/src/legacy/core_plugins/vis_type_markdown/index.ts
deleted file mode 100644
index 3c00420e57d55..0000000000000
--- a/src/legacy/core_plugins/vis_type_markdown/index.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { resolve } from 'path';
-import { Legacy } from 'kibana';
-
-import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types';
-
-const markdownPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) =>
- new Plugin({
- id: 'markdown_vis',
- require: ['kibana', 'elasticsearch'],
- publicDir: resolve(__dirname, 'public'),
- uiExports: {
- styleSheetPaths: resolve(__dirname, 'public/index.scss'),
- hacks: [resolve(__dirname, 'public/legacy')],
- injectDefaultVars: server => ({}),
- },
- init: (server: Legacy.Server) => ({}),
- config(Joi: any) {
- return Joi.object({
- enabled: Joi.boolean().default(true),
- }).default();
- },
- } as Legacy.PluginSpecOptions);
-
-// eslint-disable-next-line import/no-default-export
-export default markdownPluginInitializer;
diff --git a/src/legacy/core_plugins/vis_type_markdown/package.json b/src/legacy/core_plugins/vis_type_markdown/package.json
deleted file mode 100644
index 5c233d82fe506..0000000000000
--- a/src/legacy/core_plugins/vis_type_markdown/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "markdown_vis",
- "version": "kibana"
-}
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx b/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx
index afffcf7ccaf7a..dfe017d3a273f 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx
+++ b/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx
@@ -24,7 +24,9 @@ import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { VisParams } from './timelion_vis_fn';
import { TimelionInterval, TimelionExpressionInput } from './components';
-function TimelionOptions({ stateParams, setValue, setValidity }: VisOptionsProps) {
+export type TimelionOptionsProps = VisOptionsProps;
+
+function TimelionOptions({ stateParams, setValue, setValidity }: TimelionOptionsProps) {
const setInterval = useCallback((value: VisParams['interval']) => setValue('interval', value), [
setValue,
]);
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx b/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx
index 5be77b3e51a6a..0900b7d898ede 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx
+++ b/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx
@@ -24,7 +24,7 @@ import { KibanaContextProvider } from '../../../../plugins/kibana_react/public';
import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public';
import { getTimelionRequestHandler } from './helpers/timelion_request_handler';
import { TimelionVisComponent, TimelionVisComponentProp } from './components';
-import { TimelionOptions } from './timelion_options';
+import { TimelionOptions, TimelionOptionsProps } from './timelion_options';
import { TimelionVisDependencies } from './plugin';
export const TIMELION_VIS_NAME = 'timelion';
@@ -53,7 +53,11 @@ export function getTimelionVisDefinition(dependencies: TimelionVisDependencies)
),
},
editorConfig: {
- optionsTemplate: TimelionOptions,
+ optionsTemplate: (props: TimelionOptionsProps) => (
+
+
+
+ ),
defaultSize: DefaultEditorSize.MEDIUM,
},
requestHandler: timelionRequestHandler,
diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts
index a9b8c29374854..0d2f3528c9019 100644
--- a/src/legacy/server/kbn_server.d.ts
+++ b/src/legacy/server/kbn_server.d.ts
@@ -41,10 +41,11 @@ import {
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { LegacyConfig, ILegacyService, ILegacyInternals } from '../../core/server/legacy';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { UiPlugins } from '../../core/server/plugins';
import { ApmOssPlugin } from '../core_plugins/apm_oss';
import { CallClusterWithRequest, ElasticsearchPlugin } from '../core_plugins/elasticsearch';
import { UsageCollectionSetup } from '../../plugins/usage_collection/server';
-import { Capabilities } from '../../core/server';
import { UiSettingsServiceFactoryOptions } from '../../legacy/ui/ui_settings/ui_settings_service_factory';
import { HomeServerPluginSetup } from '../../plugins/home/server';
@@ -111,7 +112,7 @@ export interface KibanaCore {
kibanaMigrator: LegacyServiceStartDeps['core']['savedObjects']['migrator'];
legacy: ILegacyInternals;
rendering: LegacyServiceSetupDeps['core']['rendering'];
- uiPlugins: LegacyServiceSetupDeps['core']['plugins']['uiPlugins'];
+ uiPlugins: UiPlugins;
uiSettings: LegacyServiceSetupDeps['core']['uiSettings'];
savedObjectsClientProvider: LegacyServiceStartDeps['core']['savedObjects']['clientProvider'];
};
diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss
index 87006d9347de4..aaed52f8b120a 100644
--- a/src/legacy/ui/public/_index.scss
+++ b/src/legacy/ui/public/_index.scss
@@ -17,9 +17,3 @@
@import './field_editor/index';
@import './style_compile/index';
@import '../../../plugins/management/public/components/index';
-
-// The following are prefixed with "vis"
-
-// Can't import vis folder here because of cascading issues, it's imported in core_plugins/kibana
-// @import './vis/index';
-@import './visualize/index';
diff --git a/src/legacy/ui/public/visualize/_index.scss b/src/legacy/ui/public/visualize/_index.scss
deleted file mode 100644
index d9761f741353b..0000000000000
--- a/src/legacy/ui/public/visualize/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import '../../../../plugins/visualizations/public/components/index';
diff --git a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
index e71e4f1b15134..7210879c5eacc 100644
--- a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
+++ b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
@@ -667,14 +667,13 @@ exports[`DashboardEmptyScreen renders correctly with visualize paragraph 1`] = `
@@ -685,12 +684,12 @@ exports[`DashboardEmptyScreen renders correctly with visualize paragraph 1`] = `
aria-hidden="true"
className="euiButton__icon"
size="m"
- type="arrowDown"
+ type="plusInCircle"
>
diff --git a/src/plugins/dashboard/public/application/_dashboard_app.scss b/src/plugins/dashboard/public/application/_dashboard_app.scss
index 03a8a07d6b17d..8f389bb031df1 100644
--- a/src/plugins/dashboard/public/application/_dashboard_app.scss
+++ b/src/plugins/dashboard/public/application/_dashboard_app.scss
@@ -19,18 +19,10 @@
.dshEmptyWidget {
border: $euiBorderThin;
- border-style: dashed;
+ border-style: dashed;
border-radius: $euiBorderRadius;
padding: $euiSizeXXL * 2;
max-width: 400px;
margin-left: $euiSizeS;
text-align: center;
}
-
-.dshEmptyWidget {
- border: 2px dashed $euiColorLightShade;
- padding: 4 * $euiSize;
- max-width: 20em;
- margin-left: 10px;
- text-align: center;
-}
diff --git a/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx b/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx
index 3ef87fda7236b..8bf205b8cb507 100644
--- a/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx
+++ b/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx
@@ -55,10 +55,9 @@ export function DashboardEmptyScreen({
const linkToVisualizeParagraph = (
diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts
index 9ad9f14ac5659..9bec91b859ab7 100644
--- a/src/plugins/kibana_react/public/index.ts
+++ b/src/plugins/kibana_react/public/index.ts
@@ -25,7 +25,7 @@ export * from './ui_settings';
export * from './field_icon';
export * from './table_list_view';
export * from './split_panel';
-export { ValidatedDualRange } from './validated_range';
+export { ValidatedDualRange, Value } from './validated_range';
export * from './notifications';
export { Markdown, MarkdownSimple } from './markdown';
export { reactToUiComponent, uiToReactComponent } from './adapters';
diff --git a/src/plugins/kibana_react/public/validated_range/index.ts b/src/plugins/kibana_react/public/validated_range/index.ts
index bc643373f5e60..7d720ec842a43 100644
--- a/src/plugins/kibana_react/public/validated_range/index.ts
+++ b/src/plugins/kibana_react/public/validated_range/index.ts
@@ -17,4 +17,4 @@
* under the License.
*/
-export { ValidatedDualRange } from './validated_dual_range';
+export { ValidatedDualRange, Value } from './validated_dual_range';
diff --git a/src/plugins/kibana_utils/public/core/create_start_service_getter.test.ts b/src/plugins/kibana_utils/public/core/create_start_service_getter.test.ts
new file mode 100644
index 0000000000000..9d9b21269e102
--- /dev/null
+++ b/src/plugins/kibana_utils/public/core/create_start_service_getter.test.ts
@@ -0,0 +1,78 @@
+/*
+ * 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 { StartServicesAccessor } from '../../../../core/public';
+import { createStartServicesGetter } from './create_start_service_getter';
+import { Defer } from '../../common/defer';
+
+describe('createStartServicesGetter', () => {
+ test('throws if services are accessed before accessor resolves', async () => {
+ const future = new Defer();
+ const accessor: StartServicesAccessor = async () => await future.promise;
+ const start = createStartServicesGetter(accessor);
+
+ await new Promise(r => setTimeout(r, 1));
+
+ expect(() => start()).toThrowErrorMatchingInlineSnapshot(
+ `"Trying to access start services before start."`
+ );
+ });
+
+ test('returns services after accessor resolves even if first time called before it resolved', async () => {
+ const future = new Defer();
+ const core = {};
+ const plugins = {};
+ const self = {};
+ const accessor: StartServicesAccessor = async () => await future.promise;
+ const start = createStartServicesGetter(accessor);
+
+ await new Promise(r => setTimeout(r, 1));
+
+ expect(() => start()).toThrow();
+
+ await new Promise(r => setTimeout(r, 1));
+ future.resolve([core, plugins, self]);
+ await future.promise;
+
+ expect(start()).toEqual({
+ core,
+ plugins,
+ self,
+ });
+ });
+
+ test('returns services if called after accessor resolves', async () => {
+ const future = new Defer();
+ const core = {};
+ const plugins = {};
+ const self = {};
+ const accessor: StartServicesAccessor = async () => await future.promise;
+ const start = createStartServicesGetter(accessor);
+
+ await new Promise(r => setTimeout(r, 1));
+ future.resolve([core, plugins, self]);
+ await future.promise;
+
+ expect(start()).toEqual({
+ core,
+ plugins,
+ self,
+ });
+ });
+});
diff --git a/src/plugins/kibana_utils/public/core/create_start_service_getter.ts b/src/plugins/kibana_utils/public/core/create_start_service_getter.ts
new file mode 100644
index 0000000000000..e507d1ae778e5
--- /dev/null
+++ b/src/plugins/kibana_utils/public/core/create_start_service_getter.ts
@@ -0,0 +1,56 @@
+/*
+ * 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 { CoreStart, StartServicesAccessor } from '../../../../core/public';
+
+export interface StartServices {
+ plugins: Plugins;
+ self: OwnContract;
+ core: CoreStart;
+}
+
+export type StartServicesGetter = () => StartServices<
+ Plugins,
+ OwnContract
+>;
+
+export const createStartServicesGetter = (
+ accessor: StartServicesAccessor
+): StartServicesGetter => {
+ let services: StartServices | undefined;
+
+ accessor().then(
+ ([core, plugins, self]) => {
+ services = {
+ core,
+ plugins,
+ self,
+ };
+ },
+ error => {
+ // eslint-disable-next-line no-console
+ console.error('Could not access start services.', error);
+ }
+ );
+
+ return () => {
+ if (!services) throw new Error('Trying to access start services before start.');
+ return services;
+ };
+};
diff --git a/src/plugins/kibana_utils/public/core/index.ts b/src/plugins/kibana_utils/public/core/index.ts
index 3f08d591300a2..8bbb2129071f5 100644
--- a/src/plugins/kibana_utils/public/core/index.ts
+++ b/src/plugins/kibana_utils/public/core/index.ts
@@ -18,3 +18,4 @@
*/
export * from './create_kibana_utils_core';
+export * from './create_start_service_getter';
diff --git a/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap b/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap
index 0d54d5d3e9c4a..570699aa0c0e2 100644
--- a/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap
+++ b/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap
@@ -8,11 +8,6 @@ exports[`TopNavMenu Should render emphasized item which should be clickable 1`]
isDisabled={false}
onClick={[Function]}
size="s"
- style={
- Object {
- "fontSize": "smaller",
- }
- }
>
Test
diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx
index 92e267f17d08e..3a05ce59f5d13 100644
--- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx
+++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx
@@ -49,7 +49,7 @@ export function TopNavMenuItem(props: TopNavMenuData) {
};
const btn = props.emphasize ? (
-
+
{capitalize(props.label || props.id!)}
) : (
diff --git a/src/plugins/saved_objects_management/server/services/management.test.ts b/src/plugins/saved_objects_management/server/services/management.test.ts
index 6b95048749fae..3625a3f913444 100644
--- a/src/plugins/saved_objects_management/server/services/management.test.ts
+++ b/src/plugins/saved_objects_management/server/services/management.test.ts
@@ -28,7 +28,7 @@ describe('SavedObjectsManagement', () => {
registry.registerType({
name: 'unknown',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
mappings: { properties: {} },
migrations: {},
...type,
diff --git a/src/plugins/share/server/routes/goto.ts b/src/plugins/share/server/routes/goto.ts
index 0c5b74915e58a..747af3b9e57df 100644
--- a/src/plugins/share/server/routes/goto.ts
+++ b/src/plugins/share/server/routes/goto.ts
@@ -34,7 +34,7 @@ export const createGotoRoute = ({
shortUrlLookup: ShortUrlLookupService;
http: CoreSetup['http'];
}) => {
- router.get(
+ http.resources.register(
{
path: getGotoPath('{urlId}'),
validate: {
@@ -63,14 +63,8 @@ export const createGotoRoute = ({
},
});
}
- const body = await context.core.rendering.render();
- return response.ok({
- headers: {
- 'content-security-policy': http.csp.header,
- },
- body,
- });
+ return response.renderCoreApp();
})
);
};
diff --git a/src/plugins/telemetry/server/collectors/application_usage/saved_objects_types.ts b/src/plugins/telemetry/server/collectors/application_usage/saved_objects_types.ts
index 9f997ab7b5df3..a0de79da565e6 100644
--- a/src/plugins/telemetry/server/collectors/application_usage/saved_objects_types.ts
+++ b/src/plugins/telemetry/server/collectors/application_usage/saved_objects_types.ts
@@ -33,7 +33,7 @@ export function registerMappings(registerType: SavedObjectsServiceSetup['registe
registerType({
name: 'application_usage_totals',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
appId: { type: 'keyword' },
@@ -46,7 +46,7 @@ export function registerMappings(registerType: SavedObjectsServiceSetup['registe
registerType({
name: 'application_usage_transactional',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
timestamp: { type: 'date' },
diff --git a/src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts b/src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
index 3f6e1836cac7d..603742f612a6b 100644
--- a/src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
+++ b/src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
@@ -38,7 +38,7 @@ export function registerUiMetricUsageCollector(
registerType({
name: 'ui-metric',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
count: {
diff --git a/src/plugins/telemetry/server/plugin.ts b/src/plugins/telemetry/server/plugin.ts
index 1df6a665e4d76..d1530c272027a 100644
--- a/src/plugins/telemetry/server/plugin.ts
+++ b/src/plugins/telemetry/server/plugin.ts
@@ -125,7 +125,7 @@ export class TelemetryPlugin implements Plugin {
registerType({
name: 'telemetry',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
enabled: {
diff --git a/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts b/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts
index c2ac63c98cbea..dac86249ebbb9 100644
--- a/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts
+++ b/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts
@@ -92,7 +92,7 @@ function validateValueUnique(
isDuplicate: false,
};
- if (inputValue && list.indexOf(inputValue) !== index) {
+ if (inputValue !== EMPTY_STRING && list.indexOf(inputValue) !== index) {
result.isDuplicate = true;
result.error = i18n.translate(
'visDefaultEditor.controls.numberList.duplicateValueErrorMessage',
diff --git a/src/plugins/vis_default_editor/public/default_editor.tsx b/src/plugins/vis_default_editor/public/default_editor.tsx
index f1963b94dcf95..1c2ddbc314f99 100644
--- a/src/plugins/vis_default_editor/public/default_editor.tsx
+++ b/src/plugins/vis_default_editor/public/default_editor.tsx
@@ -19,7 +19,7 @@
import React, { useEffect, useRef, useState, useCallback } from 'react';
-import { EditorRenderProps } from 'src/legacy/core_plugins/kibana/public/visualize/np_ready/types';
+import { EditorRenderProps } from 'src/plugins/visualize/public';
import { PanelsContainer, Panel } from '../../kibana_react/public';
import './vis_type_agg_filter';
diff --git a/src/plugins/vis_default_editor/public/default_editor_controller.tsx b/src/plugins/vis_default_editor/public/default_editor_controller.tsx
index 798da09f8e30b..014c69f50d558 100644
--- a/src/plugins/vis_default_editor/public/default_editor_controller.tsx
+++ b/src/plugins/vis_default_editor/public/default_editor_controller.tsx
@@ -22,7 +22,7 @@ import { render, unmountComponentAtNode } from 'react-dom';
import { i18n } from '@kbn/i18n';
import { EventEmitter } from 'events';
-import { EditorRenderProps } from 'src/legacy/core_plugins/kibana/public/visualize/np_ready/types';
+import { EditorRenderProps } from 'src/plugins/visualize/public';
import { Vis, VisualizeEmbeddableContract } from 'src/plugins/visualizations/public';
import { Storage } from '../../kibana_utils/public';
import { KibanaContextProvider } from '../../kibana_react/public';
diff --git a/src/plugins/vis_type_markdown/config.ts b/src/plugins/vis_type_markdown/config.ts
new file mode 100644
index 0000000000000..6749bd83de39f
--- /dev/null
+++ b/src/plugins/vis_type_markdown/config.ts
@@ -0,0 +1,26 @@
+/*
+ * 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 { schema, TypeOf } from '@kbn/config-schema';
+
+export const configSchema = schema.object({
+ enabled: schema.boolean({ defaultValue: true }),
+});
+
+export type ConfigSchema = TypeOf;
diff --git a/src/plugins/vis_type_markdown/kibana.json b/src/plugins/vis_type_markdown/kibana.json
new file mode 100644
index 0000000000000..d52e22118ccf0
--- /dev/null
+++ b/src/plugins/vis_type_markdown/kibana.json
@@ -0,0 +1,7 @@
+{
+ "id": "visTypeMarkdown",
+ "version": "kibana",
+ "ui": true,
+ "server": true,
+ "requiredPlugins": ["expressions", "visualizations"]
+}
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap b/src/plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap
similarity index 100%
rename from src/legacy/core_plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap
rename to src/plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/_markdown_vis.scss b/src/plugins/vis_type_markdown/public/_markdown_vis.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_markdown/public/_markdown_vis.scss
rename to src/plugins/vis_type_markdown/public/_markdown_vis.scss
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/index.scss b/src/plugins/vis_type_markdown/public/index.scss
similarity index 76%
rename from src/legacy/core_plugins/vis_type_markdown/public/index.scss
rename to src/plugins/vis_type_markdown/public/index.scss
index ebae2d1936a9e..ddb7fe3a6b0d9 100644
--- a/src/legacy/core_plugins/vis_type_markdown/public/index.scss
+++ b/src/plugins/vis_type_markdown/public/index.scss
@@ -1,5 +1,3 @@
-@import 'src/legacy/ui/public/styles/styling_constants';
-
// Prefix all styles with "mkd" to avoid conflicts.
// Examples
// mkdChart
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/index.ts b/src/plugins/vis_type_markdown/public/index.ts
similarity index 93%
rename from src/legacy/core_plugins/vis_type_markdown/public/index.ts
rename to src/plugins/vis_type_markdown/public/index.ts
index 22dd61b6bda9c..bc3b6ff7bf105 100644
--- a/src/legacy/core_plugins/vis_type_markdown/public/index.ts
+++ b/src/plugins/vis_type_markdown/public/index.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { PluginInitializerContext } from '../../../../core/public';
+import { PluginInitializerContext } from '../../../core/public';
import { MarkdownPlugin as Plugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/markdown_fn.test.ts b/src/plugins/vis_type_markdown/public/markdown_fn.test.ts
similarity index 89%
rename from src/legacy/core_plugins/vis_type_markdown/public/markdown_fn.test.ts
rename to src/plugins/vis_type_markdown/public/markdown_fn.test.ts
index 5f41840bac99b..d6085804e74ab 100644
--- a/src/legacy/core_plugins/vis_type_markdown/public/markdown_fn.test.ts
+++ b/src/plugins/vis_type_markdown/public/markdown_fn.test.ts
@@ -17,8 +17,7 @@
* under the License.
*/
-// eslint-disable-next-line
-import { functionWrapper } from '../../../../plugins/expressions/common/expression_functions/specs/tests/utils';
+import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils';
import { createMarkdownVisFn } from './markdown_fn';
describe('interpreter/functions#markdown', () => {
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/markdown_fn.ts b/src/plugins/vis_type_markdown/public/markdown_fn.ts
similarity index 95%
rename from src/legacy/core_plugins/vis_type_markdown/public/markdown_fn.ts
rename to src/plugins/vis_type_markdown/public/markdown_fn.ts
index bbf2b7844c73f..9f0809109e465 100644
--- a/src/legacy/core_plugins/vis_type_markdown/public/markdown_fn.ts
+++ b/src/plugins/vis_type_markdown/public/markdown_fn.ts
@@ -18,7 +18,7 @@
*/
import { i18n } from '@kbn/i18n';
-import { ExpressionFunctionDefinition, Render } from '../../../../plugins/expressions/public';
+import { ExpressionFunctionDefinition, Render } from '../../expressions/public';
import { Arguments, MarkdownVisParams } from './types';
interface RenderValue {
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/markdown_options.tsx b/src/plugins/vis_type_markdown/public/markdown_options.tsx
similarity index 100%
rename from src/legacy/core_plugins/vis_type_markdown/public/markdown_options.tsx
rename to src/plugins/vis_type_markdown/public/markdown_options.tsx
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts b/src/plugins/vis_type_markdown/public/markdown_vis.ts
similarity index 96%
rename from src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts
rename to src/plugins/vis_type_markdown/public/markdown_vis.ts
index 57ea6d9c9bb3d..b84d9638eb973 100644
--- a/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts
+++ b/src/plugins/vis_type_markdown/public/markdown_vis.ts
@@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n';
import { MarkdownVisWrapper } from './markdown_vis_controller';
import { MarkdownOptions } from './markdown_options';
import { SettingsOptions } from './settings_options';
-import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public';
+import { DefaultEditorSize } from '../../vis_default_editor/public';
export const markdownVisDefinition = {
name: 'markdown',
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx b/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx
rename to src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis_controller.tsx b/src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx
similarity index 97%
rename from src/legacy/core_plugins/vis_type_markdown/public/markdown_vis_controller.tsx
rename to src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx
index 3260e9f7d8091..4e77bb196b713 100644
--- a/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis_controller.tsx
+++ b/src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx
@@ -18,7 +18,7 @@
*/
import React from 'react';
-import { Markdown } from '../../../../plugins/kibana_react/public';
+import { Markdown } from '../../kibana_react/public';
import { MarkdownVisParams } from './types';
interface MarkdownVisComponentProps extends MarkdownVisParams {
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/plugin.ts b/src/plugins/vis_type_markdown/public/plugin.ts
similarity index 80%
rename from src/legacy/core_plugins/vis_type_markdown/public/plugin.ts
rename to src/plugins/vis_type_markdown/public/plugin.ts
index 0445d270c9330..9365017a31adc 100644
--- a/src/legacy/core_plugins/vis_type_markdown/public/plugin.ts
+++ b/src/plugins/vis_type_markdown/public/plugin.ts
@@ -17,12 +17,15 @@
* under the License.
*/
-import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public';
-import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public';
-import { VisualizationsSetup } from '../../../../plugins/visualizations/public';
+import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public';
+import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public';
+import { VisualizationsSetup } from '../../visualizations/public';
import { markdownVisDefinition } from './markdown_vis';
import { createMarkdownVisFn } from './markdown_fn';
+import { ConfigSchema } from '../config';
+
+import './index.scss';
/** @internal */
export interface MarkdownPluginSetupDependencies {
@@ -32,9 +35,9 @@ export interface MarkdownPluginSetupDependencies {
/** @internal */
export class MarkdownPlugin implements Plugin {
- initializerContext: PluginInitializerContext;
+ initializerContext: PluginInitializerContext;
- constructor(initializerContext: PluginInitializerContext) {
+ constructor(initializerContext: PluginInitializerContext) {
this.initializerContext = initializerContext;
}
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx b/src/plugins/vis_type_markdown/public/settings_options.tsx
similarity index 95%
rename from src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx
rename to src/plugins/vis_type_markdown/public/settings_options.tsx
index 16b2749c34e10..6f6a80564ce07 100644
--- a/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx
+++ b/src/plugins/vis_type_markdown/public/settings_options.tsx
@@ -22,7 +22,7 @@ import { EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
-import { RangeOption, SwitchOption } from '../../../../plugins/charts/public';
+import { RangeOption, SwitchOption } from '../../charts/public';
import { MarkdownVisParams } from './types';
function SettingsOptions({ stateParams, setValue }: VisOptionsProps) {
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/types.ts b/src/plugins/vis_type_markdown/public/types.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_markdown/public/types.ts
rename to src/plugins/vis_type_markdown/public/types.ts
diff --git a/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts b/src/plugins/vis_type_markdown/server/index.ts
similarity index 59%
rename from src/legacy/core_plugins/vis_type_markdown/public/legacy.ts
rename to src/plugins/vis_type_markdown/server/index.ts
index 1cfc583f6e005..73e1712353b1a 100644
--- a/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts
+++ b/src/plugins/vis_type_markdown/server/index.ts
@@ -17,17 +17,18 @@
* under the License.
*/
-import { PluginInitializerContext } from 'kibana/public';
-import { npSetup, npStart } from 'ui/new_platform';
-import { MarkdownPluginSetupDependencies } from './plugin';
-import { plugin } from '.';
+import { PluginConfigDescriptor } from 'kibana/server';
-const plugins: Readonly = {
- expressions: npSetup.plugins.expressions,
- visualizations: npSetup.plugins.visualizations,
-};
+import { configSchema, ConfigSchema } from '../config';
-const pluginInstance = plugin({} as PluginInitializerContext);
+export const config: PluginConfigDescriptor = {
+ schema: configSchema,
+ deprecations: ({ renameFromRoot }) => [
+ renameFromRoot('markdown_vis.enabled', 'vis_type_markdown.enabled'),
+ ],
+};
-export const setup = pluginInstance.setup(npSetup.core, plugins);
-export const start = pluginInstance.start(npStart.core);
+export const plugin = () => ({
+ setup() {},
+ start() {},
+});
diff --git a/src/plugins/vis_type_timeseries/kibana.json b/src/plugins/vis_type_timeseries/kibana.json
index 305e159ac2505..38662c6a7ff89 100644
--- a/src/plugins/vis_type_timeseries/kibana.json
+++ b/src/plugins/vis_type_timeseries/kibana.json
@@ -1,5 +1,5 @@
{
- "id": "metrics",
+ "id": "visTypeTimeseries",
"version": "8.0.0",
"kibanaVersion": "kibana",
"server": true,
diff --git a/src/plugins/vis_type_timeseries/server/config.ts b/src/plugins/vis_type_timeseries/server/config.ts
new file mode 100644
index 0000000000000..f4668eff8fa04
--- /dev/null
+++ b/src/plugins/vis_type_timeseries/server/config.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 { schema, TypeOf } from '@kbn/config-schema';
+
+export const config = schema.object({
+ enabled: schema.boolean({ defaultValue: true }),
+
+ /** @deprecated **/
+ chartResolution: schema.number({ defaultValue: 150 }),
+ /** @deprecated **/
+ minimumBucketSize: schema.number({ defaultValue: 10 }),
+});
+
+export type VisTypeTimeseriesConfig = TypeOf;
diff --git a/src/plugins/vis_type_timeseries/server/index.ts b/src/plugins/vis_type_timeseries/server/index.ts
index fa74b6e965971..f460257caf5e3 100644
--- a/src/plugins/vis_type_timeseries/server/index.ts
+++ b/src/plugins/vis_type_timeseries/server/index.ts
@@ -17,18 +17,25 @@
* under the License.
*/
-import { schema, TypeOf } from '@kbn/config-schema';
-import { PluginInitializerContext } from 'src/core/server';
+import { PluginInitializerContext, PluginConfigDescriptor } from 'src/core/server';
+import { VisTypeTimeseriesConfig, config as configSchema } from './config';
import { VisTypeTimeseriesPlugin } from './plugin';
+
export { VisTypeTimeseriesSetup, Framework } from './plugin';
-export const config = {
- schema: schema.object({
- enabled: schema.boolean({ defaultValue: true }),
- }),
-};
+export const config: PluginConfigDescriptor = {
+ deprecations: ({ unused, renameFromRoot }) => [
+ // In Kibana v7.8 plugin id was renamed from 'metrics' to 'vis_type_timeseries':
+ renameFromRoot('metrics.enabled', 'vis_type_timeseries.enabled', true),
+ renameFromRoot('metrics.chartResolution', 'vis_type_timeseries.chartResolution', true),
+ renameFromRoot('metrics.minimumBucketSize', 'vis_type_timeseries.minimumBucketSize', true),
-export type VisTypeTimeseriesConfig = TypeOf;
+ // Unused properties which should be removed after releasing Kibana v8.0:
+ unused('chartResolution'),
+ unused('minimumBucketSize'),
+ ],
+ schema: configSchema,
+};
export { ValidationTelemetryServiceSetup } from './validation_telemetry';
diff --git a/src/plugins/vis_type_timeseries/server/plugin.ts b/src/plugins/vis_type_timeseries/server/plugin.ts
index 6ef6362c6e37b..05257cb79a75c 100644
--- a/src/plugins/vis_type_timeseries/server/plugin.ts
+++ b/src/plugins/vis_type_timeseries/server/plugin.ts
@@ -29,7 +29,7 @@ import {
} from 'src/core/server';
import { Observable } from 'rxjs';
import { Server } from 'hapi';
-import { VisTypeTimeseriesConfig } from '.';
+import { VisTypeTimeseriesConfig } from './config';
import { getVisData, GetVisData, GetVisDataOptions } from './lib/get_vis_data';
import { ValidationTelemetryService } from './validation_telemetry';
import { UsageCollectionSetup } from '../../usage_collection/server';
diff --git a/src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts b/src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts
index 77b49e824334f..f18fa1e4cc2fa 100644
--- a/src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts
+++ b/src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts
@@ -31,7 +31,7 @@ const resetCount: SavedObjectMigrationFn = doc => ({
export const tsvbTelemetrySavedObjectType: SavedObjectsType = {
name: 'tsvb-validation-telemetry',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
failedRequests: {
diff --git a/src/plugins/visualizations/public/index.scss b/src/plugins/visualizations/public/index.scss
index eada763b63c4d..2b61535f3e7f2 100644
--- a/src/plugins/visualizations/public/index.scss
+++ b/src/plugins/visualizations/public/index.scss
@@ -1,2 +1,3 @@
@import 'wizard/index';
@import 'embeddable/index';
+@import 'components/index';
diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts
index 9f4782f3ec730..cd2211c185530 100644
--- a/src/plugins/visualizations/server/saved_objects/visualization.ts
+++ b/src/plugins/visualizations/server/saved_objects/visualization.ts
@@ -23,7 +23,7 @@ import { visualizationSavedObjectTypeMigrations } from './visualization_migratio
export const visualizationSavedObjectType: SavedObjectsType = {
name: 'visualization',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
management: {
icon: 'visualizeApp',
defaultSearchField: 'title',
diff --git a/src/plugins/visualize/kibana.json b/src/plugins/visualize/kibana.json
new file mode 100644
index 0000000000000..a7afa0697a5eb
--- /dev/null
+++ b/src/plugins/visualize/kibana.json
@@ -0,0 +1,17 @@
+{
+ "id": "visualize",
+ "version": "kibana",
+ "server": false,
+ "ui": true,
+ "requiredPlugins": [
+ "data",
+ "kibanaLegacy",
+ "navigation",
+ "savedObjects",
+ "visualizations"
+ ],
+ "optionalPlugins": [
+ "home",
+ "share"
+ ]
+}
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts b/src/plugins/visualize/public/application/application.ts
similarity index 95%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
rename to src/plugins/visualize/public/application/application.ts
index 241397884c8fe..9d8a1b98ef023 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
+++ b/src/plugins/visualize/public/application/application.ts
@@ -17,16 +17,18 @@
* under the License.
*/
+import './index.scss';
+
import angular, { IModule } from 'angular';
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
import { AppMountContext } from 'kibana/public';
-import { configureAppAngularModule } from '../legacy_imports';
-import { NavigationPublicPluginStart as NavigationStart } from '../../../../../../plugins/navigation/public';
+import { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public';
import {
+ configureAppAngularModule,
createTopNavDirective,
createTopNavHelper,
-} from '../../../../../../plugins/kibana_legacy/public';
+} from '../../../kibana_legacy/public';
// @ts-ignore
import { initVisualizeApp } from './legacy_app';
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/breadcrumbs.ts b/src/plugins/visualize/public/application/breadcrumbs.ts
similarity index 86%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/breadcrumbs.ts
rename to src/plugins/visualize/public/application/breadcrumbs.ts
index b6a63d50b205b..972bdc1462b2c 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/breadcrumbs.ts
+++ b/src/plugins/visualize/public/application/breadcrumbs.ts
@@ -24,7 +24,7 @@ import { VisualizeConstants } from './visualize_constants';
export function getLandingBreadcrumbs() {
return [
{
- text: i18n.translate('kbn.visualize.listing.breadcrumb', {
+ text: i18n.translate('visualize.listing.breadcrumb', {
defaultMessage: 'Visualize',
}),
href: `#${VisualizeConstants.LANDING_PAGE_PATH}`,
@@ -36,7 +36,7 @@ export function getWizardStep1Breadcrumbs() {
return [
...getLandingBreadcrumbs(),
{
- text: i18n.translate('kbn.visualize.wizard.step1Breadcrumb', {
+ text: i18n.translate('visualize.wizard.step1Breadcrumb', {
defaultMessage: 'Create',
}),
},
@@ -47,7 +47,7 @@ export function getWizardStep2Breadcrumbs() {
return [
...getLandingBreadcrumbs(),
{
- text: i18n.translate('kbn.visualize.wizard.step2Breadcrumb', {
+ text: i18n.translate('visualize.wizard.step2Breadcrumb', {
defaultMessage: 'Create',
}),
},
@@ -58,7 +58,7 @@ export function getCreateBreadcrumbs() {
return [
...getLandingBreadcrumbs(),
{
- text: i18n.translate('kbn.visualize.editor.createBreadcrumb', {
+ text: i18n.translate('visualize.editor.createBreadcrumb', {
defaultMessage: 'Create',
}),
},
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/_editor.scss b/src/plugins/visualize/public/application/editor/_editor.scss
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/_editor.scss
rename to src/plugins/visualize/public/application/editor/_editor.scss
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/_index.scss b/src/plugins/visualize/public/application/editor/_index.scss
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/_index.scss
rename to src/plugins/visualize/public/application/editor/_index.scss
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.html b/src/plugins/visualize/public/application/editor/editor.html
similarity index 98%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.html
rename to src/plugins/visualize/public/application/editor/editor.html
index 0dcacd30fba4e..a031d70ef9a83 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.html
+++ b/src/plugins/visualize/public/application/editor/editor.html
@@ -80,7 +80,7 @@
) : null;
@@ -229,10 +220,10 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState
: []),
{
id: 'share',
- label: i18n.translate('kbn.topNavMenu.shareVisualizationButtonLabel', {
+ label: i18n.translate('visualize.topNavMenu.shareVisualizationButtonLabel', {
defaultMessage: 'share',
}),
- description: i18n.translate('kbn.visualize.topNavMenu.shareVisualizationButtonAriaLabel', {
+ description: i18n.translate('visualize.topNavMenu.shareVisualizationButtonAriaLabel', {
defaultMessage: 'Share Visualization',
}),
testId: 'shareTopNavButton',
@@ -252,13 +243,15 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState
isDirty: hasUnappliedChanges || hasUnsavedChanges,
});
},
+ // disable the Share button if no action specified
+ disableButton: !share,
},
{
id: 'inspector',
- label: i18n.translate('kbn.topNavMenu.openInspectorButtonLabel', {
+ label: i18n.translate('visualize.topNavMenu.openInspectorButtonLabel', {
defaultMessage: 'inspect',
}),
- description: i18n.translate('kbn.visualize.topNavMenu.openInspectorButtonAriaLabel', {
+ description: i18n.translate('visualize.topNavMenu.openInspectorButtonAriaLabel', {
defaultMessage: 'Open Inspector for visualization',
}),
testId: 'openInspectorButton',
@@ -274,23 +267,12 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState
},
tooltip() {
if (!embeddableHandler.hasInspector || !embeddableHandler.hasInspector()) {
- return i18n.translate('kbn.visualize.topNavMenu.openInspectorDisabledButtonTooltip', {
+ return i18n.translate('visualize.topNavMenu.openInspectorDisabledButtonTooltip', {
defaultMessage: `This visualization doesn't support any inspectors.`,
});
}
},
},
- {
- id: 'refresh',
- label: i18n.translate('kbn.topNavMenu.refreshButtonLabel', { defaultMessage: 'refresh' }),
- description: i18n.translate('kbn.visualize.topNavMenu.refreshButtonAriaLabel', {
- defaultMessage: 'Refresh',
- }),
- run: function() {
- embeddableHandler.reload();
- },
- testId: 'visualizeRefreshButton',
- },
];
if (savedVis.id) {
@@ -330,7 +312,7 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState
stopAllSyncing();
toastNotifications.addWarning({
- title: i18n.translate('kbn.visualize.visualizationTypeInvalidNotificationMessage', {
+ title: i18n.translate('visualize.visualizationTypeInvalidNotificationMessage', {
defaultMessage: 'Invalid visualization type',
}),
text: toMountPoint({error.message} ),
@@ -370,7 +352,7 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState
return;
}
- savedQueryService.getSavedQuery(savedQueryId).then(savedQuery => {
+ queryService.savedQueries.getSavedQuery(savedQueryId).then(savedQuery => {
$scope.$evalAsync(() => {
$scope.updateSavedQuery(savedQuery);
});
@@ -633,7 +615,7 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState
if (id) {
toastNotifications.addSuccess({
title: i18n.translate(
- 'kbn.visualize.topNavMenu.saveVisualization.successNotificationText',
+ 'visualize.topNavMenu.saveVisualization.successNotificationText',
{
defaultMessage: `Saved '{visTitle}'`,
values: {
@@ -672,15 +654,12 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState
// eslint-disable-next-line
console.error(error);
toastNotifications.addDanger({
- title: i18n.translate(
- 'kbn.visualize.topNavMenu.saveVisualization.failureNotificationText',
- {
- defaultMessage: `Error on saving '{visTitle}'`,
- values: {
- visTitle: savedVis.title,
- },
- }
- ),
+ title: i18n.translate('visualize.topNavMenu.saveVisualization.failureNotificationText', {
+ defaultMessage: `Error on saving '{visTitle}'`,
+ values: {
+ visTitle: savedVis.title,
+ },
+ }),
text: error.message,
'data-test-subj': 'saveVisualizationError',
});
@@ -703,7 +682,7 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState
});
toastNotifications.addSuccess(
- i18n.translate('kbn.visualize.linkedToSearch.unlinkSuccessNotificationText', {
+ i18n.translate('visualize.linkedToSearch.unlinkSuccessNotificationText', {
defaultMessage: `Unlinked from saved search '{searchTitle}'`,
values: {
searchTitle: savedSearch.title,
@@ -715,7 +694,7 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState
$scope.getAdditionalMessage = () => {
return (
' ' +
- i18n.translate('kbn.visualize.experimentalVisInfoText', {
+ i18n.translate('visualize.experimentalVisInfoText', {
defaultMessage: 'This visualization is marked as experimental.',
}) +
' ' +
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts b/src/plugins/visualize/public/application/editor/lib/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts
rename to src/plugins/visualize/public/application/editor/lib/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/make_stateful.ts b/src/plugins/visualize/public/application/editor/lib/make_stateful.ts
similarity index 91%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/make_stateful.ts
rename to src/plugins/visualize/public/application/editor/lib/make_stateful.ts
index 8384585108a59..d071df314d99c 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/make_stateful.ts
+++ b/src/plugins/visualize/public/application/editor/lib/make_stateful.ts
@@ -17,8 +17,8 @@
* under the License.
*/
-import { PersistedState } from '../../../../../../../../plugins/visualizations/public';
-import { ReduxLikeStateContainer } from '../../../../../../../../plugins/kibana_utils/public';
+import { PersistedState } from '../../../../../visualizations/public';
+import { ReduxLikeStateContainer } from '../../../../../kibana_utils/public';
import { VisualizeAppState, VisualizeAppStateTransitions } from '../../types';
/**
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/migrate_app_state.ts b/src/plugins/visualize/public/application/editor/lib/migrate_app_state.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/migrate_app_state.ts
rename to src/plugins/visualize/public/application/editor/lib/migrate_app_state.ts
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.test.ts b/src/plugins/visualize/public/application/editor/lib/url_helper.test.ts
similarity index 88%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.test.ts
rename to src/plugins/visualize/public/application/editor/lib/url_helper.test.ts
index e6974af9af832..09609e3d7e362 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.test.ts
+++ b/src/plugins/visualize/public/application/editor/lib/url_helper.test.ts
@@ -19,15 +19,6 @@
import { addEmbeddableToDashboardUrl } from './url_helper';
-jest.mock('../../../legacy_imports', () => ({
- DashboardConstants: {
- ADD_EMBEDDABLE_ID: 'addEmbeddableId',
- ADD_EMBEDDABLE_TYPE: 'addEmbeddableType',
- CREATE_NEW_DASHBOARD_URL: '/dashboard',
- },
- VISUALIZE_EMBEDDABLE_TYPE: 'visualization',
-}));
-
describe('', () => {
it('addEmbeddableToDashboardUrl when dashboard is not saved', () => {
const id = '123eb456cd';
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.ts b/src/plugins/visualize/public/application/editor/lib/url_helper.ts
similarity index 92%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.ts
rename to src/plugins/visualize/public/application/editor/lib/url_helper.ts
index c7937c856184a..84e1ef9687cd0 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/url_helper.ts
+++ b/src/plugins/visualize/public/application/editor/lib/url_helper.ts
@@ -18,7 +18,8 @@
*/
import { parseUrl, stringify } from 'query-string';
-import { DashboardConstants, VISUALIZE_EMBEDDABLE_TYPE } from '../../../legacy_imports';
+import { DashboardConstants } from '../../../../../dashboard/public';
+import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../visualizations/public';
/** *
* Returns relative dashboard URL with added embeddableType and embeddableId query params
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/visualize_app_state.ts b/src/plugins/visualize/public/application/editor/lib/visualize_app_state.ts
similarity index 98%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/visualize_app_state.ts
rename to src/plugins/visualize/public/application/editor/lib/visualize_app_state.ts
index 86f39ea76dd3a..fe2a19b7315c3 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/visualize_app_state.ts
+++ b/src/plugins/visualize/public/application/editor/lib/visualize_app_state.ts
@@ -24,7 +24,7 @@ import {
createStateContainer,
syncState,
IKbnUrlStateStorage,
-} from '../../../../../../../../plugins/kibana_utils/public';
+} from '../../../../../kibana_utils/public';
import { PureVisState, VisualizeAppState, VisualizeAppStateTransitions } from '../../types';
const STATE_STORAGE_KEY = '_a';
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js b/src/plugins/visualize/public/application/editor/visualization.js
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js
rename to src/plugins/visualize/public/application/editor/visualization.js
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js b/src/plugins/visualize/public/application/editor/visualization_editor.js
similarity index 98%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js
rename to src/plugins/visualize/public/application/editor/visualization_editor.js
index ef174dbaa5865..874b69532ec11 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js
+++ b/src/plugins/visualize/public/application/editor/visualization_editor.js
@@ -44,7 +44,6 @@ export function initVisEditorDirective(app, deps) {
editor.render({
core: deps.core,
data: deps.data,
- embeddable: deps.embeddable,
uiState: $scope.uiState,
timeRange: $scope.timeRange,
filters: $scope.filters,
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/help_menu/help_menu_util.js b/src/plugins/visualize/public/application/help_menu/help_menu_util.js
similarity index 94%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/help_menu/help_menu_util.js
rename to src/plugins/visualize/public/application/help_menu/help_menu_util.js
index 9c00947d7663c..c297326f2e264 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/help_menu/help_menu_util.js
+++ b/src/plugins/visualize/public/application/help_menu/help_menu_util.js
@@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n';
export function addHelpMenuToAppChrome(chrome, docLinks) {
chrome.setHelpExtension({
- appName: i18n.translate('kbn.visualize.helpMenu.appName', {
+ appName: i18n.translate('visualize.helpMenu.appName', {
defaultMessage: 'Visualize',
}),
links: [
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/_index.scss b/src/plugins/visualize/public/application/index.scss
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/_index.scss
rename to src/plugins/visualize/public/application/index.scss
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js b/src/plugins/visualize/public/application/legacy_app.js
similarity index 94%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js
rename to src/plugins/visualize/public/application/legacy_app.js
index a710d3e318749..7c5e3ce9408f0 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js
+++ b/src/plugins/visualize/public/application/legacy_app.js
@@ -25,7 +25,8 @@ import {
createKbnUrlStateStorage,
redirectWhenMissing,
ensureDefaultIndexPattern,
-} from '../../../../../../plugins/kibana_utils/public';
+} from '../../../kibana_utils/public';
+import { createSavedSearchesLoader } from '../../../discover/public';
import editorTemplate from './editor/editor.html';
import visualizeListingTemplate from './listing/visualize_listing.html';
@@ -40,7 +41,6 @@ import {
getCreateBreadcrumbs,
getEditBreadcrumbs,
} from './breadcrumbs';
-import { createSavedSearchesLoader } from '../../../../../../plugins/discover/public';
const getResolvedResults = deps => {
const { core, data, visualizations, createVisEmbeddableFromObject } = deps;
@@ -93,7 +93,7 @@ export function initVisualizeApp(app, deps) {
app.factory('kbnUrlStateStorage', history =>
createKbnUrlStateStorage({
history,
- useHash: deps.uiSettings.get('state:storeInSessionStorage'),
+ useHash: deps.core.uiSettings.get('state:storeInSessionStorage'),
})
);
@@ -107,10 +107,10 @@ export function initVisualizeApp(app, deps) {
}
return {
- text: i18n.translate('kbn.visualize.badge.readOnly.text', {
+ text: i18n.translate('visualize.badge.readOnly.text', {
defaultMessage: 'Read only',
}),
- tooltip: i18n.translate('kbn.visualize.badge.readOnly.tooltip', {
+ tooltip: i18n.translate('visualize.badge.readOnly.tooltip', {
defaultMessage: 'Unable to save visualizations',
}),
iconType: 'glasses',
@@ -156,7 +156,7 @@ export function initVisualizeApp(app, deps) {
if (shouldHaveIndex && !hasIndex) {
throw new Error(
i18n.translate(
- 'kbn.visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage',
+ 'visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage',
{
defaultMessage: 'You must provide either an indexPattern or a savedSearchId',
}
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/_index.scss b/src/plugins/visualize/public/application/listing/_index.scss
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/_index.scss
rename to src/plugins/visualize/public/application/listing/_index.scss
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/_listing.scss b/src/plugins/visualize/public/application/listing/_listing.scss
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/_listing.scss
rename to src/plugins/visualize/public/application/listing/_listing.scss
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.html b/src/plugins/visualize/public/application/listing/visualize_listing.html
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.html
rename to src/plugins/visualize/public/application/listing/visualize_listing.html
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js b/src/plugins/visualize/public/application/listing/visualize_listing.js
similarity index 93%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js
rename to src/plugins/visualize/public/application/listing/visualize_listing.js
index 098633d046062..900c17fa394de 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js
+++ b/src/plugins/visualize/public/application/listing/visualize_listing.js
@@ -24,7 +24,7 @@ import { VisualizeConstants } from '../visualize_constants';
import { i18n } from '@kbn/i18n';
import { getServices } from '../../kibana_services';
-import { syncQueryStateWithUrl } from '../../../../../../../plugins/data/public';
+import { syncQueryStateWithUrl } from '../../../../data/public';
export function initListingDirective(app, I18nContext) {
app.directive('visualizeListingTable', reactDirective =>
@@ -40,9 +40,8 @@ export function VisualizeListingController($scope, createNewVis, kbnUrlStateStor
savedVisualizations,
data: { query },
toastNotifications,
- uiSettings,
visualizations,
- core: { docLinks, savedObjects },
+ core: { docLinks, savedObjects, uiSettings },
} = getServices();
// syncs `_g` portion of url with query services
@@ -110,7 +109,7 @@ export function VisualizeListingController($scope, createNewVis, kbnUrlStateStor
})
).catch(error => {
toastNotifications.addError(error, {
- title: i18n.translate('kbn.visualize.visualizeListingDeleteErrorTitle', {
+ title: i18n.translate('visualize.visualizeListingDeleteErrorTitle', {
defaultMessage: 'Error deleting visualization',
}),
});
@@ -119,7 +118,7 @@ export function VisualizeListingController($scope, createNewVis, kbnUrlStateStor
chrome.setBreadcrumbs([
{
- text: i18n.translate('kbn.visualize.visualizeListingBreadcrumbsTitle', {
+ text: i18n.translate('visualize.visualizeListingBreadcrumbsTitle', {
defaultMessage: 'Visualize',
}),
},
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing_table.js b/src/plugins/visualize/public/application/listing/visualize_listing_table.js
similarity index 83%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing_table.js
rename to src/plugins/visualize/public/application/listing/visualize_listing_table.js
index 932ac8996e97e..100becdd3a80f 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing_table.js
+++ b/src/plugins/visualize/public/application/listing/visualize_listing_table.js
@@ -21,7 +21,7 @@ import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
-import { TableListView } from '../../../../../../../plugins/kibana_react/public';
+import { TableListView } from '../../../../kibana_react/public';
import { EuiIcon, EuiBetaBadge, EuiLink, EuiButton, EuiEmptyPrompt } from '@elastic/eui';
@@ -33,7 +33,7 @@ class VisualizeListingTable extends Component {
}
render() {
- const { visualizeCapabilities, uiSettings, toastNotifications } = getServices();
+ const { visualizeCapabilities, core, toastNotifications } = getServices();
return (
item.canDelete}
initialFilter={''}
noItemsFragment={this.getNoItemsMessage()}
- entityName={i18n.translate('kbn.visualize.listing.table.entityName', {
+ entityName={i18n.translate('visualize.listing.table.entityName', {
defaultMessage: 'visualization',
})}
- entityNamePlural={i18n.translate('kbn.visualize.listing.table.entityNamePlural', {
+ entityNamePlural={i18n.translate('visualize.listing.table.entityNamePlural', {
defaultMessage: 'visualizations',
})}
- tableListTitle={i18n.translate('kbn.visualize.listing.table.listTitle', {
+ tableListTitle={i18n.translate('visualize.listing.table.listTitle', {
defaultMessage: 'Visualizations',
})}
toastNotifications={toastNotifications}
- uiSettings={uiSettings}
+ uiSettings={core.uiSettings}
/>
);
}
@@ -67,7 +67,7 @@ class VisualizeListingTable extends Component {
const tableColumns = [
{
field: 'title',
- name: i18n.translate('kbn.visualize.listing.table.titleColumnName', {
+ name: i18n.translate('visualize.listing.table.titleColumnName', {
defaultMessage: 'Title',
}),
sortable: true,
@@ -82,7 +82,7 @@ class VisualizeListingTable extends Component {
},
{
field: 'typeTitle',
- name: i18n.translate('kbn.visualize.listing.table.typeColumnName', {
+ name: i18n.translate('visualize.listing.table.typeColumnName', {
defaultMessage: 'Type',
}),
sortable: true,
@@ -96,7 +96,7 @@ class VisualizeListingTable extends Component {
},
{
field: 'description',
- name: i18n.translate('kbn.dashboard.listing.table.descriptionColumnName', {
+ name: i18n.translate('visualize.listing.table.descriptionColumnName', {
defaultMessage: 'Description',
}),
sortable: true,
@@ -116,7 +116,7 @@ class VisualizeListingTable extends Component {
title={
@@ -133,7 +133,7 @@ class VisualizeListingTable extends Component {
title={
@@ -142,7 +142,7 @@ class VisualizeListingTable extends Component {
@@ -156,7 +156,7 @@ class VisualizeListingTable extends Component {
data-test-subj="createVisualizationPromptButton"
>
@@ -192,10 +192,10 @@ class VisualizeListingTable extends Component {
{
return new VisualizePlugin(context);
};
diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/plugins/visualize/public/kibana_services.ts
similarity index 68%
rename from src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts
rename to src/plugins/visualize/public/kibana_services.ts
index d5440c4677d8a..765e9a82ae899 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts
+++ b/src/plugins/visualize/public/kibana_services.ts
@@ -22,21 +22,18 @@ import {
CoreStart,
SavedObjectsClientContract,
ToastsStart,
- IUiSettingsClient,
- I18nStart,
PluginInitializerContext,
+ I18nStart,
} from 'kibana/public';
-import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public';
-import { Storage } from '../../../../../plugins/kibana_utils/public';
-import { EmbeddableStart } from '../../../../../plugins/embeddable/public';
-import { SharePluginStart } from '../../../../../plugins/share/public';
-import { DataPublicPluginStart, IndexPatternsContract } from '../../../../../plugins/data/public';
-import { VisualizationsStart } from '../../../../../plugins/visualizations/public';
-import { SavedVisualizations } from './np_ready/types';
-import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public';
-import { KibanaLegacyStart } from '../../../../../plugins/kibana_legacy/public';
-import { DefaultEditorController } from '../../../../../plugins/vis_default_editor/public';
+import { NavigationPublicPluginStart as NavigationStart } from '../../navigation/public';
+import { Storage } from '../../kibana_utils/public';
+import { SharePluginStart } from '../../share/public';
+import { DataPublicPluginStart } from '../../data/public';
+import { VisualizationsStart } from '../../visualizations/public';
+import { SavedVisualizations } from './application/types';
+import { KibanaLegacyStart } from '../../kibana_legacy/public';
+import { DefaultEditorController } from '../../vis_default_editor/public';
export interface VisualizeKibanaServices {
pluginInitializerContext: PluginInitializerContext;
@@ -44,20 +41,15 @@ export interface VisualizeKibanaServices {
chrome: ChromeStart;
core: CoreStart;
data: DataPublicPluginStart;
- embeddable: EmbeddableStart;
- indexPatterns: IndexPatternsContract;
localStorage: Storage;
navigation: NavigationStart;
toastNotifications: ToastsStart;
savedObjectsClient: SavedObjectsClientContract;
- savedQueryService: DataPublicPluginStart['query']['savedQueries'];
savedVisualizations: SavedVisualizations;
- share: SharePluginStart;
- uiSettings: IUiSettingsClient;
+ share?: SharePluginStart;
config: KibanaLegacyStart['config'];
visualizeCapabilities: any;
visualizations: VisualizationsStart;
- usageCollection?: UsageCollectionSetup;
I18nContext: I18nStart['Context'];
setActiveUrl: (newUrl: string) => void;
DefaultVisualizationEditor: typeof DefaultEditorController;
diff --git a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts b/src/plugins/visualize/public/plugin.ts
similarity index 53%
rename from src/legacy/core_plugins/kibana/public/visualize/plugin.ts
rename to src/plugins/visualize/public/plugin.ts
index 4ffbc307c69a8..ab64e083a553d 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts
+++ b/src/plugins/visualize/public/plugin.ts
@@ -27,64 +27,43 @@ import {
CoreStart,
Plugin,
PluginInitializerContext,
- SavedObjectsClientContract,
} from 'kibana/public';
-import { Storage, createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public';
-import {
- DataPublicPluginStart,
- DataPublicPluginSetup,
- esFilters,
-} from '../../../../../plugins/data/public';
-import { EmbeddableStart } from '../../../../../plugins/embeddable/public';
-import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public';
-import { SharePluginStart } from '../../../../../plugins/share/public';
-import {
- KibanaLegacySetup,
- AngularRenderedAppUpdater,
-} from '../../../../../plugins/kibana_legacy/public';
-import { VisualizationsStart } from '../../../../../plugins/visualizations/public';
-import { VisualizeConstants } from './np_ready/visualize_constants';
+import { Storage, createKbnUrlTracker } from '../../kibana_utils/public';
+import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public';
+import { NavigationPublicPluginStart as NavigationStart } from '../../navigation/public';
+import { SharePluginStart } from '../../share/public';
+import { KibanaLegacySetup, AngularRenderedAppUpdater } from '../../kibana_legacy/public';
+import { VisualizationsStart } from '../../visualizations/public';
+import { VisualizeConstants } from './application/visualize_constants';
import { setServices, VisualizeKibanaServices } from './kibana_services';
-import {
- FeatureCatalogueCategory,
- HomePublicPluginSetup,
-} from '../../../../../plugins/home/public';
-import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public';
-import { DefaultEditorController } from '../../../../../plugins/vis_default_editor/public';
+import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/public';
+import { DefaultEditorController } from '../../vis_default_editor/public';
export interface VisualizePluginStartDependencies {
data: DataPublicPluginStart;
- embeddable: EmbeddableStart;
navigation: NavigationStart;
- share: SharePluginStart;
+ share?: SharePluginStart;
visualizations: VisualizationsStart;
}
export interface VisualizePluginSetupDependencies {
- home: HomePublicPluginSetup;
+ home?: HomePublicPluginSetup;
kibanaLegacy: KibanaLegacySetup;
- usageCollection?: UsageCollectionSetup;
data: DataPublicPluginSetup;
}
-export class VisualizePlugin implements Plugin {
- private startDependencies: {
- data: DataPublicPluginStart;
- embeddable: EmbeddableStart;
- navigation: NavigationStart;
- savedObjectsClient: SavedObjectsClientContract;
- share: SharePluginStart;
- visualizations: VisualizationsStart;
- } | null = null;
+export class VisualizePlugin
+ implements
+ Plugin {
private appStateUpdater = new BehaviorSubject(() => ({}));
private stopUrlTracking: (() => void) | undefined = undefined;
constructor(private initializerContext: PluginInitializerContext) {}
public async setup(
- core: CoreSetup,
- { home, kibanaLegacy, usageCollection, data }: VisualizePluginSetupDependencies
+ core: CoreSetup,
+ { home, kibanaLegacy, data }: VisualizePluginSetupDependencies
) {
const { appMounted, appUnMounted, stop: stopUrlTracker, setActiveUrl } = createKbnUrlTracker({
baseUrl: core.http.basePath.prepend('/app/kibana'),
@@ -117,50 +96,34 @@ export class VisualizePlugin implements Plugin {
updater$: this.appStateUpdater.asObservable(),
navLinkId: 'kibana:visualize',
mount: async (params: AppMountParameters) => {
- const [coreStart] = await core.getStartServices();
-
- if (this.startDependencies === null) {
- throw new Error('not started yet');
- }
+ const [coreStart, pluginsStart] = await core.getStartServices();
appMounted();
- const {
- savedObjectsClient,
- embeddable,
- navigation,
- visualizations,
- data: dataStart,
- share,
- } = this.startDependencies;
const deps: VisualizeKibanaServices = {
pluginInitializerContext: this.initializerContext,
addBasePath: coreStart.http.basePath.prepend,
core: coreStart,
+ config: kibanaLegacy.config,
chrome: coreStart.chrome,
- data: dataStart,
- embeddable,
- indexPatterns: dataStart.indexPatterns,
+ data: pluginsStart.data,
localStorage: new Storage(localStorage),
- navigation,
- savedObjectsClient,
- savedVisualizations: visualizations.savedVisualizationsLoader,
- savedQueryService: dataStart.query.savedQueries,
- share,
+ navigation: pluginsStart.navigation,
+ savedObjectsClient: coreStart.savedObjects.client,
+ savedVisualizations: pluginsStart.visualizations.savedVisualizationsLoader,
+ share: pluginsStart.share,
toastNotifications: coreStart.notifications.toasts,
- uiSettings: coreStart.uiSettings,
- config: kibanaLegacy.config,
visualizeCapabilities: coreStart.application.capabilities.visualize,
- visualizations,
- usageCollection,
+ visualizations: pluginsStart.visualizations,
I18nContext: coreStart.i18n.Context,
setActiveUrl,
DefaultVisualizationEditor: DefaultEditorController,
- createVisEmbeddableFromObject: visualizations.__LEGACY.createVisEmbeddableFromObject,
+ createVisEmbeddableFromObject:
+ pluginsStart.visualizations.__LEGACY.createVisEmbeddableFromObject,
};
setServices(deps);
- const { renderApp } = await import('./np_ready/application');
+ const { renderApp } = await import('./application/application');
const unmount = renderApp(params.element, params.appBasePath, deps);
return () => {
unmount();
@@ -169,33 +132,23 @@ export class VisualizePlugin implements Plugin {
},
});
- home.featureCatalogue.register({
- id: 'visualize',
- title: 'Visualize',
- description: i18n.translate('kbn.visualize.visualizeDescription', {
- defaultMessage:
- 'Create visualizations and aggregate data stores in your Elasticsearch indices.',
- }),
- icon: 'visualizeApp',
- path: `/app/kibana#${VisualizeConstants.LANDING_PAGE_PATH}`,
- showOnHomePage: true,
- category: FeatureCatalogueCategory.DATA,
- });
+ if (home) {
+ home.featureCatalogue.register({
+ id: 'visualize',
+ title: 'Visualize',
+ description: i18n.translate('visualize.visualizeDescription', {
+ defaultMessage:
+ 'Create visualizations and aggregate data stores in your Elasticsearch indices.',
+ }),
+ icon: 'visualizeApp',
+ path: `/app/kibana#${VisualizeConstants.LANDING_PAGE_PATH}`,
+ showOnHomePage: true,
+ category: FeatureCatalogueCategory.DATA,
+ });
+ }
}
- public start(
- core: CoreStart,
- { embeddable, navigation, data, share, visualizations }: VisualizePluginStartDependencies
- ) {
- this.startDependencies = {
- data,
- embeddable,
- navigation,
- savedObjectsClient: core.savedObjects.client,
- share,
- visualizations,
- };
- }
+ public start(core: CoreStart, plugins: VisualizePluginStartDependencies) {}
stop() {
if (this.stopUrlTracking) {
diff --git a/test/api_integration/apis/saved_objects/migrations.js b/test/api_integration/apis/saved_objects/migrations.js
index bd207ccb41b20..9d908b95ca575 100644
--- a/test/api_integration/apis/saved_objects/migrations.js
+++ b/test/api_integration/apis/saved_objects/migrations.js
@@ -383,7 +383,7 @@ function migrationsToTypes(migrations) {
return Object.entries(migrations).map(([type, migrations]) => ({
name: type,
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
mappings: { properties: {} },
migrations: { ...migrations },
}));
diff --git a/test/functional/apps/dashboard/create_and_add_embeddables.js b/test/functional/apps/dashboard/create_and_add_embeddables.js
index 3ce8e353e61fc..410acdcb5680d 100644
--- a/test/functional/apps/dashboard/create_and_add_embeddables.js
+++ b/test/functional/apps/dashboard/create_and_add_embeddables.js
@@ -19,7 +19,7 @@
import expect from '@kbn/expect';
-import { VisualizeConstants } from '../../../../src/legacy/core_plugins/kibana/public/visualize/np_ready/visualize_constants';
+import { VisualizeConstants } from '../../../../src/plugins/visualize/public/application/visualize_constants';
export default function({ getService, getPageObjects }) {
const retry = getService('retry');
diff --git a/test/functional/apps/dashboard/panel_controls.js b/test/functional/apps/dashboard/panel_controls.js
index f30f58913bd97..52c4a11360355 100644
--- a/test/functional/apps/dashboard/panel_controls.js
+++ b/test/functional/apps/dashboard/panel_controls.js
@@ -24,7 +24,7 @@ import {
AREA_CHART_VIS_NAME,
LINE_CHART_VIS_NAME,
} from '../../page_objects/dashboard_page';
-import { VisualizeConstants } from '../../../../src/legacy/core_plugins/kibana/public/visualize/np_ready/visualize_constants';
+import { VisualizeConstants } from '../../../../src/plugins/visualize/public/application/visualize_constants';
export default function({ getService, getPageObjects }) {
const browser = getService('browser');
diff --git a/test/functional/config.edge.js b/test/functional/config.edge.js
new file mode 100644
index 0000000000000..ed68b41e8c89a
--- /dev/null
+++ b/test/functional/config.edge.js
@@ -0,0 +1,34 @@
+/*
+ * 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 default async function({ readConfigFile }) {
+ const defaultConfig = await readConfigFile(require.resolve('./config'));
+
+ return {
+ ...defaultConfig.getAll(),
+
+ browser: {
+ type: 'msedge',
+ },
+
+ junit: {
+ reportName: 'MS Chromium Edge UI Functional Tests',
+ },
+ };
+}
diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts
index 3b63fa68d71ee..220c2d8f6b363 100644
--- a/test/functional/page_objects/visualize_page.ts
+++ b/test/functional/page_objects/visualize_page.ts
@@ -18,7 +18,7 @@
*/
import { FtrProviderContext } from '../ftr_provider_context';
-import { VisualizeConstants } from '../../../src/legacy/core_plugins/kibana/public/visualize/np_ready/visualize_constants';
+import { VisualizeConstants } from '../../../src/plugins/visualize/public/application/visualize_constants';
export function VisualizePageProvider({ getService, getPageObjects }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts
index 5017947e95d03..13d2365c07191 100644
--- a/test/functional/services/browser.ts
+++ b/test/functional/services/browser.ts
@@ -47,7 +47,9 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
*/
public readonly browserType: string = browserType;
- public readonly isChrome: boolean = browserType === Browsers.Chrome;
+ public readonly isChromium: boolean = [Browsers.Chrome, Browsers.ChromiumEdge].includes(
+ browserType
+ );
public readonly isFirefox: boolean = browserType === Browsers.Firefox;
diff --git a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts
index 157918df874c8..8b57ecd3c8235 100644
--- a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts
+++ b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts
@@ -55,6 +55,7 @@ export class WebElementWrapper {
private driver: WebDriver = this.webDriver.driver;
private Keys = Key;
public isW3CEnabled: boolean = (this.webDriver.driver as any).executor_.w3c === true;
+ public isChromium: boolean = [Browsers.Chrome, Browsers.ChromiumEdge].includes(this.browserType);
public static create(
webElement: WebElement | WebElementWrapper,
@@ -63,7 +64,7 @@ export class WebElementWrapper {
timeout: number,
fixedHeaderHeight: number,
logger: ToolingLog,
- browserType: string
+ browserType: Browsers
): WebElementWrapper {
if (webElement instanceof WebElementWrapper) {
return webElement;
@@ -87,7 +88,7 @@ export class WebElementWrapper {
private timeout: number,
private fixedHeaderHeight: number,
private logger: ToolingLog,
- private browserType: string
+ private browserType: Browsers
) {}
private async _findWithCustomTimeout(
@@ -243,7 +244,7 @@ export class WebElementWrapper {
return this.clearValueWithKeyboard();
}
await this.retryCall(async function clearValue(wrapper) {
- if (wrapper.browserType === Browsers.Chrome || options.withJS) {
+ if (wrapper.isChromium || options.withJS) {
// https://bugs.chromium.org/p/chromedriver/issues/detail?id=2702
await wrapper.driver.executeScript(`arguments[0].value=''`, wrapper._webElement);
} else {
@@ -275,7 +276,7 @@ export class WebElementWrapper {
await delay(100);
}
} else {
- if (this.browserType === Browsers.Chrome) {
+ if (this.isChromium) {
// https://bugs.chromium.org/p/chromedriver/issues/detail?id=30
await this.retryCall(async function clearValueWithKeyboard(wrapper) {
await wrapper.driver.executeScript(`arguments[0].select();`, wrapper._webElement);
diff --git a/test/functional/services/remote/browsers.ts b/test/functional/services/remote/browsers.ts
index 46d81f1737a55..aa6e364d0a09d 100644
--- a/test/functional/services/remote/browsers.ts
+++ b/test/functional/services/remote/browsers.ts
@@ -21,4 +21,5 @@ export enum Browsers {
Chrome = 'chrome',
Firefox = 'firefox',
InternetExplorer = 'ie',
+ ChromiumEdge = 'msedge',
}
diff --git a/test/functional/services/remote/remote.ts b/test/functional/services/remote/remote.ts
index e571a1a7e5551..933b08f7681e8 100644
--- a/test/functional/services/remote/remote.ts
+++ b/test/functional/services/remote/remote.ts
@@ -69,13 +69,15 @@ export async function RemoteProvider({ getService }: FtrProviderContext) {
const caps = await driver.getCapabilities();
const browserVersion = caps.get(isW3CEnabled ? 'browserVersion' : 'version');
- log.info(`Remote initialized: ${caps.get('browserName')} ${browserVersion}`);
+ log.info(
+ `Remote initialized: ${caps.get(
+ 'browserName'
+ )} ${browserVersion}, w3c compliance=${isW3CEnabled}, collectingCoverage=${collectCoverage}`
+ );
- if (browserType === Browsers.Chrome) {
+ if ([Browsers.Chrome, Browsers.ChromiumEdge].includes(browserType)) {
log.info(
- `Chromedriver version: ${
- caps.get('chrome').chromedriverVersion
- }, w3c=${isW3CEnabled}, codeCoverage=${collectCoverage}`
+ `${browserType}driver version: ${caps.get(browserType)[`${browserType}driverVersion`]}`
);
}
diff --git a/test/functional/services/remote/webdriver.ts b/test/functional/services/remote/webdriver.ts
index 3bf5b865aa7ba..1b7ef2c1855d0 100644
--- a/test/functional/services/remote/webdriver.ts
+++ b/test/functional/services/remote/webdriver.ts
@@ -30,10 +30,12 @@ import geckoDriver from 'geckodriver';
import { Builder, Capabilities, By, logging, until } from 'selenium-webdriver';
import chrome from 'selenium-webdriver/chrome';
import firefox from 'selenium-webdriver/firefox';
+import edge from 'selenium-webdriver/edge';
// @ts-ignore internal modules are not typed
import { Executor } from 'selenium-webdriver/lib/http';
// @ts-ignore internal modules are not typed
import { getLogger } from 'selenium-webdriver/lib/logging';
+import { installDriver } from 'ms-chromium-edge-driver';
import { pollForLogEntry$ } from './poll_for_log_entry';
import { createStdoutSocket } from './create_stdout_stream';
@@ -63,6 +65,7 @@ Executor.prototype.execute = preventParallelCalls(
);
let attemptCounter = 0;
+let edgePaths: { driverPath: string | undefined; browserPath: string | undefined };
async function attemptToCreateCommand(
log: ToolingLog,
browserType: Browsers,
@@ -136,6 +139,46 @@ async function attemptToCreateCommand(
};
}
+ case 'msedge': {
+ if (edgePaths && edgePaths.browserPath && edgePaths.driverPath) {
+ const edgeOptions = new edge.Options();
+ if (headlessBrowser === '1') {
+ // @ts-ignore internal modules are not typed
+ edgeOptions.headless();
+ }
+ // @ts-ignore internal modules are not typed
+ edgeOptions.setEdgeChromium(true);
+ // @ts-ignore internal modules are not typed
+ edgeOptions.setBinaryPath(edgePaths.browserPath);
+ const session = await new Builder()
+ .forBrowser('MicrosoftEdge')
+ .setEdgeOptions(edgeOptions)
+ .setEdgeService(new edge.ServiceBuilder(edgePaths.driverPath))
+ .build();
+ return {
+ session,
+ consoleLog$: pollForLogEntry$(
+ session,
+ logging.Type.BROWSER,
+ logPollingMs,
+ lifecycle.cleanup.after$
+ ).pipe(
+ takeUntil(lifecycle.cleanup.after$),
+ map(({ message, level: { name: level } }) => ({
+ message: message.replace(/\\n/g, '\n'),
+ level,
+ }))
+ ),
+ };
+ } else {
+ throw new Error(
+ `Chromium Edge session requires browser or driver path to be defined: ${JSON.stringify(
+ edgePaths
+ )}`
+ );
+ }
+ }
+
case 'firefox': {
const firefoxOptions = new firefox.Options();
// Firefox 65+ supports logging console output to stdout
@@ -265,6 +308,11 @@ export async function initWebDriver(
log.verbose(entry.message);
});
+ // download Edge driver only in case of usage
+ if (browserType === Browsers.ChromiumEdge) {
+ edgePaths = await installDriver();
+ }
+
return await Promise.race([
(async () => {
await delay(2 * MINUTE);
diff --git a/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts
index 3f6a8e8773e04..f0b1cde24c6fe 100644
--- a/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts
+++ b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts
@@ -17,15 +17,13 @@
* under the License.
*/
-import { Plugin, CoreSetup, IRenderOptions } from 'kibana/server';
+import { Plugin, CoreSetup } from 'kibana/server';
import { schema } from '@kbn/config-schema';
export class RenderingPlugin implements Plugin {
public setup(core: CoreSetup) {
- const router = core.http.createRouter();
-
- router.get(
+ core.http.resources.register(
{
path: '/render/{id}',
validate: {
@@ -41,18 +39,12 @@ export class RenderingPlugin implements Plugin {
},
},
async (context, req, res) => {
- const { id } = req.params;
const { includeUserSettings } = req.query;
- const app = { getId: () => id! };
- const options: Partial = { app, includeUserSettings };
- const body = await context.core.rendering.render(options);
- return res.ok({
- body,
- headers: {
- 'content-security-policy': core.http.csp.header,
- },
- });
+ if (includeUserSettings) {
+ return res.renderCoreApp();
+ }
+ return res.renderAnonymousCoreApp();
}
);
}
diff --git a/test/scripts/jenkins_ci_group.sh b/test/scripts/jenkins_ci_group.sh
index 23807a6e98dc2..c77ffe40db553 100755
--- a/test/scripts/jenkins_ci_group.sh
+++ b/test/scripts/jenkins_ci_group.sh
@@ -22,7 +22,7 @@ else
echo " -> running tests from the clone folder"
#yarn run grunt "run:functionalTests_ciGroup${CI_GROUP}";
- node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --config test/functional/config.coverage.js || true;
+ node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --exclude-tag "skipCoverage" || true;
if [[ -d target/kibana-coverage/functional ]]; then
echo " -> replacing kibana${CI_GROUP} with kibana in json files"
diff --git a/test/scripts/jenkins_xpack.sh b/test/scripts/jenkins_xpack.sh
index 5055997df642a..7070fb1063cb5 100755
--- a/test/scripts/jenkins_xpack.sh
+++ b/test/scripts/jenkins_xpack.sh
@@ -39,7 +39,7 @@ else
# build runtime for canvas
echo "NODE_ENV=$NODE_ENV"
node ./legacy/plugins/canvas/scripts/shareable_runtime
- node scripts/jest --ci --verbose --coverage
+ node --max-old-space-size=6144 scripts/jest --ci --verbose --coverage
# rename file in order to be unique one
test -f ../target/kibana-coverage/jest/coverage-final.json \
&& mv ../target/kibana-coverage/jest/coverage-final.json \
diff --git a/test/scripts/jenkins_xpack_ci_group.sh b/test/scripts/jenkins_xpack_ci_group.sh
index 01b13293c10ba..a6e600630364e 100755
--- a/test/scripts/jenkins_xpack_ci_group.sh
+++ b/test/scripts/jenkins_xpack_ci_group.sh
@@ -23,7 +23,7 @@ else
cd "kibana${CI_GROUP}/x-pack"
echo " -> running tests from the clone folder"
- node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --config test/functional/config.coverage.js || true;
+ node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --exclude-tag "skipCoverage" || true;
if [[ -d ../target/kibana-coverage/functional ]]; then
echo " -> replacing kibana${CI_GROUP} with kibana in json files"
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx
index a1462c7637358..cc5c62e25b491 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx
@@ -57,7 +57,8 @@ export class MachineLearningFlyout extends Component {
};
public addErrorToast = () => {
- const core = this.context;
+ const { core } = this.context;
+
const { urlParams } = this.props;
const { serviceName } = urlParams;
diff --git a/x-pack/legacy/plugins/apm/public/services/rest/ml.ts b/x-pack/legacy/plugins/apm/public/services/rest/ml.ts
index 1c618098b36e3..0cd1bdf907531 100644
--- a/x-pack/legacy/plugins/apm/public/services/rest/ml.ts
+++ b/x-pack/legacy/plugins/apm/public/services/rest/ml.ts
@@ -12,7 +12,8 @@ import {
} from '../../../../../../plugins/apm/common/elasticsearch_fieldnames';
import {
getMlJobId,
- getMlPrefix
+ getMlPrefix,
+ encodeForMlApi
} from '../../../../../../plugins/apm/common/ml_job_constants';
import { callApi } from './callApi';
import { ESFilter } from '../../../../../../plugins/apm/typings/elasticsearch';
@@ -53,13 +54,16 @@ export async function startMLJob({
http: HttpSetup;
}) {
const transactionIndices = await getTransactionIndices(http);
- const groups = ['apm', serviceName.toLowerCase()];
+ const groups = [
+ 'apm',
+ encodeForMlApi(serviceName),
+ encodeForMlApi(transactionType)
+ ];
const filter: ESFilter[] = [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [PROCESSOR_EVENT]: 'transaction' } },
{ term: { [TRANSACTION_TYPE]: transactionType } }
];
- groups.push(transactionType.toLowerCase());
return callApi(http, {
method: 'POST',
pathname: `/api/ml/modules/setup/apm_transaction`,
diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js
index 1a7f478d3bbad..f4e01efc05f45 100644
--- a/x-pack/legacy/plugins/maps/index.js
+++ b/x-pack/legacy/plugins/maps/index.js
@@ -38,6 +38,7 @@ export function maps(kibana) {
return {
showMapVisualizationTypes: serverConfig.get('xpack.maps.showMapVisualizationTypes'),
showMapsInspectorAdapter: serverConfig.get('xpack.maps.showMapsInspectorAdapter'),
+ enableVectorTiles: serverConfig.get('xpack.maps.enableVectorTiles'),
preserveDrawingBuffer: serverConfig.get('xpack.maps.preserveDrawingBuffer'),
isEmsEnabled: mapConfig.includeElasticMapsService,
emsFontLibraryUrl: mapConfig.emsFontLibraryUrl,
@@ -85,6 +86,7 @@ export function maps(kibana) {
showMapVisualizationTypes: Joi.boolean().default(false),
showMapsInspectorAdapter: Joi.boolean().default(false), // flag used in functional testing
preserveDrawingBuffer: Joi.boolean().default(false), // flag used in functional testing
+ enableVectorTiles: Joi.boolean().default(false), // flag used to enable/disable vector-tiles
}).default();
},
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/view.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/view.js
index a54df69471aa0..92fcf01f3901f 100644
--- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/view.js
+++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/view.js
@@ -63,13 +63,13 @@ export class AddLayerPanel extends Component {
return;
}
- const style =
+ const styleDescriptor =
this.state.layer && this.state.layer.getCurrentStyle()
? this.state.layer.getCurrentStyle().getDescriptor()
: null;
const layerInitProps = {
...options,
- style: style,
+ style: styleDescriptor,
};
const newLayer = source.createDefaultLayer(layerInitProps, this.props.mapColors);
if (!this._isMounted) {
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js
index 73c98db8e429d..e8f980bbbf2b4 100644
--- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js
+++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js
@@ -13,10 +13,13 @@ import {
updateLayerMinZoom,
updateLayerAlpha,
} from '../../../actions/map_actions';
+import { MAX_ZOOM } from '../../../../../../../plugins/maps/common/constants';
function mapStateToProps(state = {}) {
const selectedLayer = getSelectedLayer(state);
return {
+ minVisibilityZoom: selectedLayer.getMinSourceZoom(),
+ maxVisibilityZoom: MAX_ZOOM,
alpha: selectedLayer.getAlpha(),
label: selectedLayer.getLabel(),
layerId: selectedLayer.getId(),
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js
index bd27450943638..1d352913e54a3 100644
--- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js
+++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js
@@ -13,8 +13,6 @@ import { ValidatedRange } from '../../../../../../../plugins/maps/public/compone
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { ValidatedDualRange } from '../../../../../../../../src/plugins/kibana_react/public';
-import { MAX_ZOOM, MIN_ZOOM } from '../../../../common/constants';
-
export function LayerSettings(props) {
const onLabelChange = event => {
const label = event.target.value;
@@ -22,8 +20,8 @@ export function LayerSettings(props) {
};
const onZoomChange = ([min, max]) => {
- props.updateMinZoom(props.layerId, Math.max(MIN_ZOOM, parseInt(min, 10)));
- props.updateMaxZoom(props.layerId, Math.min(MAX_ZOOM, parseInt(max, 10)));
+ props.updateMinZoom(props.layerId, Math.max(props.minVisibilityZoom, parseInt(min, 10)));
+ props.updateMaxZoom(props.layerId, Math.min(props.maxVisibilityZoom, parseInt(max, 10)));
};
const onAlphaChange = alpha => {
@@ -38,8 +36,8 @@ export function LayerSettings(props) {
defaultMessage: 'Visibility',
})}
formRowDisplay="columnCompressed"
- min={MIN_ZOOM}
- max={MAX_ZOOM}
+ min={props.minVisibilityZoom}
+ max={props.maxVisibilityZoom}
value={[props.minZoom, props.maxZoom]}
showInput="inputWithPopover"
showRange
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js
index 1fe3d0d493ee7..a36e1d7048e92 100644
--- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js
+++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js
@@ -14,7 +14,12 @@ import {
} from './utils';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getGlyphUrl, isRetina } from '../../../../../../../plugins/maps/public/meta';
-import { DECIMAL_DEGREES_PRECISION, ZOOM_PRECISION } from '../../../../common/constants';
+import {
+ DECIMAL_DEGREES_PRECISION,
+ MAX_ZOOM,
+ MIN_ZOOM,
+ ZOOM_PRECISION,
+} from '../../../../common/constants';
import mapboxgl from 'mapbox-gl/dist/mapbox-gl-csp';
import mbWorkerUrl from '!!file-loader!mapbox-gl/dist/mapbox-gl-csp-worker';
import mbRtlPlugin from '!!file-loader!@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js';
@@ -132,6 +137,8 @@ export class MBMapContainer extends React.Component {
scrollZoom: this.props.scrollZoom,
preserveDrawingBuffer: getInjectedVarFunc()('preserveDrawingBuffer', false),
interactive: !this.props.disableInteractive,
+ minZoom: MIN_ZOOM,
+ maxZoom: MAX_ZOOM,
};
const initialView = _.get(this.props.goto, 'center');
if (initialView) {
diff --git a/x-pack/legacy/plugins/maps/public/plugin.ts b/x-pack/legacy/plugins/maps/public/plugin.ts
index 4ec068ff44029..71f1a30c1fbef 100644
--- a/x-pack/legacy/plugins/maps/public/plugin.ts
+++ b/x-pack/legacy/plugins/maps/public/plugin.ts
@@ -4,13 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../plugins/maps/public/layers/layer_wizard_registry';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../plugins/maps/public/layers/sources/source_registry';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../plugins/maps/public/layers/load_layer_wizards';
-
import { Plugin, CoreStart, CoreSetup } from 'src/core/public';
// @ts-ignore
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js
index f350a2c944756..1e71025935519 100644
--- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js
+++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js
@@ -19,6 +19,8 @@ import { BlendedVectorLayer } from '../../../../../plugins/maps/public/layers/bl
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getTimeFilter } from '../../../../../plugins/maps/public/kibana_services';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { TiledVectorLayer } from '../../../../../plugins/maps/public/layers/tiled_vector_layer';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getInspectorAdapters } from '../../../../../plugins/maps/public/reducers/non_serializable_instances';
import {
copyPersistentState,
@@ -51,6 +53,8 @@ function createLayerInstance(layerDescriptor, inspectorAdapters) {
return new HeatmapLayer({ layerDescriptor, source });
case BlendedVectorLayer.type:
return new BlendedVectorLayer({ layerDescriptor, source });
+ case TiledVectorLayer.type:
+ return new TiledVectorLayer({ layerDescriptor, source });
default:
throw new Error(`Unrecognized layerType ${layerDescriptor.type}`);
}
diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js
index b83be301653b8..72cc748617540 100644
--- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js
+++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js
@@ -5,6 +5,7 @@
*/
jest.mock('../../../../../plugins/maps/public/layers/vector_layer', () => {});
+jest.mock('../../../../../plugins/maps/public/layers/tiled_vector_layer', () => {});
jest.mock('../../../../../plugins/maps/public/layers/blended_vector_layer', () => {});
jest.mock('../../../../../plugins/maps/public/layers/heatmap_layer', () => {});
jest.mock('../../../../../plugins/maps/public/layers/vector_tile_layer', () => {});
diff --git a/x-pack/legacy/plugins/rollup/index.ts b/x-pack/legacy/plugins/rollup/index.ts
index 2c8363cc397f4..621667f3618b2 100644
--- a/x-pack/legacy/plugins/rollup/index.ts
+++ b/x-pack/legacy/plugins/rollup/index.ts
@@ -40,7 +40,7 @@ export function rollup(kibana: any) {
},
init(server: any) {
const { core: coreSetup, plugins } = server.newPlatform.setup;
- const { usageCollection, metrics, indexManagement } = plugins;
+ const { usageCollection, visTypeTimeseries, indexManagement } = plugins;
const rollupSetup = (plugins.rollup as unknown) as RollupSetup;
@@ -53,7 +53,7 @@ export function rollup(kibana: any) {
rollupPluginInstance.setup(coreSetup, {
usageCollection,
- metrics,
+ visTypeTimeseries,
indexManagement,
__LEGACY: {
plugins: {
diff --git a/x-pack/legacy/plugins/rollup/kibana.json b/x-pack/legacy/plugins/rollup/kibana.json
index 3df8bd7c187d5..78458c9218be3 100644
--- a/x-pack/legacy/plugins/rollup/kibana.json
+++ b/x-pack/legacy/plugins/rollup/kibana.json
@@ -4,7 +4,7 @@
"requiredPlugins": [
"home",
"index_management",
- "metrics",
+ "visTypeTimeseries",
"indexPatternManagement"
],
"optionalPlugins": [
diff --git a/x-pack/legacy/plugins/rollup/server/plugin.ts b/x-pack/legacy/plugins/rollup/server/plugin.ts
index 5f29ad160e052..05c22b030fff9 100644
--- a/x-pack/legacy/plugins/rollup/server/plugin.ts
+++ b/x-pack/legacy/plugins/rollup/server/plugin.ts
@@ -38,12 +38,12 @@ export class RollupsServerPlugin implements Plugin {
{
__LEGACY: serverShim,
usageCollection,
- metrics,
+ visTypeTimeseries,
indexManagement,
}: {
__LEGACY: ServerShim;
usageCollection?: UsageCollectionSetup;
- metrics?: VisTypeTimeseriesSetup;
+ visTypeTimeseries?: VisTypeTimeseriesSetup;
indexManagement?: IndexManagementPluginSetup;
}
) {
@@ -83,8 +83,8 @@ export class RollupsServerPlugin implements Plugin {
indexManagement.indexDataEnricher.add(rollupDataEnricher);
}
- if (metrics) {
- const { addSearchStrategy } = metrics;
+ if (visTypeTimeseries) {
+ const { addSearchStrategy } = visTypeTimeseries;
registerRollupSearchStrategy(routeDependencies, addSearchStrategy);
}
}
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.test.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.test.ts
index e8019659d49c6..9eb4acbdb6164 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.test.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.test.ts
@@ -501,10 +501,8 @@ describe('Detections Rules API', () => {
test('check parameter url, query', async () => {
await getRuleStatusById({ id: 'mySuperRuleId', signal: abortCtrl.signal });
expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find_statuses', {
- query: {
- ids: '["mySuperRuleId"]',
- },
- method: 'GET',
+ body: '{"ids":["mySuperRuleId"]}',
+ method: 'POST',
signal: abortCtrl.signal,
});
});
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts
index 2dd6955581eff..5cc73e17662c6 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts
@@ -266,8 +266,8 @@ export const getRuleStatusById = async ({
signal: AbortSignal;
}): Promise =>
KibanaServices.get().http.fetch(DETECTION_ENGINE_RULES_STATUS_URL, {
- method: 'GET',
- query: { ids: JSON.stringify([id]) },
+ method: 'POST',
+ body: JSON.stringify({ ids: [id] }),
signal,
});
@@ -289,8 +289,8 @@ export const getRulesStatusByIds = async ({
const res = await KibanaServices.get().http.fetch(
DETECTION_ENGINE_RULES_STATUS_URL,
{
- method: 'GET',
- query: { ids: JSON.stringify(ids) },
+ method: 'POST',
+ body: JSON.stringify({ ids }),
signal,
}
);
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts
index e400360a5a5b2..94097df48949f 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts
@@ -254,9 +254,9 @@ export const getFindResultWithMultiHits = ({
export const ruleStatusRequest = () =>
requestMock.create({
- method: 'get',
+ method: 'post',
path: `${DETECTION_ENGINE_RULES_URL}/_find_statuses`,
- query: { ids: ['someId'] },
+ body: { ids: ['someId'] },
});
export const getImportRulesRequest = (hapiStream?: HapiReadableStream) =>
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts
index 89c9f34027120..d7c6d317227fa 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts
@@ -60,9 +60,9 @@ describe('find_statuses', () => {
describe('request validation', () => {
test('disallows singular id query param', async () => {
const request = requestMock.create({
- method: 'get',
+ method: 'post',
path: `${DETECTION_ENGINE_RULES_URL}/_find_statuses`,
- query: { id: ['someId'] },
+ body: { id: ['someId'] },
});
const result = server.validate(request);
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts
index 6fee4d71a904e..8eed146537718 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts
@@ -22,18 +22,18 @@ import {
} from '../utils';
export const findRulesStatusesRoute = (router: IRouter) => {
- router.get(
+ router.post(
{
path: `${DETECTION_ENGINE_RULES_URL}/_find_statuses`,
validate: {
- query: buildRouteValidation(findRulesStatusesSchema),
+ body: buildRouteValidation(findRulesStatusesSchema),
},
options: {
tags: ['access:siem'],
},
},
async (context, request, response) => {
- const { query } = request;
+ const { body } = request;
const siemResponse = buildSiemResponse(response);
const alertsClient = context.alerting?.getAlertsClient();
const savedObjectsClient = context.core.savedObjects.client;
@@ -50,7 +50,7 @@ export const findRulesStatusesRoute = (router: IRouter) => {
}
*/
try {
- const statuses = await query.ids.reduce>(
+ const statuses = await body.ids.reduce>(
async (acc, id) => {
const lastFiveErrorsForId = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_rules_statuses_by_ids.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_rules_statuses_by_ids.sh
index 543c019067e8e..5ae4904e9e4ec 100755
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_rules_statuses_by_ids.sh
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_rules_statuses_by_ids.sh
@@ -10,8 +10,12 @@ set -e
./check_env_variables.sh
-# Example: ./find_rules_statuses_by_ids.sh '["12345","6789abc"]'
+# Example: ./find_rules_statuses_by_ids.sh [\"12345\",\"6789abc\"]
curl -g -k \
+ -s \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
- -X GET "${KIBANA_URL}${SPACE_URL}/api/detection_engine/rules/_find_statuses?ids=$1" \
+ -X POST "${KIBANA_URL}${SPACE_URL}/api/detection_engine/rules/_find_statuses" \
+ -d "{\"ids\": $1}" \
| jq .
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts
index 7a211c5631da6..3bdcc3f92f44c 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { SignalSourceHit, SignalSearchResponse } from '../types';
+import { SignalSourceHit, SignalSearchResponse, BulkResponse, BulkItem } from '../types';
import {
Logger,
SavedObject,
@@ -416,3 +416,68 @@ export const exampleFindRuleStatusResponse: (
});
export const mockLogger: Logger = loggingServiceMock.createLogger();
+
+export const sampleBulkErrorItem = (
+ {
+ status,
+ reason,
+ }: {
+ status: number;
+ reason: string;
+ } = { status: 400, reason: 'Invalid call' }
+): BulkItem => {
+ return {
+ create: {
+ _index: 'mock_index',
+ _id: '123',
+ _version: 1,
+ status,
+ _shards: {
+ total: 1,
+ successful: 0,
+ failed: 1,
+ },
+ error: {
+ type: 'Invalid',
+ reason,
+ shard: 'shard 123',
+ index: 'mock_index',
+ },
+ },
+ };
+};
+
+export const sampleBulkItem = (): BulkItem => {
+ return {
+ create: {
+ _index: 'mock_index',
+ _id: '123',
+ _version: 1,
+ status: 200,
+ result: 'some result here',
+ _shards: {
+ total: 1,
+ successful: 1,
+ failed: 0,
+ },
+ },
+ };
+};
+
+export const sampleEmptyBulkResponse = (): BulkResponse => ({
+ took: 0,
+ errors: false,
+ items: [],
+});
+
+export const sampleBulkError = (): BulkResponse => ({
+ took: 0,
+ errors: true,
+ items: [sampleBulkErrorItem()],
+});
+
+export const sampleBulkResponse = (): BulkResponse => ({
+ took: 0,
+ errors: true,
+ items: [sampleBulkItem()],
+});
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts
index fc33d0e15e43f..4373a35cac0c5 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts
@@ -10,7 +10,7 @@ import { AlertServices } from '../../../../../../../plugins/alerting/server';
import { SignalSearchResponse, BulkResponse } from './types';
import { RuleAlertAction } from '../../../../common/detection_engine/types';
import { RuleTypeParams, RefreshTypes } from '../types';
-import { generateId, makeFloatString } from './utils';
+import { generateId, makeFloatString, errorAggregator } from './utils';
import { buildBulkBody } from './build_bulk_body';
import { Logger } from '../../../../../../../../src/core/server';
@@ -134,17 +134,10 @@ export const singleBulkCreate = async ({
logger.debug(`took property says bulk took: ${response.took} milliseconds`);
if (response.errors) {
- const itemsWithErrors = response.items.filter(item => item.create.error);
- const errorCountsByStatus = countBy(itemsWithErrors, item => item.create.status);
- delete errorCountsByStatus['409']; // Duplicate signals are expected
-
- if (!isEmpty(errorCountsByStatus)) {
+ const errorCountByMessage = errorAggregator(response, [409]);
+ if (!isEmpty(errorCountByMessage)) {
logger.error(
- `[-] bulkResponse had errors with response statuses:counts of...\n${JSON.stringify(
- errorCountsByStatus,
- null,
- 2
- )}`
+ `[-] bulkResponse had errors with responses of: ${JSON.stringify(errorCountByMessage)}`
);
}
}
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts
index 040e32aa0d360..a7556d992d20a 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts
@@ -59,35 +59,35 @@ export interface SignalSource {
};
}
+export interface BulkItem {
+ create: {
+ _index: string;
+ _type?: string;
+ _id: string;
+ _version: number;
+ result?: string;
+ _shards?: {
+ total: number;
+ successful: number;
+ failed: number;
+ };
+ _seq_no?: number;
+ _primary_term?: number;
+ status: number;
+ error?: {
+ type: string;
+ reason: string;
+ index_uuid?: string;
+ shard: string;
+ index: string;
+ };
+ };
+}
+
export interface BulkResponse {
took: number;
errors: boolean;
- items: [
- {
- create: {
- _index: string;
- _type?: string;
- _id: string;
- _version: number;
- result?: string;
- _shards?: {
- total: number;
- successful: number;
- failed: number;
- };
- _seq_no?: number;
- _primary_term?: number;
- status: number;
- error?: {
- type: string;
- reason: string;
- index_uuid?: string;
- shard: string;
- index: string;
- };
- };
- }
- ];
+ items: BulkItem[];
}
export interface MGetResponse {
@@ -169,3 +169,5 @@ export interface RuleAlertAttributes extends AlertAttributes {
ruleId: string;
};
}
+
+export type BulkResponseErrorAggregation = Record;
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.test.ts
index 873e06fcbb44e..e3a1b0c052aca 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.test.ts
@@ -13,8 +13,18 @@ import {
parseScheduleDates,
getDriftTolerance,
getGapBetweenRuns,
+ errorAggregator,
} from './utils';
+import { BulkResponseErrorAggregation } from './types';
+
+import {
+ sampleBulkResponse,
+ sampleEmptyBulkResponse,
+ sampleBulkError,
+ sampleBulkErrorItem,
+} from './__mocks__/es_results';
+
describe('utils', () => {
const anchor = '2020-01-01T06:06:06.666Z';
const unix = moment(anchor).valueOf();
@@ -351,4 +361,206 @@ describe('utils', () => {
expect(gap?.asMilliseconds()).toEqual(moment.duration(1, 'minute').asMilliseconds());
});
});
+
+ describe('errorAggregator', () => {
+ test('it should aggregate with an empty object when given an empty bulk response', () => {
+ const empty = sampleEmptyBulkResponse();
+ const aggregated = errorAggregator(empty, []);
+ const expected: BulkResponseErrorAggregation = {};
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it should aggregate with an empty object when given a valid bulk response with no errors', () => {
+ const validResponse = sampleBulkResponse();
+ const aggregated = errorAggregator(validResponse, []);
+ const expected: BulkResponseErrorAggregation = {};
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it should aggregate with a single error when given a single error item', () => {
+ const singleError = sampleBulkError();
+ const aggregated = errorAggregator(singleError, []);
+ const expected: BulkResponseErrorAggregation = {
+ 'Invalid call': {
+ count: 1,
+ statusCode: 400,
+ },
+ };
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it should aggregate two errors with a correct count when given the same two error items', () => {
+ const twoAggregatedErrors = sampleBulkError();
+ const item1 = sampleBulkErrorItem();
+ const item2 = sampleBulkErrorItem();
+ twoAggregatedErrors.items = [item1, item2];
+ const aggregated = errorAggregator(twoAggregatedErrors, []);
+ const expected: BulkResponseErrorAggregation = {
+ 'Invalid call': {
+ count: 2,
+ statusCode: 400,
+ },
+ };
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it should aggregate three errors with a correct count when given the same two error items', () => {
+ const twoAggregatedErrors = sampleBulkError();
+ const item1 = sampleBulkErrorItem();
+ const item2 = sampleBulkErrorItem();
+ const item3 = sampleBulkErrorItem();
+ twoAggregatedErrors.items = [item1, item2, item3];
+ const aggregated = errorAggregator(twoAggregatedErrors, []);
+ const expected: BulkResponseErrorAggregation = {
+ 'Invalid call': {
+ count: 3,
+ statusCode: 400,
+ },
+ };
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it should aggregate two distinct errors with the correct count of 1 for each error type', () => {
+ const twoAggregatedErrors = sampleBulkError();
+ const item1 = sampleBulkErrorItem({ status: 400, reason: 'Parse Error' });
+ const item2 = sampleBulkErrorItem({ status: 500, reason: 'Bad Network' });
+ twoAggregatedErrors.items = [item1, item2];
+ const aggregated = errorAggregator(twoAggregatedErrors, []);
+ const expected: BulkResponseErrorAggregation = {
+ 'Parse Error': {
+ count: 1,
+ statusCode: 400,
+ },
+ 'Bad Network': {
+ count: 1,
+ statusCode: 500,
+ },
+ };
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it should aggregate two of the same errors with the correct count of 2 for each error type', () => {
+ const twoAggregatedErrors = sampleBulkError();
+ const item1 = sampleBulkErrorItem({ status: 400, reason: 'Parse Error' });
+ const item2 = sampleBulkErrorItem({ status: 400, reason: 'Parse Error' });
+ const item3 = sampleBulkErrorItem({ status: 500, reason: 'Bad Network' });
+ const item4 = sampleBulkErrorItem({ status: 500, reason: 'Bad Network' });
+ twoAggregatedErrors.items = [item1, item2, item3, item4];
+ const aggregated = errorAggregator(twoAggregatedErrors, []);
+ const expected: BulkResponseErrorAggregation = {
+ 'Parse Error': {
+ count: 2,
+ statusCode: 400,
+ },
+ 'Bad Network': {
+ count: 2,
+ statusCode: 500,
+ },
+ };
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it should aggregate three of the same errors with the correct count of 2 for each error type', () => {
+ const twoAggregatedErrors = sampleBulkError();
+ const item1 = sampleBulkErrorItem({ status: 400, reason: 'Parse Error' });
+ const item2 = sampleBulkErrorItem({ status: 400, reason: 'Parse Error' });
+ const item3 = sampleBulkErrorItem({ status: 500, reason: 'Bad Network' });
+ const item4 = sampleBulkErrorItem({ status: 500, reason: 'Bad Network' });
+ const item5 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ const item6 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ twoAggregatedErrors.items = [item1, item2, item3, item4, item5, item6];
+ const aggregated = errorAggregator(twoAggregatedErrors, []);
+ const expected: BulkResponseErrorAggregation = {
+ 'Parse Error': {
+ count: 2,
+ statusCode: 400,
+ },
+ 'Bad Network': {
+ count: 2,
+ statusCode: 500,
+ },
+ 'Bad Gateway': {
+ count: 2,
+ statusCode: 502,
+ },
+ };
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it should aggregate a mix of errors with the correct aggregate count of each', () => {
+ const twoAggregatedErrors = sampleBulkError();
+ const item1 = sampleBulkErrorItem({ status: 400, reason: 'Parse Error' });
+ const item2 = sampleBulkErrorItem({ status: 500, reason: 'Bad Network' });
+ const item3 = sampleBulkErrorItem({ status: 500, reason: 'Bad Network' });
+ const item4 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ const item5 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ const item6 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ twoAggregatedErrors.items = [item1, item2, item3, item4, item5, item6];
+ const aggregated = errorAggregator(twoAggregatedErrors, []);
+ const expected: BulkResponseErrorAggregation = {
+ 'Parse Error': {
+ count: 1,
+ statusCode: 400,
+ },
+ 'Bad Network': {
+ count: 2,
+ statusCode: 500,
+ },
+ 'Bad Gateway': {
+ count: 3,
+ statusCode: 502,
+ },
+ };
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it will ignore error single codes such as 409', () => {
+ const twoAggregatedErrors = sampleBulkError();
+ const item1 = sampleBulkErrorItem({ status: 409, reason: 'Conflict Error' });
+ const item2 = sampleBulkErrorItem({ status: 409, reason: 'Conflict Error' });
+ const item3 = sampleBulkErrorItem({ status: 500, reason: 'Bad Network' });
+ const item4 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ const item5 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ const item6 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ twoAggregatedErrors.items = [item1, item2, item3, item4, item5, item6];
+ const aggregated = errorAggregator(twoAggregatedErrors, [409]);
+ const expected: BulkResponseErrorAggregation = {
+ 'Bad Network': {
+ count: 1,
+ statusCode: 500,
+ },
+ 'Bad Gateway': {
+ count: 3,
+ statusCode: 502,
+ },
+ };
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it will ignore two error codes such as 409 and 502', () => {
+ const twoAggregatedErrors = sampleBulkError();
+ const item1 = sampleBulkErrorItem({ status: 409, reason: 'Conflict Error' });
+ const item2 = sampleBulkErrorItem({ status: 409, reason: 'Conflict Error' });
+ const item3 = sampleBulkErrorItem({ status: 500, reason: 'Bad Network' });
+ const item4 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ const item5 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ const item6 = sampleBulkErrorItem({ status: 502, reason: 'Bad Gateway' });
+ twoAggregatedErrors.items = [item1, item2, item3, item4, item5, item6];
+ const aggregated = errorAggregator(twoAggregatedErrors, [409, 502]);
+ const expected: BulkResponseErrorAggregation = {
+ 'Bad Network': {
+ count: 1,
+ statusCode: 500,
+ },
+ };
+ expect(aggregated).toEqual(expected);
+ });
+
+ test('it will return an empty object given valid inputs and status codes to ignore', () => {
+ const bulkResponse = sampleBulkResponse();
+ const aggregated = errorAggregator(bulkResponse, [409, 502]);
+ const expected: BulkResponseErrorAggregation = {};
+ expect(aggregated).toEqual(expected);
+ });
+ });
});
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts
index 49af310db559f..077d3a9279c5e 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts
@@ -7,6 +7,7 @@ import { createHash } from 'crypto';
import moment from 'moment';
import dateMath from '@elastic/datemath';
import { parseDuration } from '../../../../../../../plugins/alerting/server';
+import { BulkResponse, BulkResponseErrorAggregation } from './types';
export const generateId = (
docIndex: string,
@@ -91,3 +92,45 @@ export const getGapBetweenRuns = ({
};
export const makeFloatString = (num: number): string => Number(num).toFixed(2);
+
+/**
+ * Given a BulkResponse this will return an aggregation based on the errors if any exist
+ * from the BulkResponse. Errors are aggregated on the reason as the unique key.
+ *
+ * Example would be:
+ * {
+ * 'Parse Error': {
+ * count: 100,
+ * statusCode: 400,
+ * },
+ * 'Internal server error': {
+ * count: 3,
+ * statusCode: 500,
+ * }
+ * }
+ * If this does not return any errors then you will get an empty object like so: {}
+ * @param response The bulk response to aggregate based on the error message
+ * @param ignoreStatusCodes Optional array of status codes to ignore when creating aggregate error messages
+ * @returns The aggregated example as shown above.
+ */
+export const errorAggregator = (
+ response: BulkResponse,
+ ignoreStatusCodes: number[]
+): BulkResponseErrorAggregation => {
+ return response.items.reduce((accum, item) => {
+ if (item.create.error != null && !ignoreStatusCodes.includes(item.create.status)) {
+ if (accum[item.create.error.reason] == null) {
+ accum[item.create.error.reason] = {
+ count: 1,
+ statusCode: item.create.status,
+ };
+ } else {
+ accum[item.create.error.reason] = {
+ count: accum[item.create.error.reason].count + 1,
+ statusCode: item.create.status,
+ };
+ }
+ }
+ return accum;
+ }, Object.create(null));
+};
diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/dynamic_settings.ts b/x-pack/legacy/plugins/uptime/common/runtime_types/dynamic_settings.ts
index 8dedd4672eeae..985b51891da99 100644
--- a/x-pack/legacy/plugins/uptime/common/runtime_types/dynamic_settings.ts
+++ b/x-pack/legacy/plugins/uptime/common/runtime_types/dynamic_settings.ts
@@ -6,10 +6,20 @@
import * as t from 'io-ts';
-export const DynamicSettingsType = t.type({
- heartbeatIndices: t.string,
+export const CertificatesStatesThresholdType = t.interface({
+ warningState: t.number,
+ errorState: t.number,
});
+export const DynamicSettingsType = t.intersection([
+ t.type({
+ heartbeatIndices: t.string,
+ }),
+ t.partial({
+ certificatesThresholds: CertificatesStatesThresholdType,
+ }),
+]);
+
export const DynamicSettingsSaveType = t.intersection([
t.type({
success: t.boolean,
@@ -21,7 +31,12 @@ export const DynamicSettingsSaveType = t.intersection([
export type DynamicSettings = t.TypeOf;
export type DynamicSettingsSaveResponse = t.TypeOf;
+export type CertificatesStatesThreshold = t.TypeOf;
export const defaultDynamicSettings: DynamicSettings = {
heartbeatIndices: 'heartbeat-8*',
+ certificatesThresholds: {
+ errorState: 7,
+ warningState: 30,
+ },
};
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx
index d149e7a6deb5a..ec2081d715554 100644
--- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx
@@ -9,8 +9,7 @@ import { i18n } from '@kbn/i18n';
import moment from 'moment';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui';
-import { Axis, Chart, Position, timeFormatter, Settings } from '@elastic/charts';
-import { SeriesIdentifier } from '@elastic/charts/dist/chart_types/xy_chart/utils/series';
+import { Axis, Chart, Position, timeFormatter, Settings, SeriesIdentifier } from '@elastic/charts';
import { getChartDateLabel } from '../../../lib/helper';
import { LocationDurationLine } from '../../../../common/types';
import { DurationLineSeriesList } from './duration_line_series_list';
diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/__snapshots__/certificate_form.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/__snapshots__/certificate_form.test.tsx.snap
new file mode 100644
index 0000000000000..36bc9bb860211
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/__snapshots__/certificate_form.test.tsx.snap
@@ -0,0 +1,69 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`CertificateForm shallow renders expected elements for valid props 1`] = `
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/__snapshots__/indices_form.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/__snapshots__/indices_form.test.tsx.snap
new file mode 100644
index 0000000000000..93151198c0f49
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/__snapshots__/indices_form.test.tsx.snap
@@ -0,0 +1,69 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`CertificateForm shallow renders expected elements for valid props 1`] = `
+
+
+
+`;
diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/certificate_form.test.tsx b/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/certificate_form.test.tsx
new file mode 100644
index 0000000000000..a3158f3d72445
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/certificate_form.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { CertificateExpirationForm } from '../certificate_form';
+import { shallowWithRouter } from '../../../lib';
+
+describe('CertificateForm', () => {
+ it('shallow renders expected elements for valid props', () => {
+ expect(
+ shallowWithRouter(
+
+ )
+ ).toMatchSnapshot();
+ });
+});
diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/indices_form.test.tsx b/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/indices_form.test.tsx
new file mode 100644
index 0000000000000..654d51019d4e5
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/indices_form.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { IndicesForm } from '../indices_form';
+import { shallowWithRouter } from '../../../lib';
+
+describe('CertificateForm', () => {
+ it('shallow renders expected elements for valid props', () => {
+ expect(
+ shallowWithRouter(
+
+ )
+ ).toMatchSnapshot();
+ });
+});
diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/certificate_form.tsx b/x-pack/legacy/plugins/uptime/public/components/settings/certificate_form.tsx
new file mode 100644
index 0000000000000..5103caee1e1c0
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/settings/certificate_form.tsx
@@ -0,0 +1,160 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { useSelector } from 'react-redux';
+import {
+ EuiDescribedFormGroup,
+ EuiFormRow,
+ EuiCode,
+ EuiFieldNumber,
+ EuiTitle,
+ EuiSpacer,
+ EuiSelect,
+ EuiFlexGroup,
+ EuiFlexItem,
+} from '@elastic/eui';
+import { defaultDynamicSettings, DynamicSettings } from '../../../common/runtime_types';
+import { selectDynamicSettings } from '../../state/selectors';
+
+type NumStr = string | number;
+
+export type OnFieldChangeType = (field: string, value?: NumStr) => void;
+
+export interface SettingsFormProps {
+ onChange: OnFieldChangeType;
+ formFields: DynamicSettings | null;
+ fieldErrors: any;
+ isDisabled: boolean;
+}
+
+export const CertificateExpirationForm: React.FC = ({
+ onChange,
+ formFields,
+ fieldErrors,
+ isDisabled,
+}) => {
+ const dss = useSelector(selectDynamicSettings);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ }
+ description={
+
+ }
+ >
+ {defaultDynamicSettings?.certificatesThresholds?.errorState}
+ ),
+ }}
+ />
+ }
+ isInvalid={!!fieldErrors?.certificatesThresholds?.errorState}
+ label={
+
+ }
+ >
+
+
+
+ onChange(
+ 'certificatesThresholds.errorState',
+ value === '' ? undefined : Number(value)
+ )
+ }
+ />
+
+
+
+
+
+
+ {defaultDynamicSettings?.certificatesThresholds?.warningState}
+ ),
+ }}
+ />
+ }
+ isInvalid={!!fieldErrors?.certificatesThresholds?.warningState}
+ label={
+
+ }
+ >
+
+
+
+ onChange('certificatesThresholds.warningState', Number(event.currentTarget.value))
+ }
+ />
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/indices_form.tsx b/x-pack/legacy/plugins/uptime/public/components/settings/indices_form.tsx
new file mode 100644
index 0000000000000..c28eca2ea229e
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/components/settings/indices_form.tsx
@@ -0,0 +1,90 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { useSelector } from 'react-redux';
+import {
+ EuiDescribedFormGroup,
+ EuiFormRow,
+ EuiCode,
+ EuiFieldText,
+ EuiTitle,
+ EuiSpacer,
+} from '@elastic/eui';
+import { defaultDynamicSettings } from '../../../common/runtime_types';
+import { selectDynamicSettings } from '../../state/selectors';
+import { SettingsFormProps } from './certificate_form';
+
+export const IndicesForm: React.FC = ({
+ onChange,
+ formFields,
+ fieldErrors,
+ isDisabled,
+}) => {
+ const dss = useSelector(selectDynamicSettings);
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ }
+ description={
+
+ }
+ >
+ {defaultDynamicSettings.heartbeatIndices},
+ }}
+ />
+ }
+ isInvalid={!!fieldErrors?.heartbeatIndices}
+ label={
+
+ }
+ >
+ onChange('heartbeatIndices', event.currentTarget.value)}
+ />
+
+
+ >
+ );
+};
diff --git a/x-pack/legacy/plugins/uptime/public/pages/settings.tsx b/x-pack/legacy/plugins/uptime/public/pages/settings.tsx
index 765b0e3c664bc..049dffecd3f2e 100644
--- a/x-pack/legacy/plugins/uptime/public/pages/settings.tsx
+++ b/x-pack/legacy/plugins/uptime/public/pages/settings.tsx
@@ -9,46 +9,54 @@ import {
EuiButton,
EuiButtonEmpty,
EuiCallOut,
- EuiCode,
- EuiDescribedFormGroup,
- EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
EuiForm,
- EuiFormRow,
EuiPanel,
EuiSpacer,
- EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { connect } from 'react-redux';
-import { isEqual } from 'lodash';
+import { useDispatch, useSelector } from 'react-redux';
+import { cloneDeep, isEqual, set } from 'lodash';
import { i18n } from '@kbn/i18n';
import { Link } from 'react-router-dom';
-import { AppState } from '../state';
import { selectDynamicSettings } from '../state/selectors';
-import { DynamicSettingsState } from '../state/reducers/dynamic_settings';
import { getDynamicSettings, setDynamicSettings } from '../state/actions/dynamic_settings';
-import { defaultDynamicSettings, DynamicSettings } from '../../common/runtime_types';
+import { DynamicSettings } from '../../common/runtime_types';
import { useBreadcrumbs } from '../hooks/use_breadcrumbs';
import { OVERVIEW_ROUTE } from '../../common/constants';
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
import { UptimePage, useUptimeTelemetry } from '../hooks';
+import { IndicesForm } from '../components/settings/indices_form';
+import {
+ CertificateExpirationForm,
+ OnFieldChangeType,
+} from '../components/settings/certificate_form';
+
+const getFieldErrors = (formFields: DynamicSettings | null) => {
+ if (formFields) {
+ const blankStr = 'May not be blank';
+ const { certificatesThresholds, heartbeatIndices } = formFields;
+ const heartbeatIndErr = heartbeatIndices.match(/^\S+$/) ? '' : blankStr;
+ const errorStateErr = certificatesThresholds?.errorState ? null : blankStr;
+ const warningStateErr = certificatesThresholds?.warningState ? null : blankStr;
+ return {
+ heartbeatIndices: heartbeatIndErr,
+ certificatesThresholds:
+ errorStateErr || warningStateErr
+ ? {
+ errorState: errorStateErr,
+ warningState: warningStateErr,
+ }
+ : null,
+ };
+ }
+ return null;
+};
-interface Props {
- dynamicSettingsState: DynamicSettingsState;
-}
-
-interface DispatchProps {
- dispatchGetDynamicSettings: typeof getDynamicSettings;
- dispatchSetDynamicSettings: typeof setDynamicSettings;
-}
+export const SettingsPage = () => {
+ const dss = useSelector(selectDynamicSettings);
-export const SettingsPageComponent = ({
- dynamicSettingsState: dss,
- dispatchGetDynamicSettings,
- dispatchSetDynamicSettings,
-}: Props & DispatchProps) => {
const settingsBreadcrumbText = i18n.translate('xpack.uptime.settingsBreadcrumbText', {
defaultMessage: 'Settings',
});
@@ -56,9 +64,11 @@ export const SettingsPageComponent = ({
useUptimeTelemetry(UptimePage.Settings);
+ const dispatch = useDispatch();
+
useEffect(() => {
- dispatchGetDynamicSettings({});
- }, [dispatchGetDynamicSettings]);
+ dispatch(getDynamicSettings({}));
+ }, [dispatch]);
const [formFields, setFormFields] = useState(dss.settings || null);
@@ -66,22 +76,22 @@ export const SettingsPageComponent = ({
setFormFields({ ...dss.settings });
}
- const fieldErrors = formFields && {
- heartbeatIndices: formFields.heartbeatIndices.match(/^\S+$/) ? null : 'May not be blank',
- };
+ const fieldErrors = getFieldErrors(formFields);
+
const isFormValid = !(fieldErrors && Object.values(fieldErrors).find(v => !!v));
- const onChangeFormField = (field: keyof DynamicSettings, value: any) => {
+ const onChangeFormField: OnFieldChangeType = (field, value) => {
if (formFields) {
- formFields[field] = value;
- setFormFields({ ...formFields });
+ const newFormFields = cloneDeep(formFields);
+ set(newFormFields, field, value);
+ setFormFields(cloneDeep(newFormFields));
}
};
const onApply = (event: React.FormEvent) => {
event.preventDefault();
if (formFields) {
- dispatchSetDynamicSettings(formFields);
+ dispatch(setDynamicSettings(formFields));
}
};
@@ -128,68 +138,18 @@ export const SettingsPageComponent = ({