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 000000000000..cb3170e989e1
--- /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 000000000000..fe3803a6ffe5
--- /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 000000000000..bb6dec504ff4
--- /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 000000000000..6563e3c636a9
--- /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 000000000000..20f930382955
--- /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 000000000000..2ea3ea7e58c7
--- /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 000000000000..1c221e13f534
--- /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 000000000000..3dce9d88c803
--- /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 000000000000..eb4f095bc19b
--- /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 000000000000..325d19625df4
--- /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 000000000000..f8d4418fc6cb
--- /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 94cf3c94187b..35d109975c83 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 073f02f1a419..4bade638a65a 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 0632b5e5e229..000000000000
--- 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 ca114bed2114..000000000000
--- 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 f037b7b3e7cb..a5c1d59be06d 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 000000000000..d19a7dfcbfcf
--- /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 5c0f10cac517..5450e84417f8 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 156f38fab098..cecef7c92356 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 3c6bee114b6a..0d640e52c3a0 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 b65ae47f0e0c..0966b91a4ebf 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 000000000000..a9fe188ee2bf
--- /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 4551d1cab863..fb7d6a10c6b6 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 d8202545f0ea..a8894286de91 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 e34742159048..000000000000
--- 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 69912f914498..3a3b0f7f3a9a 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/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md
new file mode 100644
index 000000000000..d1a1ee0905c6
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [indexPattern](./kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md)
+
+## IndexPatternField.indexPattern property
+
+Signature:
+
+```typescript
+indexPattern?: IndexPattern;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md
index 121ae80734df..df0de6ce0e54 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md
@@ -27,6 +27,7 @@ export declare class Field implements IFieldType
| [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) | | string[]
| |
| [filterable](./kibana-plugin-plugins-data-public.indexpatternfield.filterable.md) | | boolean
| |
| [format](./kibana-plugin-plugins-data-public.indexpatternfield.format.md) | | any
| |
+| [indexPattern](./kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md) | | IndexPattern
| |
| [lang](./kibana-plugin-plugins-data-public.indexpatternfield.lang.md) | | string
| |
| [name](./kibana-plugin-plugins-data-public.indexpatternfield.name.md) | | string
| |
| [script](./kibana-plugin-plugins-data-public.indexpatternfield.script.md) | | string
| |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
index fc0dab94a0f6..bf29c883e4eb 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
@@ -88,7 +88,6 @@
| [SavedQuery](./kibana-plugin-plugins-data-public.savedquery.md) | |
| [SavedQueryService](./kibana-plugin-plugins-data-public.savedqueryservice.md) | |
| [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) | |
-| [SearchStrategyProvider](./kibana-plugin-plugins-data-public.searchstrategyprovider.md) | |
| [TabbedAggColumn](./kibana-plugin-plugins-data-public.tabbedaggcolumn.md) | \* |
| [TabbedTable](./kibana-plugin-plugins-data-public.tabbedtable.md) | \* |
| [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) | |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.id.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.id.md
deleted file mode 100644
index d60ffba6a05c..000000000000
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.id.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchStrategyProvider](./kibana-plugin-plugins-data-public.searchstrategyprovider.md) > [id](./kibana-plugin-plugins-data-public.searchstrategyprovider.id.md)
-
-## SearchStrategyProvider.id property
-
-Signature:
-
-```typescript
-id: string;
-```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.isviable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.isviable.md
deleted file mode 100644
index aa8ed49051ee..000000000000
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.isviable.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchStrategyProvider](./kibana-plugin-plugins-data-public.searchstrategyprovider.md) > [isViable](./kibana-plugin-plugins-data-public.searchstrategyprovider.isviable.md)
-
-## SearchStrategyProvider.isViable property
-
-Signature:
-
-```typescript
-isViable: (indexPattern: IndexPattern) => boolean;
-```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.md
deleted file mode 100644
index b271a921906a..000000000000
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchStrategyProvider](./kibana-plugin-plugins-data-public.searchstrategyprovider.md)
-
-## SearchStrategyProvider interface
-
-Signature:
-
-```typescript
-export interface SearchStrategyProvider
-```
-
-## Properties
-
-| Property | Type | Description |
-| --- | --- | --- |
-| [id](./kibana-plugin-plugins-data-public.searchstrategyprovider.id.md) | string
| |
-| [isViable](./kibana-plugin-plugins-data-public.searchstrategyprovider.isviable.md) | (indexPattern: IndexPattern) => boolean
| |
-| [search](./kibana-plugin-plugins-data-public.searchstrategyprovider.search.md) | (params: SearchStrategySearchParams) => SearchStrategyResponse
| |
-
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.search.md
deleted file mode 100644
index 6e2561c3b0ad..000000000000
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.search.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchStrategyProvider](./kibana-plugin-plugins-data-public.searchstrategyprovider.md) > [search](./kibana-plugin-plugins-data-public.searchstrategyprovider.search.md)
-
-## SearchStrategyProvider.search property
-
-Signature:
-
-```typescript
-search: (params: SearchStrategySearchParams) => SearchStrategyResponse;
-```
diff --git a/docs/images/add_remote_cluster.png b/docs/images/add_remote_cluster.png
index 376b1d839236..160d29b741c6 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 3bf86458eddd..f80de9352280 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 000000000000..4c45174cff7f
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 f103bb3cf2ac..505adeb45ae2
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 000000000000..c28379863b74
Binary files /dev/null and b/docs/images/remote-clusters-list-view.png differ
diff --git a/docs/ingest_manager/index-templates.asciidoc b/docs/ingest_manager/index-templates.asciidoc
new file mode 100644
index 000000000000..e19af63c3116
--- /dev/null
+++ b/docs/ingest_manager/index-templates.asciidoc
@@ -0,0 +1,7 @@
+# Elasticsearch Index Templates
+
+## Generation
+
+* Index templates are generated from `YAML` files contained in the package.
+* There is one index template per dataset.
+* For the generation of an index template, all `yml` files contained in the package subdirectory `dataset/DATASET_NAME/fields/` are used.
diff --git a/docs/ingest_manager/index.asciidoc b/docs/ingest_manager/index.asciidoc
index 22afa88c919e..866935d1fa58 100644
--- a/docs/ingest_manager/index.asciidoc
+++ b/docs/ingest_manager/index.asciidoc
@@ -199,3 +199,10 @@ The new ingest pipeline is expected to still work with the data coming from olde
In case of a breaking change in the data structure, the new ingest pipeline is also expected to deal with this change. In case there are breaking changes which cannot be dealt with in an ingest pipeline, a new package has to be created.
Each package lists its minimal required agent version. In case there are agents enrolled with an older version, the user is notified to upgrade these agents as otherwise the new configs cannot be rolled out.
+
+=== Generated assets
+
+When a package is installed or upgraded, certain Kibana and Elasticsearch assets are generated from . These follow the naming conventions explained above (see "indexing strategy") and contain configuration for the elastic stack that makes ingesting and displaying data work with as little user interaction as possible.
+
+* link:index-templates.asciidoc[Elasticsearch Index Templates]
+* Kibana Index Patterns
diff --git a/docs/management/managing-ccr.asciidoc b/docs/management/managing-ccr.asciidoc
new file mode 100644
index 000000000000..b2db5a80cfe7
--- /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 6b69cfef5b76..00ec5c7d2dde 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 fd835bde8332..a5503969a3ec 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 7bbc01bb303f..09763182fa88 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 e7c50bb7604c..0ed1270ca92c 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 0c6fa4c6c4f5..000000000000
--- 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 000000000000..7fc1e3d9aefe
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 6418495eee9e..91565bf59aa7 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 99f017f2945a..000000000000
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 dbfa43f47656..522f34662657 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 d5c612c7589c..6589c0c5565d 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 000000000000..dd36f0a6d702
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 020396d0f3e4..1fce2e9592c1 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 e0e9b2755590..8d242789cdcc 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 785b9f818f5b..a355f8ecf484 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 d54fd02c7c06..8a4be1f11a72 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 098ce12a5699..71c09c968e51 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 000000000000..55da6e802bec
--- /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/alerting/action-types.asciidoc b/docs/user/alerting/action-types.asciidoc
index 2913bf28dd76..49e7bd1d7774 100644
--- a/docs/user/alerting/action-types.asciidoc
+++ b/docs/user/alerting/action-types.asciidoc
@@ -34,14 +34,23 @@ a| <>
[NOTE]
==============================================
-Some action types are paid commercial features, while others are free.
-For a comparison of the Elastic subscription levels,
-see https://www.elastic.co/subscriptions[the subscription page].
+Some action types are paid commercial features, while others are free.
+For a comparison of the Elastic subscription levels,
+see https://www.elastic.co/subscriptions[the subscription page].
==============================================
+[float]
+[[create-connectors]]
+=== Connectors
+
+You can create connectors for actions in <> or via the action API.
+For out-of-the-box and standardized connectors, you can <>
+before {kib} starts.
+
include::action-types/email.asciidoc[]
include::action-types/index.asciidoc[]
include::action-types/pagerduty.asciidoc[]
include::action-types/server-log.asciidoc[]
include::action-types/slack.asciidoc[]
include::action-types/webhook.asciidoc[]
+include::pre-configured-connectors.asciidoc[]
diff --git a/docs/user/alerting/images/alert-pre-configured-connectors-dropdown.png b/docs/user/alerting/images/alert-pre-configured-connectors-dropdown.png
new file mode 100644
index 000000000000..4e6c71329862
Binary files /dev/null and b/docs/user/alerting/images/alert-pre-configured-connectors-dropdown.png differ
diff --git a/docs/user/alerting/images/alert-pre-configured-slack-connector.png b/docs/user/alerting/images/alert-pre-configured-slack-connector.png
new file mode 100644
index 000000000000..de05e2074ddd
Binary files /dev/null and b/docs/user/alerting/images/alert-pre-configured-slack-connector.png differ
diff --git a/docs/user/alerting/images/pre-configured-connectors-managing.png b/docs/user/alerting/images/pre-configured-connectors-managing.png
new file mode 100644
index 000000000000..f97e93175fa3
Binary files /dev/null and b/docs/user/alerting/images/pre-configured-connectors-managing.png differ
diff --git a/docs/user/alerting/images/pre-configured-connectors-view-screen.png b/docs/user/alerting/images/pre-configured-connectors-view-screen.png
new file mode 100644
index 000000000000..43ac44e7536d
Binary files /dev/null and b/docs/user/alerting/images/pre-configured-connectors-view-screen.png differ
diff --git a/docs/user/alerting/pre-configured-connectors.asciidoc b/docs/user/alerting/pre-configured-connectors.asciidoc
new file mode 100644
index 000000000000..3db13acfb423
--- /dev/null
+++ b/docs/user/alerting/pre-configured-connectors.asciidoc
@@ -0,0 +1,88 @@
+[role="xpack"]
+[[pre-configured-connectors]]
+
+== Preconfigured connectors
+
+You can preconfigure an action connector to have all the information it needs prior to startup
+by adding it to the `kibana.yml` file.
+Sensitive configuration information, such as credentials, can use the {kib} keystore.
+
+Preconfigured connectors offer the following capabilities:
+
+- Require no setup. Configuration and credentials needed to execute an
+action are predefined, including the connector name and ID.
+- Appear in all spaces because they are not saved objects.
+- Cannot be edited or deleted.
+
+[float]
+[[preconfigured-connector-example]]
+=== Example of a preconfigured connector
+
+The following example shows a valid configuration 2 out-of-the box connector.
+
+[source,console]
+------------------------
+ xpack.actions.preconfigured:
+ - id: 'my-slack1' <1>
+ actionTypeId: .slack <2>
+ name: 'Slack #xyz' <3>
+ config: <4>
+ webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz'
+ - id: 'webhook-service'
+ actionTypeId: .webhook
+ name: 'Email service'
+ config:
+ url: 'https://email-alert-service.elastic.co'
+ method: post
+ headers:
+ header1: value1
+ header2: value2
+ secrets: <5>
+ user: elastic
+ password: changeme
+------------------------
+
+<1> `id` is the action connector identifier.
+<2> `actionTypeId` is the action type identifier.
+<3> `name` is the name of the preconfigured connector.
+<4> `config` is the action type specific to the configuration.
+<5> `secrets` is sensitive configuration, such as username, password, and keys.
+
+[NOTE]
+==============================================
+Sensitive properties, such as passwords, can also be stored in the {kib} keystore.
+==============================================
+
+[float]
+[[pre-configured-connector-alert-form]]
+=== Creating an alert with a preconfigured connector
+
+When attaching an action to an alert,
+select from a list of available action types, and
+then select the Slack or Webhook type. Those action types were configured previously.
+The preconfigured connector is installed and is automatically selected.
+
+[role="screenshot"]
+image::images/alert-pre-configured-slack-connector.png[Create alert with selected Slack action type]
+
+The dropdown is populated with additional preconfigured Slack connectors.
+The `preconfigured` label distinguishes them from space-aware connectors that use saved objects.
+
+[role="screenshot"]
+image::images/alert-pre-configured-connectors-dropdown.png[Dropdown list with pre-cofigured connectors]
+
+[float]
+[[managing-pre-configured-connectors]]
+=== Managing preconfigured connectors
+
+Preconfigured connectors appear in the connector list, regardless of which space the user is in.
+They are tagged as “preconfigured” and cannot be deleted.
+
+[role="screenshot"]
+image::images/pre-configured-connectors-managing.png[Connectors managing tab with pre-cofigured]
+
+Clicking on a preconfigured connector shows the description, but not any of the configuration.
+A message indicates that this is a preconfigured connector.
+
+[role="screenshot"]
+image::images/pre-configured-connectors-view-screen.png[Pre-configured connector view details]
diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc
index fa34802abe2a..a4ba320e826b 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 0f67c53cfa3e..58da2014de49 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 c60cf5234c9f..21e9f67e6206 100644
--- a/package.json
+++ b/package.json
@@ -119,8 +119,8 @@
"dependencies": {
"@babel/core": "^7.9.0",
"@babel/register": "^7.9.0",
- "@elastic/apm-rum": "^4.6.0",
- "@elastic/charts": "18.2.2",
+ "@elastic/apm-rum": "^5.1.1",
+ "@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-config-schema/src/index.ts b/packages/kbn-config-schema/src/index.ts
index fc3e3c541846..5d387f327e58 100644
--- a/packages/kbn-config-schema/src/index.ts
+++ b/packages/kbn-config-schema/src/index.ts
@@ -60,6 +60,7 @@ import {
export { ObjectType, TypeOf, Type };
export { ByteSizeValue } from './byte_size_value';
export { SchemaTypeError, ValidationError } from './errors';
+export { isConfigSchema } from './typeguards';
function any(options?: TypeOptions) {
return new AnyType(options);
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/legacy_imports.ts b/packages/kbn-config-schema/src/typeguards/index.ts
similarity index 87%
rename from src/legacy/core_plugins/vis_type_timelion/public/legacy_imports.ts
rename to packages/kbn-config-schema/src/typeguards/index.ts
index e7612b288fb2..e724878eb33e 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/legacy_imports.ts
+++ b/packages/kbn-config-schema/src/typeguards/index.ts
@@ -17,5 +17,4 @@
* under the License.
*/
-export { npSetup, npStart } from 'ui/new_platform';
-export { PluginsStart } from 'ui/new_platform/new_platform';
+export { isConfigSchema } from './is_config_schema';
diff --git a/packages/kbn-config-schema/src/typeguards/is_config_schema.test.ts b/packages/kbn-config-schema/src/typeguards/is_config_schema.test.ts
new file mode 100644
index 000000000000..e0ef3835ca0a
--- /dev/null
+++ b/packages/kbn-config-schema/src/typeguards/is_config_schema.test.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 { schema } from '..';
+import { isConfigSchema } from './is_config_schema';
+
+describe('isConfigSchema', () => {
+ it('returns true for every sub classes of `Type`', () => {
+ expect(isConfigSchema(schema.any())).toBe(true);
+ expect(isConfigSchema(schema.arrayOf(schema.string()))).toBe(true);
+ expect(isConfigSchema(schema.boolean())).toBe(true);
+ expect(isConfigSchema(schema.buffer())).toBe(true);
+ expect(isConfigSchema(schema.byteSize())).toBe(true);
+ expect(isConfigSchema(schema.duration())).toBe(true);
+ expect(isConfigSchema(schema.literal(''))).toBe(true);
+ expect(isConfigSchema(schema.mapOf(schema.string(), schema.number()))).toBe(true);
+ expect(isConfigSchema(schema.nullable(schema.string()))).toBe(true);
+ expect(isConfigSchema(schema.number())).toBe(true);
+ expect(isConfigSchema(schema.object({}))).toBe(true);
+ expect(isConfigSchema(schema.oneOf([schema.string()]))).toBe(true);
+ expect(isConfigSchema(schema.recordOf(schema.string(), schema.object({})))).toBe(true);
+ expect(isConfigSchema(schema.string())).toBe(true);
+ expect(isConfigSchema(schema.stream())).toBe(true);
+ });
+
+ it('returns false for every javascript data type', () => {
+ expect(isConfigSchema('foo')).toBe(false);
+ expect(isConfigSchema(42)).toBe(false);
+ expect(isConfigSchema(new Date())).toBe(false);
+ expect(isConfigSchema(null)).toBe(false);
+ expect(isConfigSchema(undefined)).toBe(false);
+ expect(isConfigSchema([1, 2, 3])).toBe(false);
+ expect(isConfigSchema({ foo: 'bar' })).toBe(false);
+ expect(isConfigSchema(function() {})).toBe(false);
+ });
+
+ it('returns true as long as `__isKbnConfigSchemaType` is true', () => {
+ expect(isConfigSchema({ __isKbnConfigSchemaType: true })).toBe(true);
+ });
+});
diff --git a/packages/kbn-config-schema/src/typeguards/is_config_schema.ts b/packages/kbn-config-schema/src/typeguards/is_config_schema.ts
new file mode 100644
index 000000000000..20e68ab2ead2
--- /dev/null
+++ b/packages/kbn-config-schema/src/typeguards/is_config_schema.ts
@@ -0,0 +1,24 @@
+/*
+ * 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 { Type } from '../types';
+
+export function isConfigSchema(obj: any): obj is Type {
+ return obj ? obj.__isKbnConfigSchemaType === true : false;
+}
diff --git a/packages/kbn-config-schema/src/types/type.ts b/packages/kbn-config-schema/src/types/type.ts
index 6d5ddf6b24af..5ca16c61399e 100644
--- a/packages/kbn-config-schema/src/types/type.ts
+++ b/packages/kbn-config-schema/src/types/type.ts
@@ -32,6 +32,9 @@ export abstract class Type {
// sets the value to `null` while still keeping the type.
public readonly type: V = null! as V;
+ // used for the `isConfigSchema` typeguard
+ public readonly __isKbnConfigSchemaType = true;
+
/**
* Internal "schema" backed by Joi.
* @type {Schema}
diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts
index d67b95741675..cc564dd4a838 100644
--- a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts
+++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts
@@ -146,6 +146,7 @@ describe('OptimizerConfig::parseOptions()', () => {
/x-pack/plugins,
/plugins,
/examples,
+ /x-pack/examples,
-extra,
],
"profileWebpack": false,
diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts
index 1c8ae265bf6b..7e1514058446 100644
--- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts
+++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts
@@ -91,14 +91,14 @@ export class OptimizerConfig {
/**
* BEWARE: this needs to stay roughly synchronized with
- * `src/core/server/config/env.ts` which determins which paths
+ * `src/core/server/config/env.ts` which determines which paths
* should be searched for plugins to load
*/
const pluginScanDirs = options.pluginScanDirs || [
Path.resolve(repoRoot, 'src/plugins'),
...(oss ? [] : [Path.resolve(repoRoot, 'x-pack/plugins')]),
Path.resolve(repoRoot, 'plugins'),
- ...(examples ? [Path.resolve('examples')] : []),
+ ...(examples ? [Path.resolve('examples'), Path.resolve('x-pack/examples')] : []),
Path.resolve(repoRoot, '../kibana-extra'),
];
if (!pluginScanDirs.every(p => Path.isAbsolute(p))) {
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 66f17ab579ec..f4b91d154cbb 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 7c5d6a62a11c..c8614b1df9d5 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/cli/cluster/cluster_manager.ts b/src/cli/cluster/cluster_manager.ts
index 44b6c39556af..a87e2aa11f2c 100644
--- a/src/cli/cluster/cluster_manager.ts
+++ b/src/cli/cluster/cluster_manager.ts
@@ -263,7 +263,7 @@ export class ClusterManager {
...pluginInternalDirsIgnore,
fromRoot('src/legacy/server/sass/__tmp__'),
fromRoot('x-pack/legacy/plugins/reporting/.chromium'),
- fromRoot('x-pack/legacy/plugins/siem/cypress'),
+ fromRoot('x-pack/plugins/siem/cypress'),
fromRoot('x-pack/legacy/plugins/apm/e2e'),
fromRoot('x-pack/legacy/plugins/apm/scripts'),
fromRoot('x-pack/legacy/plugins/canvas/canvas_plugin_src'), // prevents server from restarting twice for Canvas plugin changes,
diff --git a/src/core/CONVENTIONS.md b/src/core/CONVENTIONS.md
index 0f592d108c56..a82cc27839a1 100644
--- a/src/core/CONVENTIONS.md
+++ b/src/core/CONVENTIONS.md
@@ -1,6 +1,6 @@
-# Kibana Conventions
-
-- [Kibana Conventions](#kibana-conventions)
+- [Organisational Conventions](#organisational-conventions)
+ - [Definition of done](#definition-of-done)
+- [Technical Conventions](#technical-conventions)
- [Plugin Structure](#plugin-structure)
- [The PluginInitializer](#the-plugininitializer)
- [The Plugin class](#the-plugin-class)
@@ -8,8 +8,34 @@
- [Services](#services)
- [Usage Collection](#usage-collection)
- [Saved Objects Types](#saved-objects-types)
-
-## Plugin Structure
+ - [Naming conventions](#naming-conventions)
+
+## Organisational Conventions
+### Definition of done
+Definition of done for a feature:
+- has a dedicated Github issue describing problem space
+- an umbrella task closed/updated with follow-ups
+- all code review comments are resolved
+- has been verified manually by at least one reviewer
+- can be used by first & third party plugins
+- there is no contradiction between client and server API
+- works for OSS version
+ - works with and without a `server.basePath` configured
+ - cannot crash the Kibana server when it fails
+- works for the commercial version with a license
+ - for a logged-in user
+ - for anonymous user
+ - compatible with Spaces
+- has unit & integration tests for public contracts
+- has functional tests for user scenarios
+- uses standard tooling:
+ - code - `TypeScript`
+ - UI - `React`
+ - tests - `jest` & `FTR`
+- has documentation for the public contract, provides a usage example
+
+## Technical Conventions
+### Plugin Structure
All Kibana plugins built at Elastic should follow the same structure.
@@ -60,7 +86,7 @@ my_plugin/
- More should be fleshed out here...
- Usage collectors for Telemetry should be defined in a separate `server/collectors/` directory.
-### The PluginInitializer
+#### The PluginInitializer
```ts
// my_plugin/public/index.ts
@@ -75,7 +101,7 @@ export {
}
```
-### The Plugin class
+#### The Plugin class
```ts
// my_plugin/public/plugin.ts
@@ -130,7 +156,7 @@ Difference between `setup` and `start`:
The bulk of your plugin logic will most likely live inside _handlers_ registered during `setup`.
-### Applications
+#### Applications
It's important that UI code is not included in the main bundle for your plugin. Our webpack configuration supports
dynamic async imports to split out imports into a separate bundle. Every app's rendering logic and UI code should
@@ -175,7 +201,7 @@ export class MyPlugin implements Plugin {
}
```
-### Services
+#### Services
Service structure should mirror the plugin lifecycle to make reasoning about how the service is executed more clear.
@@ -226,7 +252,7 @@ export class Plugin {
}
```
-### Usage Collection
+#### Usage Collection
For creating and registering a Usage Collector. Collectors should be defined in a separate directory `server/collectors/`. You can read more about usage collectors on `src/plugins/usage_collection/README.md`.
@@ -263,7 +289,7 @@ export function registerMyPluginUsageCollector(usageCollection?: UsageCollection
}
```
-### Saved Objects Types
+#### Saved Objects Types
Saved object type definitions should be defined in their own `server/saved_objects` directory.
diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md
index 368d1f47e9c3..80f12dd78214 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 37d0b9297ed3..8c5fe4875aae 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/public/plugins/plugin_loader.test.ts b/src/core/public/plugins/plugin_loader.test.ts
index b4e2c3095f14..18cc2d7a6f18 100644
--- a/src/core/public/plugins/plugin_loader.test.ts
+++ b/src/core/public/plugins/plugin_loader.test.ts
@@ -62,7 +62,7 @@ test('`loadPluginBundles` creates a script tag and loads initializer', async ()
const fakeScriptTag = createdScriptTags[0];
expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith(
'src',
- '/bundles/plugin/plugin-a/plugin-a.plugin.js'
+ '/bundles/plugin:plugin-a/plugin-a.plugin.js'
);
expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith('id', 'kbn-plugin-plugin-a');
expect(fakeScriptTag.onload).toBeInstanceOf(Function);
@@ -85,7 +85,7 @@ test('`loadPluginBundles` includes the basePath', async () => {
const fakeScriptTag = createdScriptTags[0];
expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith(
'src',
- '/mybasepath/bundles/plugin/plugin-a/plugin-a.plugin.js'
+ '/mybasepath/bundles/plugin:plugin-a/plugin-a.plugin.js'
);
});
@@ -96,7 +96,7 @@ test('`loadPluginBundles` rejects if script.onerror is called', async () => {
fakeScriptTag1.onerror(new Error('Whoa there!'));
await expect(loadPromise).rejects.toThrowErrorMatchingInlineSnapshot(
- `"Failed to load \\"plugin-a\\" bundle (/bundles/plugin/plugin-a/plugin-a.plugin.js)"`
+ `"Failed to load \\"plugin-a\\" bundle (/bundles/plugin:plugin-a/plugin-a.plugin.js)"`
);
});
@@ -105,7 +105,7 @@ test('`loadPluginBundles` rejects if timeout is reached', async () => {
// Override the timeout to 1 ms for testi.
loadPluginBundle(addBasePath, 'plugin-a', { timeoutMs: 1 })
).rejects.toThrowErrorMatchingInlineSnapshot(
- `"Timeout reached when loading \\"plugin-a\\" bundle (/bundles/plugin/plugin-a/plugin-a.plugin.js)"`
+ `"Timeout reached when loading \\"plugin-a\\" bundle (/bundles/plugin:plugin-a/plugin-a.plugin.js)"`
);
});
@@ -115,11 +115,11 @@ test('`loadPluginBundles` rejects if bundle does attach an initializer to window
const fakeScriptTag1 = createdScriptTags[0];
// Setup a fake initializer as if a plugin bundle had actually been loaded.
- coreWindow.__kbnBundles__['plugin/plugin-a'] = undefined;
+ coreWindow.__kbnBundles__['plugin:plugin-a'] = undefined;
// Call the onload callback
fakeScriptTag1.onload();
await expect(loadPromise).rejects.toThrowErrorMatchingInlineSnapshot(
- `"Definition of plugin \\"plugin-a\\" should be a function (/bundles/plugin/plugin-a/plugin-a.plugin.js)."`
+ `"Definition of plugin \\"plugin-a\\" should be a function (/bundles/plugin:plugin-a/plugin-a.plugin.js)."`
);
});
diff --git a/src/core/public/plugins/plugin_loader.ts b/src/core/public/plugins/plugin_loader.ts
index bf7711055e97..9b35588dfe72 100644
--- a/src/core/public/plugins/plugin_loader.ts
+++ b/src/core/public/plugins/plugin_loader.ts
@@ -93,7 +93,7 @@ export const loadPluginBundle: LoadPluginBundle = <
const script = document.createElement('script');
// Assumes that all plugin bundles get put into the bundles/plugins subdirectory
- const bundlePath = addBasePath(`/bundles/plugin/${pluginName}/${pluginName}.plugin.js`);
+ const bundlePath = addBasePath(`/bundles/plugin:${pluginName}/${pluginName}.plugin.js`);
script.setAttribute('src', bundlePath);
script.setAttribute('id', `kbn-plugin-${pluginName}`);
script.setAttribute('async', '');
diff --git a/src/core/server/config/env.test.ts b/src/core/server/config/env.test.ts
index c244012e3446..0fffcc44781d 100644
--- a/src/core/server/config/env.test.ts
+++ b/src/core/server/config/env.test.ts
@@ -164,6 +164,17 @@ test('pluginSearchPaths contains examples plugins path if --run-examples flag is
expect(env.pluginSearchPaths).toContain('/some/home/dir/examples');
});
+test('pluginSearchPaths contains x-pack/examples plugins path if --run-examples flag is true', () => {
+ const env = new Env(
+ '/some/home/dir',
+ getEnvOptions({
+ cliArgs: { runExamples: true },
+ })
+ );
+
+ expect(env.pluginSearchPaths).toContain('/some/home/dir/x-pack/examples');
+});
+
test('pluginSearchPaths does not contains examples plugins path if --run-examples flag is false', () => {
const env = new Env(
'/some/home/dir',
@@ -174,3 +185,14 @@ test('pluginSearchPaths does not contains examples plugins path if --run-example
expect(env.pluginSearchPaths).not.toContain('/some/home/dir/examples');
});
+
+test('pluginSearchPaths does not contains x-pack/examples plugins path if --run-examples flag is false', () => {
+ const env = new Env(
+ '/some/home/dir',
+ getEnvOptions({
+ cliArgs: { runExamples: false },
+ })
+ );
+
+ expect(env.pluginSearchPaths).not.toContain('/some/home/dir/x-pack/examples');
+});
diff --git a/src/core/server/config/env.ts b/src/core/server/config/env.ts
index 05a8f40a09a8..d8068c5b383f 100644
--- a/src/core/server/config/env.ts
+++ b/src/core/server/config/env.ts
@@ -109,7 +109,9 @@ export class Env {
resolve(this.homeDir, 'src', 'plugins'),
...(options.cliArgs.oss ? [] : [resolve(this.homeDir, 'x-pack', 'plugins')]),
resolve(this.homeDir, 'plugins'),
- ...(options.cliArgs.runExamples ? [resolve(this.homeDir, 'examples')] : []),
+ ...(options.cliArgs.runExamples
+ ? [resolve(this.homeDir, 'examples'), resolve(this.homeDir, 'x-pack', 'examples')]
+ : []),
resolve(this.homeDir, '..', 'kibana-extra'),
];
diff --git a/src/core/server/http/index.ts b/src/core/server/http/index.ts
index a75eb04fa012..ca9dfde2e71d 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 50d3d7b47bf8..050881472bc8 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 8f895753c38c..af99812eff4b 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 19eaee508199..b79cc0d325f1 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 d254f391ca5e..83ceff4a25d8 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 bb56ee3727d1..69402a74eda5 100644
--- a/src/core/server/http/router/router.ts
+++ b/src/core/server/http/router/router.ts
@@ -20,7 +20,7 @@
import { Request, ResponseObject, ResponseToolkit } from 'hapi';
import Boom from 'boom';
-import { Type } from '@kbn/config-schema';
+import { isConfigSchema } from '@kbn/config-schema';
import { Logger } from '../../logging';
import { KibanaRequest } from './request';
import { KibanaResponseFactory, kibanaResponseFactory, IKibanaResponse } from './response';
@@ -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.
@@ -139,7 +139,7 @@ function routeSchemasFromRouteConfig
(
if (route.validate !== false) {
Object.entries(route.validate).forEach(([key, schema]) => {
- if (!(schema instanceof Type || typeof schema === 'function')) {
+ if (!(isConfigSchema(schema) || typeof schema === 'function')) {
throw new Error(
`Expected a valid validation logic declared with '@kbn/config-schema' package or a RouteValidationFunction at key: [${key}].`
);
@@ -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/router/validator/validator.ts b/src/core/server/http/router/validator/validator.ts
index 97dd2bc894f8..6c766e69f0f3 100644
--- a/src/core/server/http/router/validator/validator.ts
+++ b/src/core/server/http/router/validator/validator.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { ValidationError, Type, schema, ObjectType } from '@kbn/config-schema';
+import { ValidationError, Type, schema, ObjectType, isConfigSchema } from '@kbn/config-schema';
import { Stream } from 'stream';
import { RouteValidationError } from './validator_error';
@@ -236,7 +236,7 @@ export class RouteValidator
{
data?: unknown,
namespace?: string
): RouteValidationResultType {
- if (validationRule instanceof Type) {
+ if (isConfigSchema(validationRule)) {
return validationRule.validate(data, {}, namespace);
} else if (typeof validationRule === 'function') {
return this.validateFunction(validationRule, data, namespace);
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 000000000000..4536b0898cad
--- /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 000000000000..e6f129ba12d7
--- /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 000000000000..bc79ad68f409
--- /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/plugins/data/public/search/search_strategy/index.ts b/src/core/server/http_resources/index.ts
similarity index 75%
rename from src/plugins/data/public/search/search_strategy/index.ts
rename to src/core/server/http_resources/index.ts
index e3de2ea46e3e..b373c6a9efa8 100644
--- a/src/plugins/data/public/search/search_strategy/index.ts
+++ b/src/core/server/http_resources/index.ts
@@ -17,8 +17,13 @@
* under the License.
*/
-export { SearchError, getSearchErrorType } from './search_error';
+export { HttpResourcesService } from './http_resources_service';
-export { SearchStrategyProvider, SearchStrategySearchParams } from './types';
-
-export { defaultSearchStrategy } from './default_search_strategy';
+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 000000000000..0a5daa02e17e
--- /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 000000000000..d761e2def102
--- /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 fe1c9925abc4..86192245bd2d 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 {
@@ -314,8 +324,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
@@ -331,7 +339,6 @@ export {
*/
export interface RequestHandlerContext {
core: {
- rendering: IScopedRenderingClient;
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
@@ -363,7 +370,10 @@ export interface CoreSetup ({
+jest.doMock('./plugins/find_legacy_plugin_specs', () => ({
findLegacyPluginSpecs: findLegacyPluginSpecsMock,
}));
+
+export const logLegacyThirdPartyPluginDeprecationWarningMock = jest.fn();
+jest.doMock('./plugins/log_legacy_plugins_warning', () => ({
+ logLegacyThirdPartyPluginDeprecationWarning: logLegacyThirdPartyPluginDeprecationWarningMock,
+}));
diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts
index 0cf2ebe55ea1..a75f7dda302c 100644
--- a/src/core/server/legacy/legacy_service.test.ts
+++ b/src/core/server/legacy/legacy_service.test.ts
@@ -22,7 +22,10 @@ jest.mock('../../../cli/cluster/cluster_manager');
jest.mock('./config/legacy_deprecation_adapters', () => ({
convertLegacyDeprecationProvider: (provider: any) => Promise.resolve(provider),
}));
-import { findLegacyPluginSpecsMock } from './legacy_service.test.mocks';
+import {
+ findLegacyPluginSpecsMock,
+ logLegacyThirdPartyPluginDeprecationWarningMock,
+} from './legacy_service.test.mocks';
import { BehaviorSubject, throwError } from 'rxjs';
@@ -41,6 +44,7 @@ import { httpServiceMock } from '../http/http_service.mock';
import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock';
import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock';
import { capabilitiesServiceMock } from '../capabilities/capabilities_service.mock';
+import { httpResourcesMock } from '../http_resources/http_resources_service.mock';
import { setupMock as renderingServiceMock } from '../rendering/__mocks__/rendering_service';
import { uuidServiceMock } from '../uuid/uuid_service.mock';
import { metricsServiceMock } from '../metrics/metrics_service.mock';
@@ -86,23 +90,11 @@ beforeEach(() => {
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 +102,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 = {
@@ -474,6 +479,38 @@ describe('#discoverPlugins()', () => {
expect(configService.addDeprecationProvider).toHaveBeenCalledWith('', 'providerA');
expect(configService.addDeprecationProvider).toHaveBeenCalledWith('', 'providerB');
});
+
+ it(`logs deprecations for legacy third party plugins`, async () => {
+ const pluginSpecs = [
+ { getId: () => 'pluginA', getDeprecationsProvider: () => undefined },
+ { getId: () => 'pluginB', getDeprecationsProvider: () => undefined },
+ ];
+ findLegacyPluginSpecsMock.mockImplementation(
+ settings =>
+ Promise.resolve({
+ pluginSpecs,
+ pluginExtendedConfig: settings,
+ disabledPluginSpecs: [],
+ uiExports: {},
+ navLinks: [],
+ }) as any
+ );
+
+ const legacyService = new LegacyService({
+ coreId,
+ env,
+ logger,
+ configService: configService as any,
+ });
+
+ await legacyService.discoverPlugins();
+
+ expect(logLegacyThirdPartyPluginDeprecationWarningMock).toHaveBeenCalledTimes(1);
+ expect(logLegacyThirdPartyPluginDeprecationWarningMock).toHaveBeenCalledWith({
+ specs: pluginSpecs,
+ log: expect.any(Object),
+ });
+ });
});
test('Sets the server.uuid property on the legacy configuration', async () => {
diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts
index f77230301ce0..b95362e1ea26 100644
--- a/src/core/server/legacy/legacy_service.ts
+++ b/src/core/server/legacy/legacy_service.ts
@@ -28,7 +28,7 @@ import { DevConfig, DevConfigType, config as devConfig } from '../dev';
import { BasePathProxyServer, HttpConfig, HttpConfigType, config as httpConfig } from '../http';
import { Logger } from '../logging';
import { PathConfigType } from '../path';
-import { findLegacyPluginSpecs } from './plugins';
+import { findLegacyPluginSpecs, logLegacyThirdPartyPluginDeprecationWarning } from './plugins';
import { convertLegacyDeprecationProvider } from './config';
import {
ILegacyInternals,
@@ -133,6 +133,11 @@ export class LegacyService implements CoreService {
this.coreContext.env.packageInfo
);
+ logLegacyThirdPartyPluginDeprecationWarning({
+ specs: pluginSpecs,
+ log: this.log,
+ });
+
this.legacyPlugins = {
pluginSpecs,
disabledPluginSpecs,
@@ -269,6 +274,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 +289,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 +349,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/plugins/index.ts b/src/core/server/legacy/plugins/index.ts
index a6d55e1da783..7ec5dbc1983a 100644
--- a/src/core/server/legacy/plugins/index.ts
+++ b/src/core/server/legacy/plugins/index.ts
@@ -18,3 +18,4 @@
*/
export { findLegacyPluginSpecs } from './find_legacy_plugin_specs';
+export { logLegacyThirdPartyPluginDeprecationWarning } from './log_legacy_plugins_warning';
diff --git a/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.ts b/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.ts
new file mode 100644
index 000000000000..1790b096a71a
--- /dev/null
+++ b/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.ts
@@ -0,0 +1,90 @@
+/*
+ * 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 { loggerMock } from '../../logging/logger.mock';
+import { logLegacyThirdPartyPluginDeprecationWarning } from './log_legacy_plugins_warning';
+import { LegacyPluginSpec } from '../types';
+
+const createPluginSpec = ({ id, path }: { id: string; path: string }): LegacyPluginSpec => {
+ return {
+ getId: () => id,
+ getExpectedKibanaVersion: () => 'kibana',
+ getConfigPrefix: () => 'plugin.config',
+ getDeprecationsProvider: () => undefined,
+ getPack: () => ({
+ getPath: () => path,
+ }),
+ };
+};
+
+describe('logLegacyThirdPartyPluginDeprecationWarning', () => {
+ let log: ReturnType;
+
+ beforeEach(() => {
+ log = loggerMock.create();
+ });
+
+ it('logs warning for third party plugins', () => {
+ logLegacyThirdPartyPluginDeprecationWarning({
+ specs: [createPluginSpec({ id: 'plugin', path: '/some-external-path' })],
+ log,
+ });
+ expect(log.warn).toHaveBeenCalledTimes(1);
+ expect(log.warn.mock.calls[0]).toMatchInlineSnapshot(`
+ Array [
+ "Some installed third party plugin(s) [plugin] are using the legacy plugin format and will no longer work in a future Kibana release. Please refer to https://www.elastic.co/guide/en/kibana/master/breaking-changes-8.0.html for a list of breaking changes and https://github.com/elastic/kibana/blob/master/src/core/MIGRATION.md for documentation on how to migrate legacy plugins.",
+ ]
+ `);
+ });
+
+ it('lists all the deprecated plugins and only log once', () => {
+ logLegacyThirdPartyPluginDeprecationWarning({
+ specs: [
+ createPluginSpec({ id: 'pluginA', path: '/abs/path/to/pluginA' }),
+ createPluginSpec({ id: 'pluginB', path: '/abs/path/to/pluginB' }),
+ createPluginSpec({ id: 'pluginC', path: '/abs/path/to/pluginC' }),
+ ],
+ log,
+ });
+ expect(log.warn).toHaveBeenCalledTimes(1);
+ expect(log.warn.mock.calls[0]).toMatchInlineSnapshot(`
+ Array [
+ "Some installed third party plugin(s) [pluginA, pluginB, pluginC] are using the legacy plugin format and will no longer work in a future Kibana release. Please refer to https://www.elastic.co/guide/en/kibana/master/breaking-changes-8.0.html for a list of breaking changes and https://github.com/elastic/kibana/blob/master/src/core/MIGRATION.md for documentation on how to migrate legacy plugins.",
+ ]
+ `);
+ });
+
+ it('does not log warning for internal legacy plugins', () => {
+ logLegacyThirdPartyPluginDeprecationWarning({
+ specs: [
+ createPluginSpec({
+ id: 'plugin',
+ path: '/absolute/path/to/kibana/src/legacy/core_plugins',
+ }),
+ createPluginSpec({
+ id: 'plugin',
+ path: '/absolute/path/to/kibana/x-pack',
+ }),
+ ],
+ log,
+ });
+
+ expect(log.warn).not.toHaveBeenCalled();
+ });
+});
diff --git a/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts b/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts
new file mode 100644
index 000000000000..f9c3dcbf554c
--- /dev/null
+++ b/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts
@@ -0,0 +1,52 @@
+/*
+ * 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 { Logger } from '../../logging';
+import { LegacyPluginSpec } from '../types';
+
+const internalPaths = ['/src/legacy/core_plugins', '/x-pack'];
+
+const breakingChangesUrl =
+ 'https://www.elastic.co/guide/en/kibana/master/breaking-changes-8.0.html';
+const migrationGuideUrl = 'https://github.com/elastic/kibana/blob/master/src/core/MIGRATION.md';
+
+export const logLegacyThirdPartyPluginDeprecationWarning = ({
+ specs,
+ log,
+}: {
+ specs: LegacyPluginSpec[];
+ log: Logger;
+}) => {
+ const thirdPartySpecs = specs.filter(isThirdPartyPluginSpec);
+ if (thirdPartySpecs.length > 0) {
+ const pluginIds = thirdPartySpecs.map(spec => spec.getId());
+ log.warn(
+ `Some installed third party plugin(s) [${pluginIds.join(
+ ', '
+ )}] are using the legacy plugin format and will no longer work in a future Kibana release. ` +
+ `Please refer to ${breakingChangesUrl} for a list of breaking changes ` +
+ `and ${migrationGuideUrl} for documentation on how to migrate legacy plugins.`
+ );
+ }
+};
+
+const isThirdPartyPluginSpec = (spec: LegacyPluginSpec): boolean => {
+ const pluginPath = spec.getPack().getPath();
+ return !internalPaths.some(internalPath => pluginPath.indexOf(internalPath) > -1);
+};
diff --git a/src/core/server/legacy/types.ts b/src/core/server/legacy/types.ts
index 0c1a7730f92a..2567ca790e04 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 };
@@ -98,6 +98,7 @@ export interface LegacyPluginSpec {
getExpectedKibanaVersion: () => string;
getConfigPrefix: () => string;
getDeprecationsProvider: () => LegacyConfigDeprecationProvider | undefined;
+ getPack: () => LegacyPluginPack;
}
/**
@@ -173,6 +174,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 faf73044cac4..3b9a39db7227 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 c7ef213c8f18..e480de750bb1 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.ts b/src/core/server/plugins/plugin.ts
index 7c67ab7a48df..d7cfaa14d234 100644
--- a/src/core/server/plugins/plugin.ts
+++ b/src/core/server/plugins/plugin.ts
@@ -21,7 +21,7 @@ import { join } from 'path';
import typeDetect from 'type-detect';
import { Subject } from 'rxjs';
import { first } from 'rxjs/operators';
-import { Type } from '@kbn/config-schema';
+import { isConfigSchema } from '@kbn/config-schema';
import { Logger } from '../logging';
import {
@@ -150,7 +150,7 @@ export class PluginWrapper<
}
const configDescriptor = pluginDefinition.config;
- if (!(configDescriptor.schema instanceof Type)) {
+ if (!isConfigSchema(configDescriptor.schema)) {
throw new Error('Configuration schema expected to be an instance of Type');
}
return configDescriptor;
diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts
index 61d97aea9745..ab18a9cbbc06 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 29e5b83b2e4c..a40566767dda 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 14147ab9f2a8..38fda12bd290 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 a0ecee47c675..d7a348affe94 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 000000000000..7eba33251238
--- /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 43ff4f633085..d1c527aca4db 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 dbafd5806bd7..a02d85d22b2c 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 cfaa23d49113..2a3be9305500 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 44add4e97700..2c710d4eaa07 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 ef3f546b5e57..64270c677ff2 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 257b32c1e4c2..3f5c0c387661 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 336eeff99f47..cda0e86f15bd 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 82a889f75d3c..23e0285201dc 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 84337474f3ee..f82822f90f48 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 be3fdb86a994..740313a53d1e 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 a6b580e9b346..ea881805e1ae 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 9efc82603b17..b50c6dc9a1ab 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 64bdf1771dec..033aeea7c018 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 534896381262..af7c08d1fbfc 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 37051da4b17d..7ca5c75f19e8 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
@@ -2458,12 +2468,11 @@ export const validBodyOutput: readonly ["data", "stream"];
// Warnings were encountered during analysis:
//
// src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts
-// src/core/server/legacy/types.ts:162:3 - (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts
-// src/core/server/legacy/types.ts:163:3 - (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts
-// 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/legacy/types.ts:163:3 - (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts
+// src/core/server/legacy/types.ts:164:3 - (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts
+// src/core/server/legacy/types.ts:165:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts
+// src/core/server/legacy/types.ts:166:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts
+// src/core/server/legacy/types.ts:167:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" 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 24c41d511180..1e3e1638cf2a 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 684f50a5666e..d4c0ebcfb7cf 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 031315bec0da..1bea65ddee92 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/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts
index 8ed64f004c9b..43114b2edccf 100644
--- a/src/dev/storybook/aliases.ts
+++ b/src/dev/storybook/aliases.ts
@@ -24,6 +24,6 @@ export const storybookAliases = {
drilldowns: 'x-pack/plugins/drilldowns/scripts/storybook.js',
embeddable: 'src/plugins/embeddable/scripts/storybook.js',
infra: 'x-pack/legacy/plugins/infra/scripts/storybook.js',
- siem: 'x-pack/legacy/plugins/siem/scripts/storybook.js',
+ siem: 'x-pack/plugins/siem/scripts/storybook.js',
ui_actions: 'x-pack/plugins/advanced_ui_actions/scripts/storybook.js',
};
diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts
index 34756912fc24..01d8a30b598c 100644
--- a/src/dev/typescript/projects.ts
+++ b/src/dev/typescript/projects.ts
@@ -27,7 +27,7 @@ export const PROJECTS = [
new Project(resolve(REPO_ROOT, 'test/tsconfig.json'), { name: 'kibana/test' }),
new Project(resolve(REPO_ROOT, 'x-pack/tsconfig.json')),
new Project(resolve(REPO_ROOT, 'x-pack/test/tsconfig.json'), { name: 'x-pack/test' }),
- new Project(resolve(REPO_ROOT, 'x-pack/legacy/plugins/siem/cypress/tsconfig.json'), {
+ new Project(resolve(REPO_ROOT, 'x-pack/plugins/siem/cypress/tsconfig.json'), {
name: 'siem/cypress',
}),
new Project(resolve(REPO_ROOT, 'x-pack/legacy/plugins/apm/e2e/tsconfig.json'), {
@@ -44,6 +44,9 @@ export const PROJECTS = [
...glob
.sync('examples/*/tsconfig.json', { cwd: REPO_ROOT })
.map(path => new Project(resolve(REPO_ROOT, path))),
+ ...glob
+ .sync('x-pack/examples/*/tsconfig.json', { cwd: REPO_ROOT })
+ .map(path => new Project(resolve(REPO_ROOT, path))),
...glob
.sync('test/plugin_functional/plugins/*/tsconfig.json', { cwd: REPO_ROOT })
.map(path => new Project(resolve(REPO_ROOT, path))),
diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js
index 7a11845e29b5..6fc7c2f0a01a 100644
--- a/src/legacy/core_plugins/kibana/index.js
+++ b/src/legacy/core_plugins/kibana/index.js
@@ -53,11 +53,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 fbfb0a06fabc..d49c59970f52 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 0a026a5e0c31..20c46765dcb3 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/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.js.snap
deleted file mode 100644
index 59b275c7708a..000000000000
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.js.snap
+++ /dev/null
@@ -1,205 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`CreateIndexPatternWizard defaults to the loading state 1`] = `
-
-
-
-
-
-
-
-`;
-
-exports[`CreateIndexPatternWizard renders index pattern step when there are indices 1`] = `
-
-
-
-
-
-
-
-`;
-
-exports[`CreateIndexPatternWizard renders the empty state when there are no indices 1`] = `
-
-
-
-
-
-
-
-`;
-
-exports[`CreateIndexPatternWizard renders time field step when step is set to 2 1`] = `
-
-
-
-
-
-
-
-`;
-
-exports[`CreateIndexPatternWizard renders when there are no indices but there are remote clusters 1`] = `
-
-
-
-
-
-
-
-`;
-
-exports[`CreateIndexPatternWizard shows system indices even if there are no other indices if the include system indices is toggled 1`] = `
-
-
-
-
-
-
-
-`;
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap
new file mode 100644
index 000000000000..09a06bd8827c
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap
@@ -0,0 +1,312 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`CreateIndexPatternWizard defaults to the loading state 1`] = `
+
+
+
+
+
+
+
+`;
+
+exports[`CreateIndexPatternWizard renders index pattern step when there are indices 1`] = `
+
+
+
+
+
+
+
+`;
+
+exports[`CreateIndexPatternWizard renders the empty state when there are no indices 1`] = `
+
+
+
+
+
+
+
+`;
+
+exports[`CreateIndexPatternWizard renders time field step when step is set to 2 1`] = `
+
+
+
+
+
+
+
+`;
+
+exports[`CreateIndexPatternWizard renders when there are no indices but there are remote clusters 1`] = `
+
+
+
+
+
+
+
+`;
+
+exports[`CreateIndexPatternWizard shows system indices even if there are no other indices if the include system indices is toggled 1`] = `
+
+
+
+
+
+
+
+`;
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
index 648bf7f8f973..d8f677b7f608 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
@@ -48,7 +48,7 @@ interface StepIndexPatternProps {
esService: DataPublicPluginStart['search']['__LEGACY']['esClient'];
savedObjectsClient: SavedObjectsClient;
indexPatternCreationType: IndexPatternCreationConfig;
- goToNextStep: () => void;
+ goToNextStep: (query: string) => void;
initialQuery?: string;
uiSettings: IUiSettingsClient;
}
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.tsx
similarity index 55%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.tsx
index 941f87d4d9fd..45af98661eda 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.tsx
@@ -21,17 +21,12 @@ import React from 'react';
import { shallow } from 'enzyme';
import { CreateIndexPatternWizard } from './create_index_pattern_wizard';
-const mockIndexPatternCreationType = {
- getIndexPatternType: () => 'default',
- getIndexPatternName: () => 'name',
- getIsBeta: () => false,
- checkIndicesForErrors: () => false,
- getShowSystemIndices: () => false,
- renderPrompt: () => {},
- getIndexPatternMappings: () => {
- return {};
- },
-};
+import { coreMock } from '../../../../../../../../core/public/mocks';
+import { dataPluginMock } from '../../../../../../../../plugins/data/public/mocks';
+import { IndexPatternCreationConfig } from '../../../../../../../../plugins/index_pattern_management/public';
+import { IndexPattern } from '../../../../../../../../plugins/data/public';
+import { SavedObjectsClient } from '../../../../../../../../core/public';
+
jest.mock('./components/step_index_pattern', () => ({ StepIndexPattern: 'StepIndexPattern' }));
jest.mock('./components/step_time_field', () => ({ StepTimeField: 'StepTimeField' }));
jest.mock('./components/header', () => ({ Header: 'Header' }));
@@ -46,39 +41,36 @@ jest.mock('ui/chrome', () => ({
addBasePath: () => {},
}));
-const loadingDataDocUrl = '';
+const { savedObjects, overlays, uiSettings } = coreMock.createStart();
+const { indexPatterns, search } = dataPluginMock.createStartContract();
+const mockIndexPatternCreationType = new IndexPatternCreationConfig({
+ type: 'default',
+ name: 'name',
+});
+
const initialQuery = '';
const services = {
- es: {},
- indexPatterns: {},
- savedObjectsClient: {},
- config: {},
- changeUrl: () => {},
- scopeApply: () => {},
-
+ es: search.__LEGACY.esClient,
+ indexPatterns,
+ savedObjectsClient: savedObjects.client as SavedObjectsClient,
+ uiSettings,
+ changeUrl: jest.fn(),
+ openConfirm: overlays.openConfirm,
indexPatternCreationType: mockIndexPatternCreationType,
};
describe('CreateIndexPatternWizard', () => {
- it(`defaults to the loading state`, async () => {
+ test(`defaults to the loading state`, () => {
const component = shallow(
-
+
);
expect(component).toMatchSnapshot();
});
- it('renders the empty state when there are no indices', async () => {
+ test('renders the empty state when there are no indices', async () => {
const component = shallow(
-
+
);
component.setState({
@@ -91,13 +83,9 @@ describe('CreateIndexPatternWizard', () => {
expect(component).toMatchSnapshot();
});
- it('renders when there are no indices but there are remote clusters', async () => {
+ test('renders when there are no indices but there are remote clusters', async () => {
const component = shallow(
-
+
);
component.setState({
@@ -110,13 +98,9 @@ describe('CreateIndexPatternWizard', () => {
expect(component).toMatchSnapshot();
});
- it('shows system indices even if there are no other indices if the include system indices is toggled', async () => {
+ test('shows system indices even if there are no other indices if the include system indices is toggled', async () => {
const component = shallow(
-
+
);
component.setState({
@@ -129,13 +113,9 @@ describe('CreateIndexPatternWizard', () => {
expect(component).toMatchSnapshot();
});
- it('renders index pattern step when there are indices', async () => {
+ test('renders index pattern step when there are indices', async () => {
const component = shallow(
-
+
);
component.setState({
@@ -147,13 +127,9 @@ describe('CreateIndexPatternWizard', () => {
expect(component).toMatchSnapshot();
});
- it('renders time field step when step is set to 2', async () => {
+ test('renders time field step when step is set to 2', async () => {
const component = shallow(
-
+
);
component.setState({
@@ -166,37 +142,30 @@ describe('CreateIndexPatternWizard', () => {
expect(component).toMatchSnapshot();
});
- it('invokes the provided services when creating an index pattern', async () => {
- const get = jest.fn();
- const set = jest.fn();
+ test('invokes the provided services when creating an index pattern', async () => {
const create = jest.fn().mockImplementation(() => 'id');
const clear = jest.fn();
- const changeUrl = jest.fn();
-
- const component = shallow(
- ({
- create,
- }),
- clearCache: clear,
- },
- changeUrl,
- indexPatternCreationType: mockIndexPatternCreationType,
- }}
- />
+ services.indexPatterns.clearCache = clear;
+ const indexPattern = ({
+ id: '1',
+ title: 'my-fake-index-pattern',
+ timeFieldName: 'timestamp',
+ fields: [],
+ create,
+ } as unknown) as IndexPattern;
+ services.indexPatterns.make = async () => {
+ return indexPattern;
+ };
+
+ const component = shallow(
+
);
component.setState({ indexPattern: 'foo' });
- await component.instance().createIndexPattern(null, 'id');
- expect(get).toBeCalled();
+ await component.instance().createIndexPattern(undefined, 'id');
+ expect(services.uiSettings.get).toBeCalled();
expect(create).toBeCalled();
expect(clear).toBeCalledWith('id');
- expect(changeUrl).toBeCalledWith(`/management/kibana/index_patterns/id`);
+ expect(services.changeUrl).toBeCalledWith(`/management/kibana/index_patterns/id`);
});
});
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.tsx
similarity index 63%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.tsx
index 1a93188edd6c..4166d48349d3 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.tsx
@@ -17,11 +17,11 @@
* under the License.
*/
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
+import React, { ReactElement, Component } from 'react';
-import { EuiGlobalToastList } from '@elastic/eui';
+import { EuiGlobalToastList, EuiGlobalToastListToast } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
+import { i18n } from '@kbn/i18n';
import { StepIndexPattern } from './components/step_index_pattern';
import { StepTimeField } from './components/step_time_field';
@@ -31,41 +31,61 @@ import { EmptyState } from './components/empty_state';
import { MAX_SEARCH_SIZE } from './constants';
import { ensureMinimumTime, getIndices } from './lib';
-import { i18n } from '@kbn/i18n';
-
-export class CreateIndexPatternWizard extends Component {
- static propTypes = {
- initialQuery: PropTypes.string,
- services: PropTypes.shape({
- es: PropTypes.object.isRequired,
- indexPatterns: PropTypes.object.isRequired,
- savedObjectsClient: PropTypes.object.isRequired,
- indexPatternCreationType: PropTypes.object.isRequired,
- config: PropTypes.object.isRequired,
- changeUrl: PropTypes.func.isRequired,
- openConfirm: PropTypes.func.isRequired,
- }).isRequired,
+import {
+ SavedObjectsClient,
+ IUiSettingsClient,
+ OverlayStart,
+} from '../../../../../../../../core/public';
+import { DataPublicPluginStart } from '../../../../../../../../plugins/data/public';
+import { IndexPatternCreationConfig } from '../../../../../../../../plugins/index_pattern_management/public';
+import { MatchedIndex } from './types';
+
+interface CreateIndexPatternWizardProps {
+ initialQuery: string;
+ services: {
+ indexPatternCreationType: IndexPatternCreationConfig;
+ es: DataPublicPluginStart['search']['__LEGACY']['esClient'];
+ indexPatterns: DataPublicPluginStart['indexPatterns'];
+ savedObjectsClient: SavedObjectsClient;
+ uiSettings: IUiSettingsClient;
+ changeUrl: (url: string) => void;
+ openConfirm: OverlayStart['openConfirm'];
};
+}
- constructor(props) {
- super(props);
- this.indexPatternCreationType = this.props.services.indexPatternCreationType;
- this.state = {
- step: 1,
- indexPattern: '',
- allIndices: [],
- remoteClustersExist: false,
- isInitiallyLoadingIndices: true,
- isIncludingSystemIndices: false,
- toasts: [],
- };
- }
+interface CreateIndexPatternWizardState {
+ step: number;
+ indexPattern: string;
+ allIndices: MatchedIndex[];
+ remoteClustersExist: boolean;
+ isInitiallyLoadingIndices: boolean;
+ isIncludingSystemIndices: boolean;
+ toasts: EuiGlobalToastListToast[];
+}
+
+export class CreateIndexPatternWizard extends Component<
+ CreateIndexPatternWizardProps,
+ CreateIndexPatternWizardState
+> {
+ state = {
+ step: 1,
+ indexPattern: '',
+ allIndices: [],
+ remoteClustersExist: false,
+ isInitiallyLoadingIndices: true,
+ isIncludingSystemIndices: false,
+ toasts: [],
+ };
async UNSAFE_componentWillMount() {
this.fetchData();
}
- catchAndWarn = async (asyncFn, errorValue, errorMsg) => {
+ catchAndWarn = async (
+ asyncFn: Promise,
+ errorValue: [] | string[],
+ errorMsg: ReactElement
+ ) => {
try {
return await asyncFn;
} catch (errors) {
@@ -109,22 +129,26 @@ export class CreateIndexPatternWizard extends Component {
// query local and remote indices, updating state independently
ensureMinimumTime(
this.catchAndWarn(
- getIndices(services.es, this.indexPatternCreationType, `*`, MAX_SEARCH_SIZE),
+ getIndices(services.es, services.indexPatternCreationType, `*`, MAX_SEARCH_SIZE),
[],
indicesFailMsg
)
- ).then(allIndices => this.setState({ allIndices, isInitiallyLoadingIndices: false }));
+ ).then((allIndices: MatchedIndex[]) =>
+ this.setState({ allIndices, isInitiallyLoadingIndices: false })
+ );
this.catchAndWarn(
// if we get an error from remote cluster query, supply fallback value that allows user entry.
// ['a'] is fallback value
- getIndices(services.es, this.indexPatternCreationType, `*:*`, 1),
+ getIndices(services.es, services.indexPatternCreationType, `*:*`, 1),
['a'],
clustersFailMsg
- ).then(remoteIndices => this.setState({ remoteClustersExist: !!remoteIndices.length }));
+ ).then((remoteIndices: string[] | MatchedIndex[]) =>
+ this.setState({ remoteClustersExist: !!remoteIndices.length })
+ );
};
- createIndexPattern = async (timeFieldName, indexPatternId) => {
+ createIndexPattern = async (timeFieldName: string | undefined, indexPatternId: string) => {
const { services } = this.props;
const { indexPattern } = this.state;
@@ -134,13 +158,13 @@ export class CreateIndexPatternWizard extends Component {
id: indexPatternId,
title: indexPattern,
timeFieldName,
- ...this.indexPatternCreationType.getIndexPatternMappings(),
+ ...services.indexPatternCreationType.getIndexPatternMappings(),
});
const createdId = await emptyPattern.create();
if (!createdId) {
const confirmMessage = i18n.translate('kbn.management.indexPattern.titleExistsLabel', {
- values: { title: this.title },
+ values: { title: emptyPattern.title },
defaultMessage: "An index pattern with the title '{title}' already exists.",
});
@@ -157,15 +181,15 @@ export class CreateIndexPatternWizard extends Component {
}
}
- if (!services.config.get('defaultIndex')) {
- await services.config.set('defaultIndex', createdId);
+ if (!services.uiSettings.get('defaultIndex')) {
+ await services.uiSettings.set('defaultIndex', createdId);
}
services.indexPatterns.clearCache(createdId);
services.changeUrl(`/management/kibana/index_patterns/${createdId}`);
};
- goToTimeFieldStep = indexPattern => {
+ goToTimeFieldStep = (indexPattern: string) => {
this.setState({ step: 2, indexPattern });
};
@@ -174,22 +198,23 @@ export class CreateIndexPatternWizard extends Component {
};
onChangeIncludingSystemIndices = () => {
- this.setState(state => ({
- isIncludingSystemIndices: !state.isIncludingSystemIndices,
+ this.setState(prevState => ({
+ isIncludingSystemIndices: !prevState.isIncludingSystemIndices,
}));
};
renderHeader() {
const { isIncludingSystemIndices } = this.state;
+ const { services } = this.props;
return (
);
}
@@ -208,7 +233,7 @@ export class CreateIndexPatternWizard extends Component {
return ;
}
- const hasDataIndices = allIndices.some(({ name }) => !name.startsWith('.'));
+ const hasDataIndices = allIndices.some(({ name }: MatchedIndex) => !name.startsWith('.'));
if (!hasDataIndices && !isIncludingSystemIndices && !remoteClustersExist) {
return ;
}
@@ -222,7 +247,7 @@ export class CreateIndexPatternWizard extends Component {
isIncludingSystemIndices={isIncludingSystemIndices}
esService={services.es}
savedObjectsClient={services.savedObjectsClient}
- indexPatternCreationType={this.indexPatternCreationType}
+ indexPatternCreationType={services.indexPatternCreationType}
goToNextStep={this.goToTimeFieldStep}
uiSettings={services.uiSettings}
/>
@@ -237,7 +262,7 @@ export class CreateIndexPatternWizard extends Component {
indexPatternsService={services.indexPatterns}
goToPreviousStep={this.goToIndexPatternStep}
createIndexPattern={this.createIndexPattern}
- indexPatternCreationType={this.indexPatternCreationType}
+ indexPatternCreationType={services.indexPatternCreationType}
/>
);
}
@@ -245,9 +270,9 @@ export class CreateIndexPatternWizard extends Component {
return null;
}
- removeToast = removedToast => {
+ removeToast = (id: string) => {
this.setState(prevState => ({
- toasts: prevState.toasts.filter(toast => toast.id !== removedToast.id),
+ toasts: prevState.toasts.filter(toast => toast.id !== id),
}));
};
@@ -263,7 +288,9 @@ export class CreateIndexPatternWizard extends Component {
{
+ this.removeToast(id);
+ }}
toastLifeTimeMs={6000}
/>
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js
index 47cb773258cb..ed1fc026c560 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js
@@ -36,17 +36,15 @@ uiRoutes.when('/management/kibana/index_pattern', {
$routeParams.type
);
const services = {
- config: npStart.core.uiSettings,
+ uiSettings: npStart.core.uiSettings,
es: npStart.plugins.data.search.__LEGACY.esClient,
indexPatterns: npStart.plugins.data.indexPatterns,
- $http: npStart.core.http,
savedObjectsClient: npStart.core.savedObjects.client,
indexPatternCreationType,
changeUrl: url => {
$scope.$evalAsync(() => kbnUrl.changePath(url));
},
openConfirm: npStart.core.overlays.openConfirm,
- uiSettings: npStart.core.uiSettings,
};
const initialQuery = $routeParams.id ? decodeURIComponent($routeParams.id) : undefined;
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts
index 40583af7177f..b1500f8303b6 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts
@@ -20,7 +20,7 @@
import { getIndices } from './get_indices';
import { IndexPatternCreationConfig } from '../../../../../../../../../plugins/index_pattern_management/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { LegacyApiCaller } from '../../../../../../../../../plugins/data/public/search';
+import { LegacyApiCaller } from '../../../../../../../../../plugins/data/public/search/legacy';
export const successfulResponse = {
hits: {
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.html b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.html
index fee8525a6af4..2decaf423183 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.html
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.html
@@ -1,11 +1,5 @@
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js
index 3569e9caf4e2..95d6cb6878e5 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js
@@ -34,6 +34,8 @@ import { FieldEditor } from 'ui/field_editor';
import { I18nContext } from 'ui/i18n';
import { i18n } from '@kbn/i18n';
+import { IndexHeader } from '../index_header';
+
const REACT_FIELD_EDITOR_ID = 'reactFieldEditor';
const renderFieldEditor = (
$scope,
@@ -49,6 +51,7 @@ const renderFieldEditor = (
render(
+
-
+
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js
index c65054f583ef..69184a513f53 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js
@@ -18,7 +18,7 @@
*/
import _ from 'lodash';
-import './index_header';
+import { IndexHeader } from './index_header';
import './create_edit_field';
import { docTitle } from 'ui/doc_title';
import { KbnUrlProvider } from 'ui/url';
@@ -44,6 +44,7 @@ import { createEditIndexPatternPageStateContainer } from './edit_index_pattern_s
const REACT_SOURCE_FILTERS_DOM_ELEMENT_ID = 'reactSourceFiltersTable';
const REACT_INDEXED_FIELDS_DOM_ELEMENT_ID = 'reactIndexedFieldsTable';
const REACT_SCRIPTED_FIELDS_DOM_ELEMENT_ID = 'reactScriptedFieldsTable';
+const REACT_INDEX_HEADER_DOM_ELEMENT_ID = 'reactIndexHeader';
const TAB_INDEXED_FIELDS = 'indexedFields';
const TAB_SCRIPTED_FIELDS = 'scriptedFields';
@@ -160,6 +161,33 @@ function destroyIndexedFieldsTable() {
node && unmountComponentAtNode(node);
}
+function destroyIndexHeader() {
+ const node = document.getElementById(REACT_INDEX_HEADER_DOM_ELEMENT_ID);
+ node && unmountComponentAtNode(node);
+}
+
+function renderIndexHeader($scope, config) {
+ $scope.$$postDigest(() => {
+ const node = document.getElementById(REACT_INDEX_HEADER_DOM_ELEMENT_ID);
+ if (!node) {
+ return;
+ }
+
+ render(
+
+
+ ,
+ node
+ );
+ });
+}
+
function handleTabChange($scope, newTab) {
destroyIndexedFieldsTable();
destroySourceFiltersTable();
@@ -391,6 +419,9 @@ uiModules
destroyIndexedFieldsTable();
destroyScriptedFieldsTable();
destroySourceFiltersTable();
+ destroyIndexHeader();
destroyState();
});
+
+ renderIndexHeader($scope, config);
});
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.ts
similarity index 94%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.ts
index 7c288286bd61..44c05a55b36f 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.ts
@@ -17,4 +17,4 @@
* under the License.
*/
-import './index_header';
+export { IndexHeader } from './index_header';
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.html b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.html
deleted file mode 100644
index d6b91d96f13d..000000000000
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.html
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
- {{indexPattern.title}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.js
deleted file mode 100644
index 87bce06c1146..000000000000
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.js
+++ /dev/null
@@ -1,40 +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 { uiModules } from 'ui/modules';
-import template from './index_header.html';
-uiModules.get('apps/management').directive('kbnManagementIndexPatternsHeader', function(config) {
- return {
- restrict: 'E',
- template,
- replace: true,
- scope: {
- indexPattern: '=',
- setDefault: '&',
- refreshFields: '&',
- delete: '&',
- },
- link: function($scope, $el, attrs) {
- $scope.delete = attrs.delete ? $scope.delete : null;
- $scope.setDefault = attrs.setDefault ? $scope.setDefault : null;
- $scope.refreshFields = attrs.refreshFields ? $scope.refreshFields : null;
- config.bindToScope($scope, 'defaultIndex');
- },
- };
-});
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.tsx
new file mode 100644
index 000000000000..866d10ecb0e1
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.tsx
@@ -0,0 +1,134 @@
+/*
+ * 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 React from 'react';
+import { i18n } from '@kbn/i18n';
+import {
+ EuiFlexGroup,
+ EuiToolTip,
+ EuiFlexItem,
+ EuiIcon,
+ EuiTitle,
+ EuiButtonIcon,
+} from '@elastic/eui';
+import { IIndexPattern } from '../../../../../../../../../plugins/data/public';
+
+interface IndexHeaderProps {
+ defaultIndex: string;
+ indexPattern: IIndexPattern;
+ setDefault?: () => void;
+ refreshFields?: () => void;
+ deleteIndexPattern?: () => void;
+}
+
+const setDefaultAriaLabel = i18n.translate('kbn.management.editIndexPattern.setDefaultAria', {
+ defaultMessage: 'Set as default index.',
+});
+
+const setDefaultTooltip = i18n.translate('kbn.management.editIndexPattern.setDefaultTooltip', {
+ defaultMessage: 'Set as default index.',
+});
+
+const refreshAriaLabel = i18n.translate('kbn.management.editIndexPattern.refreshAria', {
+ defaultMessage: 'Reload field list.',
+});
+
+const refreshTooltip = i18n.translate('kbn.management.editIndexPattern.refreshTooltip', {
+ defaultMessage: 'Refresh field list.',
+});
+
+const removeAriaLabel = i18n.translate('kbn.management.editIndexPattern.removeAria', {
+ defaultMessage: 'Remove index pattern.',
+});
+
+const removeTooltip = i18n.translate('kbn.management.editIndexPattern.removeTooltip', {
+ defaultMessage: 'Remove index pattern.',
+});
+
+export function IndexHeader({
+ defaultIndex,
+ indexPattern,
+ setDefault,
+ refreshFields,
+ deleteIndexPattern,
+}: IndexHeaderProps) {
+ return (
+
+
+
+ {defaultIndex === indexPattern.id && (
+
+
+
+ )}
+
+
+ {indexPattern.title}
+
+
+
+
+
+
+ {setDefault && (
+
+
+
+
+
+ )}
+
+ {refreshFields && (
+
+
+
+
+
+ )}
+
+ {deleteIndexPattern && (
+
+
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/__snapshots__/indexed_fields_table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap
similarity index 90%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/__snapshots__/indexed_fields_table.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap
index b38036a0c2bf..db2a032b1e4d 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/__snapshots__/indexed_fields_table.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap
@@ -16,9 +16,10 @@ exports[`IndexedFieldsTable should filter based on the query bar 1`] = `
"excluded": false,
"format": undefined,
"indexPattern": undefined,
- "info": undefined,
+ "info": Array [],
"name": "Elastic",
"searchable": true,
+ "type": "name",
},
]
}
@@ -42,7 +43,7 @@ exports[`IndexedFieldsTable should filter based on the type filter 1`] = `
"excluded": false,
"format": undefined,
"indexPattern": undefined,
- "info": undefined,
+ "info": Array [],
"name": "timestamp",
"type": "date",
},
@@ -68,16 +69,17 @@ exports[`IndexedFieldsTable should render normally 1`] = `
"excluded": false,
"format": undefined,
"indexPattern": undefined,
- "info": undefined,
+ "info": Array [],
"name": "Elastic",
"searchable": true,
+ "type": "name",
},
Object {
"displayName": "timestamp",
"excluded": false,
"format": undefined,
"indexPattern": undefined,
- "info": undefined,
+ "info": Array [],
"name": "timestamp",
"type": "date",
},
@@ -86,7 +88,7 @@ exports[`IndexedFieldsTable should render normally 1`] = `
"excluded": false,
"format": undefined,
"indexPattern": undefined,
- "info": undefined,
+ "info": Array [],
"name": "conflictingField",
"type": "conflict",
},
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap
similarity index 92%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap
index f3aa2c5da4b6..2d51b1722cfb 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap
@@ -98,19 +98,26 @@ exports[`Table should render normally 1`] = `
Array [
Object {
"displayName": "Elastic",
- "info": Object {},
+ "excluded": false,
+ "format": "",
+ "info": Array [],
"name": "Elastic",
"searchable": true,
+ "type": "name",
},
Object {
"displayName": "timestamp",
- "info": Object {},
+ "excluded": false,
+ "format": "YYYY-MM-DD",
+ "info": Array [],
"name": "timestamp",
"type": "date",
},
Object {
"displayName": "conflictingField",
- "info": Object {},
+ "excluded": false,
+ "format": "",
+ "info": Array [],
"name": "conflictingField",
"type": "conflict",
},
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.js
deleted file mode 100644
index 29e160cf1c18..000000000000
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.js
+++ /dev/null
@@ -1,231 +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 React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-
-import { EuiIcon, EuiInMemoryTable, EuiIconTip } from '@elastic/eui';
-
-import { i18n } from '@kbn/i18n';
-
-export class Table extends PureComponent {
- static propTypes = {
- indexPattern: PropTypes.object.isRequired,
- items: PropTypes.array.isRequired,
- editField: PropTypes.func.isRequired,
- };
-
- renderBooleanTemplate(value, label) {
- return value ? : ;
- }
-
- renderFieldName(name, field) {
- const { indexPattern } = this.props;
-
- const infoLabel = i18n.translate(
- 'kbn.management.editIndexPattern.fields.table.additionalInfoAriaLabel',
- { defaultMessage: 'Additional field information' }
- );
- const timeLabel = i18n.translate(
- 'kbn.management.editIndexPattern.fields.table.primaryTimeAriaLabel',
- { defaultMessage: 'Primary time field' }
- );
- const timeContent = i18n.translate(
- 'kbn.management.editIndexPattern.fields.table.primaryTimeTooltip',
- { defaultMessage: 'This field represents the time that events occurred.' }
- );
-
- return (
-
- {name}
- {field.info && field.info.length ? (
-
-
- (
- {info}
- ))}
- />
-
- ) : null}
- {indexPattern.timeFieldName === name ? (
-
-
-
-
- ) : null}
-
- );
- }
-
- renderFieldType(type, isConflict) {
- const label = i18n.translate('kbn.management.editIndexPattern.fields.table.multiTypeAria', {
- defaultMessage: 'Multiple type field',
- });
- const content = i18n.translate(
- 'kbn.management.editIndexPattern.fields.table.multiTypeTooltip',
- {
- defaultMessage:
- 'The type of this field changes across indices. It is unavailable for many analysis functions.',
- }
- );
-
- return (
-
- {type}
- {isConflict ? (
-
-
-
-
- ) : (
- ''
- )}
-
- );
- }
-
- render() {
- const { items, editField } = this.props;
-
- const pagination = {
- initialPageSize: 10,
- pageSizeOptions: [5, 10, 25, 50],
- };
-
- const columns = [
- {
- field: 'displayName',
- name: i18n.translate('kbn.management.editIndexPattern.fields.table.nameHeader', {
- defaultMessage: 'Name',
- }),
- dataType: 'string',
- sortable: true,
- render: (value, field) => {
- return this.renderFieldName(value, field);
- },
- width: '38%',
- 'data-test-subj': 'indexedFieldName',
- },
- {
- field: 'type',
- name: i18n.translate('kbn.management.editIndexPattern.fields.table.typeHeader', {
- defaultMessage: 'Type',
- }),
- dataType: 'string',
- sortable: true,
- render: value => {
- return this.renderFieldType(value, value === 'conflict');
- },
- 'data-test-subj': 'indexedFieldType',
- },
- {
- field: 'format',
- name: i18n.translate('kbn.management.editIndexPattern.fields.table.formatHeader', {
- defaultMessage: 'Format',
- }),
- dataType: 'string',
- sortable: true,
- },
- {
- field: 'searchable',
- name: i18n.translate('kbn.management.editIndexPattern.fields.table.searchableHeader', {
- defaultMessage: 'Searchable',
- }),
- description: i18n.translate(
- 'kbn.management.editIndexPattern.fields.table.searchableDescription',
- { defaultMessage: 'These fields can be used in the filter bar' }
- ),
- dataType: 'boolean',
- sortable: true,
- render: value =>
- this.renderBooleanTemplate(
- value,
- i18n.translate('kbn.management.editIndexPattern.fields.table.isSearchableAria', {
- defaultMessage: 'Is searchable',
- })
- ),
- },
- {
- field: 'aggregatable',
- name: i18n.translate('kbn.management.editIndexPattern.fields.table.aggregatableLabel', {
- defaultMessage: 'Aggregatable',
- }),
- description: i18n.translate(
- 'kbn.management.editIndexPattern.fields.table.aggregatableDescription',
- { defaultMessage: 'These fields can be used in visualization aggregations' }
- ),
- dataType: 'boolean',
- sortable: true,
- render: value =>
- this.renderBooleanTemplate(
- value,
- i18n.translate('kbn.management.editIndexPattern.fields.table.isAggregatableAria', {
- defaultMessage: 'Is aggregatable',
- })
- ),
- },
- {
- field: 'excluded',
- name: i18n.translate('kbn.management.editIndexPattern.fields.table.excludedLabel', {
- defaultMessage: 'Excluded',
- }),
- description: i18n.translate(
- 'kbn.management.editIndexPattern.fields.table.excludedDescription',
- { defaultMessage: 'Fields that are excluded from _source when it is fetched' }
- ),
- dataType: 'boolean',
- sortable: true,
- render: value =>
- this.renderBooleanTemplate(
- value,
- i18n.translate('kbn.management.editIndexPattern.fields.table.isExcludedAria', {
- defaultMessage: 'Is excluded',
- })
- ),
- },
- {
- name: '',
- actions: [
- {
- name: i18n.translate('kbn.management.editIndexPattern.fields.table.editLabel', {
- defaultMessage: 'Edit',
- }),
- description: i18n.translate(
- 'kbn.management.editIndexPattern.fields.table.editDescription',
- { defaultMessage: 'Edit' }
- ),
- icon: 'pencil',
- onClick: editField,
- type: 'icon',
- 'data-test-subj': 'editFieldFormat',
- },
- ],
- width: '40px',
- },
- ];
-
- return (
-
- );
- }
-}
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/table.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx
similarity index 66%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/table.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx
index 4fd9ef7485bd..d0479a9a9e03 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/table.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx
@@ -19,31 +19,53 @@
import React from 'react';
import { shallow } from 'enzyme';
-import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers';
-
-import { Table } from '../table';
+import { IIndexPattern } from '../../../../../../../../../../../plugins/data/public';
+import { IndexedFieldItem } from '../../types';
+import { Table } from './table';
const indexPattern = {
timeFieldName: 'timestamp',
-};
-
-const items = [
- { name: 'Elastic', displayName: 'Elastic', searchable: true, info: {} },
- { name: 'timestamp', displayName: 'timestamp', type: 'date', info: {} },
- { name: 'conflictingField', displayName: 'conflictingField', type: 'conflict', info: {} },
+} as IIndexPattern;
+
+const items: IndexedFieldItem[] = [
+ {
+ name: 'Elastic',
+ displayName: 'Elastic',
+ searchable: true,
+ info: [],
+ type: 'name',
+ excluded: false,
+ format: '',
+ },
+ {
+ name: 'timestamp',
+ displayName: 'timestamp',
+ type: 'date',
+ info: [],
+ excluded: false,
+ format: 'YYYY-MM-DD',
+ },
+ {
+ name: 'conflictingField',
+ displayName: 'conflictingField',
+ type: 'conflict',
+ info: [],
+ excluded: false,
+ format: '',
+ },
];
describe('Table', () => {
- it('should render normally', async () => {
- const component = shallowWithI18nProvider(
+ test('should render normally', () => {
+ const component = shallow(
{}} />
);
expect(component).toMatchSnapshot();
});
- it('should render normal field name', async () => {
- const component = shallowWithI18nProvider(
+ test('should render normal field name', () => {
+ const component = shallow(
{}} />
);
@@ -51,8 +73,8 @@ describe('Table', () => {
expect(tableCell).toMatchSnapshot();
});
- it('should render timestamp field name', async () => {
- const component = shallowWithI18nProvider(
+ test('should render timestamp field name', () => {
+ const component = shallow(
{}} />
);
@@ -60,8 +82,8 @@ describe('Table', () => {
expect(tableCell).toMatchSnapshot();
});
- it('should render the boolean template (true)', async () => {
- const component = shallowWithI18nProvider(
+ test('should render the boolean template (true)', () => {
+ const component = shallow(
{}} />
);
@@ -69,8 +91,8 @@ describe('Table', () => {
expect(tableCell).toMatchSnapshot();
});
- it('should render the boolean template (false)', async () => {
- const component = shallowWithI18nProvider(
+ test('should render the boolean template (false)', () => {
+ const component = shallow(
{}} />
);
@@ -78,8 +100,8 @@ describe('Table', () => {
expect(tableCell).toMatchSnapshot();
});
- it('should render normal type', async () => {
- const component = shallowWithI18nProvider(
+ test('should render normal type', () => {
+ const component = shallow(
{}} />
);
@@ -87,8 +109,8 @@ describe('Table', () => {
expect(tableCell).toMatchSnapshot();
});
- it('should render conflicting type', async () => {
- const component = shallowWithI18nProvider(
+ test('should render conflicting type', () => {
+ const component = shallow(
{}} />
);
@@ -96,10 +118,10 @@ describe('Table', () => {
expect(tableCell).toMatchSnapshot();
});
- it('should allow edits', () => {
+ test('should allow edits', () => {
const editField = jest.fn();
- const component = shallowWithI18nProvider(
+ const component = shallow(
);
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.tsx
new file mode 100644
index 000000000000..aa8e8b8e13a0
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.tsx
@@ -0,0 +1,281 @@
+/*
+ * 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 React, { PureComponent } from 'react';
+
+import { EuiIcon, EuiInMemoryTable, EuiIconTip, EuiBasicTableColumn } from '@elastic/eui';
+
+import { i18n } from '@kbn/i18n';
+
+import { IIndexPattern } from '../../../../../../../../../../../plugins/data/public';
+import { IndexedFieldItem } from '../../types';
+
+// localized labels
+const additionalInfoAriaLabel = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.additionalInfoAriaLabel',
+ { defaultMessage: 'Additional field information' }
+);
+
+const primaryTimeAriaLabel = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.primaryTimeAriaLabel',
+ { defaultMessage: 'Primary time field' }
+);
+
+const primaryTimeTooltip = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.primaryTimeTooltip',
+ { defaultMessage: 'This field represents the time that events occurred.' }
+);
+
+const multiTypeAriaLabel = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.multiTypeAria',
+ {
+ defaultMessage: 'Multiple type field',
+ }
+);
+
+const multiTypeTooltip = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.multiTypeTooltip',
+ {
+ defaultMessage:
+ 'The type of this field changes across indices. It is unavailable for many analysis functions.',
+ }
+);
+
+const nameHeader = i18n.translate('kbn.management.editIndexPattern.fields.table.nameHeader', {
+ defaultMessage: 'Name',
+});
+
+const typeHeader = i18n.translate('kbn.management.editIndexPattern.fields.table.typeHeader', {
+ defaultMessage: 'Type',
+});
+
+const formatHeader = i18n.translate('kbn.management.editIndexPattern.fields.table.formatHeader', {
+ defaultMessage: 'Format',
+});
+
+const searchableHeader = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.searchableHeader',
+ {
+ defaultMessage: 'Searchable',
+ }
+);
+
+const searchableDescription = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.searchableDescription',
+ { defaultMessage: 'These fields can be used in the filter bar' }
+);
+
+const isSearchableAriaLabel = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.isSearchableAria',
+ {
+ defaultMessage: 'Is searchable',
+ }
+);
+
+const aggregatableLabel = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.aggregatableLabel',
+ {
+ defaultMessage: 'Aggregatable',
+ }
+);
+
+const aggregatableDescription = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.aggregatableDescription',
+ { defaultMessage: 'These fields can be used in visualization aggregations' }
+);
+
+const isAggregatableAriaLabel = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.isAggregatableAria',
+ {
+ defaultMessage: 'Is aggregatable',
+ }
+);
+
+const excludedLabel = i18n.translate('kbn.management.editIndexPattern.fields.table.excludedLabel', {
+ defaultMessage: 'Excluded',
+});
+
+const excludedDescription = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.excludedDescription',
+ { defaultMessage: 'Fields that are excluded from _source when it is fetched' }
+);
+
+const isExcludedAriaLabel = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.isExcludedAria',
+ {
+ defaultMessage: 'Is excluded',
+ }
+);
+
+const editLabel = i18n.translate('kbn.management.editIndexPattern.fields.table.editLabel', {
+ defaultMessage: 'Edit',
+});
+
+const editDescription = i18n.translate(
+ 'kbn.management.editIndexPattern.fields.table.editDescription',
+ { defaultMessage: 'Edit' }
+);
+
+interface IndexedFieldProps {
+ indexPattern: IIndexPattern;
+ items: IndexedFieldItem[];
+ editField: (field: IndexedFieldItem) => void;
+}
+
+export class Table extends PureComponent {
+ renderBooleanTemplate(value: string, arialLabel: string) {
+ return value ? : ;
+ }
+
+ renderFieldName(name: string, field: IndexedFieldItem) {
+ const { indexPattern } = this.props;
+
+ return (
+
+ {name}
+ {field.info && field.info.length ? (
+
+
+ (
+ {info}
+ ))}
+ />
+
+ ) : null}
+ {indexPattern.timeFieldName === name ? (
+
+
+
+
+ ) : null}
+
+ );
+ }
+
+ renderFieldType(type: string, isConflict: boolean) {
+ return (
+
+ {type}
+ {isConflict ? (
+
+
+
+
+ ) : (
+ ''
+ )}
+
+ );
+ }
+
+ render() {
+ const { items, editField } = this.props;
+
+ const pagination = {
+ initialPageSize: 10,
+ pageSizeOptions: [5, 10, 25, 50],
+ };
+
+ const columns: Array> = [
+ {
+ field: 'displayName',
+ name: nameHeader,
+ dataType: 'string',
+ sortable: true,
+ render: (value: string, field: IndexedFieldItem) => {
+ return this.renderFieldName(value, field);
+ },
+ width: '38%',
+ 'data-test-subj': 'indexedFieldName',
+ },
+ {
+ field: 'type',
+ name: typeHeader,
+ dataType: 'string',
+ sortable: true,
+ render: (value: string) => {
+ return this.renderFieldType(value, value === 'conflict');
+ },
+ 'data-test-subj': 'indexedFieldType',
+ },
+ {
+ field: 'format',
+ name: formatHeader,
+ dataType: 'string',
+ sortable: true,
+ },
+ {
+ field: 'searchable',
+ name: searchableHeader,
+ description: searchableDescription,
+ dataType: 'boolean',
+ sortable: true,
+ render: (value: string) => this.renderBooleanTemplate(value, isSearchableAriaLabel),
+ },
+ {
+ field: 'aggregatable',
+ name: aggregatableLabel,
+ description: aggregatableDescription,
+ dataType: 'boolean',
+ sortable: true,
+ render: (value: string) => this.renderBooleanTemplate(value, isAggregatableAriaLabel),
+ },
+ {
+ field: 'excluded',
+ name: excludedLabel,
+ description: excludedDescription,
+ dataType: 'boolean',
+ sortable: true,
+ render: (value: string) => this.renderBooleanTemplate(value, isExcludedAriaLabel),
+ },
+ {
+ name: '',
+ actions: [
+ {
+ name: editLabel,
+ description: editDescription,
+ icon: 'pencil',
+ onClick: editField,
+ type: 'icon',
+ 'data-test-subj': 'editFieldFormat',
+ },
+ ],
+ width: '40px',
+ },
+ ];
+
+ return (
+
+ );
+ }
+}
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/indexed_fields_table.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx
similarity index 70%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/indexed_fields_table.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx
index 26e271ea477d..f8b78a92e098 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/indexed_fields_table.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx
@@ -19,8 +19,8 @@
import React from 'react';
import { shallow } from 'enzyme';
-
-import { IndexedFieldsTable } from '../indexed_fields_table';
+import { IndexPatternField, IIndexPattern } from '../../../../../../../../../plugins/data/public';
+import { IndexedFieldsTable } from './indexed_fields_table';
jest.mock('@elastic/eui', () => ({
EuiFlexGroup: 'eui-flex-group',
@@ -29,7 +29,7 @@ jest.mock('@elastic/eui', () => ({
EuiInMemoryTable: 'eui-in-memory-table',
}));
-jest.mock('../components/table', () => ({
+jest.mock('./components/table', () => ({
// Note: this seems to fix React complaining about non lowercase attributes
Table: () => {
return 'table';
@@ -37,27 +37,37 @@ jest.mock('../components/table', () => ({
}));
const helpers = {
- redirectToRoute: () => {},
+ redirectToRoute: (obj: any) => {},
+ getFieldInfo: () => [],
};
const fields = [
- { name: 'Elastic', displayName: 'Elastic', searchable: true },
+ {
+ name: 'Elastic',
+ displayName: 'Elastic',
+ searchable: true,
+ type: 'name',
+ },
{ name: 'timestamp', displayName: 'timestamp', type: 'date' },
{ name: 'conflictingField', displayName: 'conflictingField', type: 'conflict' },
-];
+] as IndexPatternField[];
-const indexPattern = {
+const indexPattern = ({
getNonScriptedFields: () => fields,
-};
+} as unknown) as IIndexPattern;
describe('IndexedFieldsTable', () => {
- it('should render normally', async () => {
+ test('should render normally', async () => {
const component = shallow(
{}}
+ fieldWildcardMatcher={() => {
+ return () => false;
+ }}
+ indexedFieldTypeFilter=""
+ fieldFilter=""
/>
);
@@ -67,13 +77,17 @@ describe('IndexedFieldsTable', () => {
expect(component).toMatchSnapshot();
});
- it('should filter based on the query bar', async () => {
+ test('should filter based on the query bar', async () => {
const component = shallow(
{}}
+ fieldWildcardMatcher={() => {
+ return () => false;
+ }}
+ indexedFieldTypeFilter=""
+ fieldFilter=""
/>
);
@@ -84,13 +98,17 @@ describe('IndexedFieldsTable', () => {
expect(component).toMatchSnapshot();
});
- it('should filter based on the type filter', async () => {
+ test('should filter based on the type filter', async () => {
const component = shallow(
{}}
+ fieldWildcardMatcher={() => {
+ return () => false;
+ }}
+ indexedFieldTypeFilter=""
+ fieldFilter=""
/>
);
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx
similarity index 68%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx
index 074e5784f3da..7c2bb565615d 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx
@@ -18,26 +18,33 @@
*/
import React, { Component } from 'react';
-import PropTypes from 'prop-types';
import { createSelector } from 'reselect';
-
+import { IndexPatternField, IIndexPattern } from '../../../../../../../../../plugins/data/public';
import { Table } from './components/table';
import { getFieldFormat } from './lib';
+import { IndexedFieldItem } from './types';
-export class IndexedFieldsTable extends Component {
- static propTypes = {
- fields: PropTypes.array.isRequired,
- indexPattern: PropTypes.object.isRequired,
- fieldFilter: PropTypes.string,
- indexedFieldTypeFilter: PropTypes.string,
- helpers: PropTypes.shape({
- redirectToRoute: PropTypes.func.isRequired,
- getFieldInfo: PropTypes.func,
- }),
- fieldWildcardMatcher: PropTypes.func.isRequired,
+interface IndexedFieldsTableProps {
+ fields: IndexPatternField[];
+ indexPattern: IIndexPattern;
+ fieldFilter?: string;
+ indexedFieldTypeFilter?: string;
+ helpers: {
+ redirectToRoute: (obj: any) => void;
+ getFieldInfo: (indexPattern: IIndexPattern, field: string) => string[];
};
+ fieldWildcardMatcher: (filters: any[]) => (val: any) => boolean;
+}
+
+interface IndexedFieldsTableState {
+ fields: IndexedFieldItem[];
+}
- constructor(props) {
+export class IndexedFieldsTable extends Component<
+ IndexedFieldsTableProps,
+ IndexedFieldsTableState
+> {
+ constructor(props: IndexedFieldsTableProps) {
super(props);
this.state = {
@@ -45,7 +52,7 @@ export class IndexedFieldsTable extends Component {
};
}
- UNSAFE_componentWillReceiveProps(nextProps) {
+ UNSAFE_componentWillReceiveProps(nextProps: IndexedFieldsTableProps) {
if (nextProps.fields !== this.props.fields) {
this.setState({
fields: this.mapFields(nextProps.fields),
@@ -53,10 +60,11 @@ export class IndexedFieldsTable extends Component {
}
}
- mapFields(fields) {
+ mapFields(fields: IndexPatternField[]): IndexedFieldItem[] {
const { indexPattern, fieldWildcardMatcher, helpers } = this.props;
const sourceFilters =
- indexPattern.sourceFilters && indexPattern.sourceFilters.map(f => f.value);
+ indexPattern.sourceFilters &&
+ indexPattern.sourceFilters.map((f: Record) => f.value);
const fieldWildcardMatch = fieldWildcardMatcher(sourceFilters || []);
return (
@@ -76,9 +84,10 @@ export class IndexedFieldsTable extends Component {
}
getFilteredFields = createSelector(
- state => state.fields,
- (state, props) => props.fieldFilter,
- (state, props) => props.indexedFieldTypeFilter,
+ (state: IndexedFieldsTableState) => state.fields,
+ (state: IndexedFieldsTableState, props: IndexedFieldsTableProps) => props.fieldFilter,
+ (state: IndexedFieldsTableState, props: IndexedFieldsTableProps) =>
+ props.indexedFieldTypeFilter,
(fields, fieldFilter, indexedFieldTypeFilter) => {
if (fieldFilter) {
const normalizedFieldFilter = fieldFilter.toLowerCase();
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/__jest__/get_field_format.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.test.ts
similarity index 74%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/__jest__/get_field_format.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.test.ts
index 7090f7019991..fc7477c074ac 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/__jest__/get_field_format.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.test.ts
@@ -17,9 +17,10 @@
* under the License.
*/
-import { getFieldFormat } from '../get_field_format';
+import { IIndexPattern } from '../../../../../../../../../../plugins/data/public';
+import { getFieldFormat } from './get_field_format';
-const indexPattern = {
+const indexPattern = ({
fieldFormatMap: {
Elastic: {
type: {
@@ -27,26 +28,26 @@ const indexPattern = {
},
},
},
-};
+} as unknown) as IIndexPattern;
describe('getFieldFormat', () => {
- it('should handle no arguments', () => {
+ test('should handle no arguments', () => {
expect(getFieldFormat()).toEqual('');
});
- it('should handle no field name', () => {
+ test('should handle no field name', () => {
expect(getFieldFormat(indexPattern)).toEqual('');
});
- it('should handle empty name', () => {
+ test('should handle empty name', () => {
expect(getFieldFormat(indexPattern, '')).toEqual('');
});
- it('should handle undefined field name', () => {
+ test('should handle undefined field name', () => {
expect(getFieldFormat(indexPattern, 'none')).toEqual(undefined);
});
- it('should retrieve field format', () => {
+ test('should retrieve field format', () => {
expect(getFieldFormat(indexPattern, 'Elastic')).toEqual('string');
});
});
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.ts
similarity index 84%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.ts
index 9402694bb137..1d6f267430f0 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.ts
@@ -18,8 +18,9 @@
*/
import { get } from 'lodash';
+import { IIndexPattern } from '../../../../../../../../../../plugins/data/public';
-export function getFieldFormat(indexPattern, fieldName) {
+export function getFieldFormat(indexPattern?: IIndexPattern, fieldName?: string): string {
return indexPattern && fieldName
? get(indexPattern, ['fieldFormatMap', fieldName, 'type', 'title'])
: '';
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/types.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/types.ts
new file mode 100644
index 000000000000..f27f4608bf5d
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/types.ts
@@ -0,0 +1,25 @@
+/*
+ * 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 { IFieldType } from '../../../../../../../../../plugins/data/public';
+
+export interface IndexedFieldItem extends IFieldType {
+ info: string[];
+ excluded: boolean;
+}
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 079d82936bb5..000000000000
--- 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.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts
deleted file mode 100644
index 4ef2c9368971..000000000000
--- a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts
+++ /dev/null
@@ -1,28 +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 { PluginInitializerContext } from 'kibana/public';
-import { npSetup, npStart } from 'ui/new_platform';
-import { plugin } from './index';
-
-const instance = plugin({
- env: npSetup.plugins.kibanaLegacy.env,
-} as PluginInitializerContext);
-instance.setup(npSetup.core, npSetup.plugins);
-instance.start(npStart.core, npStart.plugins);
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 f6d73b987912..000000000000
--- 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/timelion/public/app.js b/src/legacy/core_plugins/timelion/public/app.js
index c15318d29e76..7f5c7d4664af 100644
--- a/src/legacy/core_plugins/timelion/public/app.js
+++ b/src/legacy/core_plugins/timelion/public/app.js
@@ -27,10 +27,11 @@ import { fatalError, toastNotifications } from 'ui/notify';
import { timefilter } from 'ui/timefilter';
import { npStart } from 'ui/new_platform';
import { getSavedSheetBreadcrumbs, getCreateBreadcrumbs } from './breadcrumbs';
-import { getTimezone } from '../../vis_type_timelion/public';
+import { getTimezone } from '../../../../plugins/vis_type_timelion/public';
import 'uiExports/savedObjectTypes';
+require('ui/i18n');
require('ui/autoload/all');
// TODO: remove ui imports completely (move to plugins)
@@ -57,7 +58,7 @@ require('plugins/timelion/directives/timelion_options_sheet');
document.title = 'Timelion - Kibana';
-const app = require('ui/modules').get('apps/timelion', []);
+const app = require('ui/modules').get('apps/timelion', ['i18n', 'ngSanitize']);
require('ui/routes').enable();
diff --git a/src/legacy/core_plugins/timelion/public/directives/timelion_expression_input.js b/src/legacy/core_plugins/timelion/public/directives/timelion_expression_input.js
index 57262fda55e4..35ac883e5d99 100644
--- a/src/legacy/core_plugins/timelion/public/directives/timelion_expression_input.js
+++ b/src/legacy/core_plugins/timelion/public/directives/timelion_expression_input.js
@@ -43,7 +43,7 @@
import _ from 'lodash';
import $ from 'jquery';
import PEG from 'pegjs';
-import grammar from 'raw-loader!../../../../../plugins/timelion/common/chain.peg';
+import grammar from 'raw-loader!../../../../../plugins/vis_type_timelion/common/chain.peg';
import timelionExpressionInputTemplate from './timelion_expression_input.html';
import {
SUGGESTION_TYPE,
@@ -52,7 +52,7 @@ import {
insertAtLocation,
} from './timelion_expression_input_helpers';
import { comboBoxKeyCodes } from '@elastic/eui';
-import { getArgValueSuggestions } from '../../../vis_type_timelion/public/helpers/arg_value_suggestions';
+import { npStart } from 'ui/new_platform';
const Parser = PEG.generate(grammar);
@@ -68,7 +68,7 @@ export function TimelionExpInput($http, $timeout) {
replace: true,
template: timelionExpressionInputTemplate,
link: function(scope, elem) {
- const argValueSuggestions = getArgValueSuggestions();
+ const argValueSuggestions = npStart.plugins.visTypeTimelion.getArgValueSuggestions();
const expressionInput = elem.find('[data-expression-input]');
const functionReference = {};
let suggestibleFunctionLocation = {};
diff --git a/src/legacy/core_plugins/timelion/public/panels/timechart/schema.ts b/src/legacy/core_plugins/timelion/public/panels/timechart/schema.ts
index cd40cbfa89ff..34b389f5ff4c 100644
--- a/src/legacy/core_plugins/timelion/public/panels/timechart/schema.ts
+++ b/src/legacy/core_plugins/timelion/public/panels/timechart/schema.ts
@@ -17,7 +17,8 @@
* under the License.
*/
-import '../../../../vis_type_timelion/public/flot';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import '../../../../../../plugins/vis_type_timelion/public/flot';
import _ from 'lodash';
import $ from 'jquery';
import moment from 'moment-timezone';
@@ -28,11 +29,14 @@ import {
calculateInterval,
DEFAULT_TIME_FORMAT,
// @ts-ignore
-} from '../../../../../../plugins/timelion/common/lib';
-import { tickFormatters } from '../../../../vis_type_timelion/public/helpers/tick_formatters';
+} from '../../../../../../plugins/vis_type_timelion/common/lib';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { tickFormatters } from '../../../../../../plugins/vis_type_timelion/public/helpers/tick_formatters';
import { TimelionVisualizationDependencies } from '../../plugin';
-import { xaxisFormatterProvider } from '../../../../vis_type_timelion/public/helpers/xaxis_formatter';
-import { generateTicksProvider } from '../../../../vis_type_timelion/public/helpers/tick_generator';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { xaxisFormatterProvider } from '../../../../../../plugins/vis_type_timelion/public/helpers/xaxis_formatter';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { generateTicksProvider } from '../../../../../../plugins/vis_type_timelion/public/helpers/tick_generator';
const DEBOUNCE_DELAY = 50;
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 3c00420e57d5..000000000000
--- 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 5c233d82fe50..000000000000
--- 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_markdown/public/legacy.ts b/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts
deleted file mode 100644
index 1cfc583f6e00..000000000000
--- a/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts
+++ /dev/null
@@ -1,33 +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 { PluginInitializerContext } from 'kibana/public';
-import { npSetup, npStart } from 'ui/new_platform';
-import { MarkdownPluginSetupDependencies } from './plugin';
-import { plugin } from '.';
-
-const plugins: Readonly = {
- expressions: npSetup.plugins.expressions,
- visualizations: npSetup.plugins.visualizations,
-};
-
-const pluginInstance = plugin({} as PluginInitializerContext);
-
-export const setup = pluginInstance.setup(npSetup.core, plugins);
-export const start = pluginInstance.start(npStart.core);
diff --git a/src/legacy/core_plugins/vis_type_metric/index.ts b/src/legacy/core_plugins/vis_type_metric/index.ts
deleted file mode 100644
index 8e6654e40f0f..000000000000
--- a/src/legacy/core_plugins/vis_type_metric/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 metricPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) =>
- new Plugin({
- id: 'metric_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 metricPluginInitializer;
diff --git a/src/legacy/core_plugins/vis_type_metric/package.json b/src/legacy/core_plugins/vis_type_metric/package.json
deleted file mode 100644
index e570261fc30d..000000000000
--- a/src/legacy/core_plugins/vis_type_metric/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "metric_vis",
- "version": "kibana"
-}
diff --git a/src/legacy/core_plugins/vis_type_metric/public/legacy.ts b/src/legacy/core_plugins/vis_type_metric/public/legacy.ts
deleted file mode 100644
index ba883601e5d6..000000000000
--- a/src/legacy/core_plugins/vis_type_metric/public/legacy.ts
+++ /dev/null
@@ -1,34 +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 { PluginInitializerContext } from 'kibana/public';
-import { npSetup, npStart } from 'ui/new_platform';
-import { MetricVisPluginSetupDependencies } from './plugin';
-import { plugin } from '.';
-
-const plugins: Readonly = {
- expressions: npSetup.plugins.expressions,
- visualizations: npSetup.plugins.visualizations,
- charts: npSetup.plugins.charts,
-};
-
-const pluginInstance = plugin({} as PluginInitializerContext);
-
-export const setup = pluginInstance.setup(npSetup.core, plugins);
-export const start = pluginInstance.start(npStart.core, { data: npStart.plugins.data });
diff --git a/src/legacy/core_plugins/vis_type_timelion/index.ts b/src/legacy/core_plugins/vis_type_timelion/index.ts
deleted file mode 100644
index 7bca5154c84f..000000000000
--- a/src/legacy/core_plugins/vis_type_timelion/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 timelionVisPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) =>
- new Plugin({
- id: 'timelion_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();
- },
- });
-
-// eslint-disable-next-line import/no-default-export
-export default timelionVisPluginInitializer;
diff --git a/src/legacy/core_plugins/vis_type_timelion/package.json b/src/legacy/core_plugins/vis_type_timelion/package.json
deleted file mode 100644
index 9b09f98ce6ca..000000000000
--- a/src/legacy/core_plugins/vis_type_timelion/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "timelion_vis",
- "version": "kibana"
-}
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/_index.scss b/src/legacy/core_plugins/vis_type_timelion/public/components/_index.scss
deleted file mode 100644
index 1d887f43ff9a..000000000000
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/_index.scss
+++ /dev/null
@@ -1,2 +0,0 @@
-@import './panel';
-@import './timelion_expression_input';
diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts
index a9b8c2937485..0d2f3528c901 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/server/logging/log_reporter.js b/src/legacy/server/logging/log_reporter.js
index 6e62a5ee284e..b784d03a5b86 100644
--- a/src/legacy/server/logging/log_reporter.js
+++ b/src/legacy/server/logging/log_reporter.js
@@ -30,7 +30,7 @@ import { LogInterceptor } from './log_interceptor';
// thrown every time we start the server.
// In order to keep using the legacy logger until we remove it I'm just adding
// a new hard limit here.
-process.stdout.setMaxListeners(15);
+process.stdout.setMaxListeners(25);
export function getLoggerStream({ events, config }) {
const squeeze = new Squeeze(events);
diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss
index 87006d9347de..aaed52f8b120 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/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts
index 80fb837258d4..5ae2e2348aaa 100644
--- a/src/legacy/ui/public/new_platform/new_platform.ts
+++ b/src/legacy/ui/public/new_platform/new_platform.ts
@@ -69,6 +69,7 @@ import {
VisualizationsSetup,
VisualizationsStart,
} from '../../../../plugins/visualizations/public';
+import { VisTypeTimelionPluginStart } from '../../../../plugins/vis_type_timelion/public';
import { MapsLegacyPluginSetup } from '../../../../plugins/maps_legacy/public';
export interface PluginsSetup {
@@ -116,6 +117,7 @@ export interface PluginsStart {
telemetry?: TelemetryPluginStart;
dashboard: DashboardStart;
savedObjectsManagement: SavedObjectsManagementPluginStart;
+ visTypeTimelion: VisTypeTimelionPluginStart;
indexPatternManagement: IndexPatternManagementStart;
}
diff --git a/src/legacy/ui/public/visualize/_index.scss b/src/legacy/ui/public/visualize/_index.scss
deleted file mode 100644
index d9761f741353..000000000000
--- a/src/legacy/ui/public/visualize/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import '../../../../plugins/visualizations/public/components/index';
diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs
index 1093153edbbf..4557d911620a 100644
--- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs
+++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs
@@ -78,10 +78,10 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) {
'{{this}}',
{{/each}}
'{{regularBundlePath}}/commons.bundle.js',
- {{!-- '{{regularBundlePath}}/plugin/data/data.plugin.js', --}}
- '{{regularBundlePath}}/plugin/kibanaUtils/kibanaUtils.plugin.js',
- '{{regularBundlePath}}/plugin/esUiShared/esUiShared.plugin.js',
- '{{regularBundlePath}}/plugin/kibanaReact/kibanaReact.plugin.js'
+ {{!-- '{{regularBundlePath}}/plugin:data/data.plugin.js', --}}
+ '{{regularBundlePath}}/plugin:kibanaUtils/kibanaUtils.plugin.js',
+ '{{regularBundlePath}}/plugin:esUiShared/esUiShared.plugin.js',
+ '{{regularBundlePath}}/plugin:kibanaReact/kibanaReact.plugin.js'
], function () {
load([
'{{regularBundlePath}}/{{appId}}.bundle.js',
diff --git a/src/optimize/bundles_route/bundles_route.js b/src/optimize/bundles_route/bundles_route.js
index 0c2e98b5acd6..f4e3108f80a3 100644
--- a/src/optimize/bundles_route/bundles_route.js
+++ b/src/optimize/bundles_route/bundles_route.js
@@ -79,8 +79,8 @@ export function createBundlesRoute({
),
...npUiPluginPublicDirs.map(({ id, path }) =>
buildRouteForBundles(
- `${basePublicPath}/bundles/plugin/${id}/`,
- `/bundles/plugin/${id}/`,
+ `${basePublicPath}/bundles/plugin:${id}/`,
+ `/bundles/plugin:${id}/`,
path,
fileHashCache
)
diff --git a/src/plugins/console/server/__tests__/proxy_route/proxy_fallback.test.ts b/src/plugins/console/server/__tests__/proxy_route/proxy_fallback.test.ts
new file mode 100644
index 000000000000..b226bad11a01
--- /dev/null
+++ b/src/plugins/console/server/__tests__/proxy_route/proxy_fallback.test.ts
@@ -0,0 +1,64 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { duration } from 'moment';
+import { getProxyRouteHandlerDeps } from './mocks';
+
+import { kibanaResponseFactory } from '../../../../../core/server';
+import { createHandler } from '../../routes/api/console/proxy/create_handler';
+import * as requestModule from '../../lib/proxy_request';
+
+describe('Console Proxy Route', () => {
+ afterEach(async () => {
+ jest.resetAllMocks();
+ });
+
+ describe('fallback behaviour', () => {
+ it('falls back to all configured endpoints regardless of error', async () => {
+ // Describe a situation where all three configured nodes reject
+ (requestModule.proxyRequest as jest.Mock).mockRejectedValueOnce(new Error('ECONNREFUSED'));
+ (requestModule.proxyRequest as jest.Mock).mockRejectedValueOnce(new Error('EHOSTUNREACH'));
+ (requestModule.proxyRequest as jest.Mock).mockRejectedValueOnce(new Error('ESOCKETTIMEDOUT'));
+
+ const handler = createHandler(
+ getProxyRouteHandlerDeps({
+ readLegacyESConfig: () => ({
+ requestTimeout: duration(30000),
+ customHeaders: {},
+ requestHeadersWhitelist: [],
+ hosts: ['http://localhost:9201', 'http://localhost:9202', 'http://localhost:9203'],
+ }),
+ })
+ );
+
+ const response = await handler(
+ {} as any,
+ {
+ headers: {},
+ query: { method: 'get', path: 'test' },
+ } as any,
+ kibanaResponseFactory
+ );
+
+ expect(response.status).toBe(502);
+ // Return the message from the ES node we attempted last.
+ expect(response.payload.message).toBe('ESOCKETTIMEDOUT');
+ });
+ });
+});
diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json
index 408b01c4cb8f..0da2c130b47c 100644
--- a/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json
+++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json
@@ -59,7 +59,61 @@
}
},
"transient": {
- "__scope_link": ".persistent"
+ "cluster": {
+ "routing": {
+ "allocation.enable": {
+ "__one_of": ["all", "primaries", "new_primaries", "none"]
+ },
+ "allocation.disk.threshold_enabled": { "__one_of": [false, true] },
+ "allocation.disk.watermark.low": "85%",
+ "allocation.disk.watermark.high": "90%",
+ "allocation.disk.reroute_interval": "60s",
+ "allocation.exclude": {
+ "_ip": "",
+ "_name": "",
+ "_host": "",
+ "_id": ""
+ },
+ "allocation.include": {
+ "_ip": "",
+ "_name": "",
+ "_host": "",
+ "_id": ""
+ },
+ "allocation.require": {
+ "_ip": "",
+ "_name": "",
+ "_host": "",
+ "_id": ""
+ },
+ "allocation.awareness.attributes": [],
+ "allocation.awareness.force": {
+ "*": {
+ "values": []
+ }
+ },
+ "allocation.allow_rebalance": {
+ "__one_of": [
+ "always",
+ "indices_primaries_active",
+ "indices_all_active"
+ ]
+ },
+ "allocation.cluster_concurrent_rebalance": 2,
+ "allocation.node_initial_primaries_recoveries": 4,
+ "allocation.node_concurrent_recoveries": 2,
+ "allocation.same_shard.host": { "__one_of": [false, true] }
+ }
+ },
+ "indices": {
+ "breaker": {
+ "total.limit": "70%",
+ "fielddata.limit": "60%",
+ "fielddata.overhead": 1.03,
+ "request.limit": "40%",
+ "request.overhead": 1.0
+ }
+ }
}
}
}
diff --git a/src/plugins/console/server/routes/api/console/proxy/create_handler.ts b/src/plugins/console/server/routes/api/console/proxy/create_handler.ts
index 50a9fcf03c20..9446289ff03e 100644
--- a/src/plugins/console/server/routes/api/console/proxy/create_handler.ts
+++ b/src/plugins/console/server/routes/api/console/proxy/create_handler.ts
@@ -175,10 +175,9 @@ export const createHandler = ({
break;
} catch (e) {
+ // If we reached here it means we hit a lower level network issue than just, for e.g., a 500.
+ // We try contacting another node in that case.
log.error(e);
- if (e.code !== 'ECONNREFUSED') {
- return response.internalError(e);
- }
if (idx === hosts.length - 1) {
log.warn(`Could not connect to any configured ES node [${hosts.join(', ')}]`);
return response.customError({
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 e71e4f1b1513..7210879c5eac 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 03a8a07d6b17..8f389bb031df 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/actions/clone_panel_action.test.tsx b/src/plugins/dashboard/public/application/actions/clone_panel_action.test.tsx
new file mode 100644
index 000000000000..17943333d25b
--- /dev/null
+++ b/src/plugins/dashboard/public/application/actions/clone_panel_action.test.tsx
@@ -0,0 +1,155 @@
+/*
+ * 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 { isErrorEmbeddable, IContainer } from '../../embeddable_plugin';
+import { DashboardContainer, DashboardPanelState } from '../embeddable';
+import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helpers';
+import {
+ CONTACT_CARD_EMBEDDABLE,
+ ContactCardEmbeddableFactory,
+ ContactCardEmbeddable,
+ ContactCardEmbeddableInput,
+ ContactCardEmbeddableOutput,
+} from '../../embeddable_plugin_test_samples';
+import { coreMock } from '../../../../../core/public/mocks';
+import { CoreStart } from 'kibana/public';
+import { ClonePanelAction } from '.';
+
+// eslint-disable-next-line
+import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks';
+
+const { setup, doStart } = embeddablePluginMock.createInstance();
+setup.registerEmbeddableFactory(
+ CONTACT_CARD_EMBEDDABLE,
+ new ContactCardEmbeddableFactory((() => null) as any, {} as any)
+);
+const start = doStart();
+
+let container: DashboardContainer;
+let embeddable: ContactCardEmbeddable;
+let coreStart: CoreStart;
+beforeEach(async () => {
+ coreStart = coreMock.createStart();
+ coreStart.savedObjects.client = {
+ ...coreStart.savedObjects.client,
+ get: jest.fn().mockImplementation(() => ({ attributes: { title: 'Holy moly' } })),
+ find: jest.fn().mockImplementation(() => ({ total: 15 })),
+ create: jest.fn().mockImplementation(() => ({ id: 'brandNewSavedObject' })),
+ };
+
+ const options = {
+ ExitFullScreenButton: () => null,
+ SavedObjectFinder: () => null,
+ application: {} as any,
+ embeddable: start,
+ inspector: {} as any,
+ notifications: {} as any,
+ overlays: coreStart.overlays,
+ savedObjectMetaData: {} as any,
+ uiActions: {} as any,
+ };
+ const input = getSampleDashboardInput({
+ panels: {
+ '123': getSampleDashboardPanel({
+ explicitInput: { firstName: 'Kibanana', id: '123' },
+ type: CONTACT_CARD_EMBEDDABLE,
+ }),
+ },
+ });
+ container = new DashboardContainer(input, options);
+
+ const contactCardEmbeddable = await container.addNewEmbeddable<
+ ContactCardEmbeddableInput,
+ ContactCardEmbeddableOutput,
+ ContactCardEmbeddable
+ >(CONTACT_CARD_EMBEDDABLE, {
+ firstName: 'Kibana',
+ });
+
+ if (isErrorEmbeddable(contactCardEmbeddable)) {
+ throw new Error('Failed to create embeddable');
+ } else {
+ embeddable = contactCardEmbeddable;
+ }
+});
+
+test('Clone adds a new embeddable', async () => {
+ const dashboard = embeddable.getRoot() as IContainer;
+ const originalPanelCount = Object.keys(dashboard.getInput().panels).length;
+ const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));
+ const action = new ClonePanelAction(coreStart);
+ await action.execute({ embeddable });
+ expect(Object.keys(container.getInput().panels).length).toEqual(originalPanelCount + 1);
+ const newPanelId = Object.keys(container.getInput().panels).find(
+ key => !originalPanelKeySet.has(key)
+ );
+ expect(newPanelId).toBeDefined();
+ const newPanel = container.getInput().panels[newPanelId!];
+ expect(newPanel.type).toEqual(embeddable.type);
+});
+
+test('Clones an embeddable without a saved object ID', async () => {
+ const dashboard = embeddable.getRoot() as IContainer;
+ const panel = dashboard.getInput().panels[embeddable.id] as DashboardPanelState;
+ const action = new ClonePanelAction(coreStart);
+ // @ts-ignore
+ const newPanel = await action.cloneEmbeddable(panel, embeddable.type);
+ expect(newPanel.type).toEqual(embeddable.type);
+});
+
+test('Clones an embeddable with a saved object ID', async () => {
+ const dashboard = embeddable.getRoot() as IContainer;
+ const panel = dashboard.getInput().panels[embeddable.id] as DashboardPanelState;
+ panel.explicitInput.savedObjectId = 'holySavedObjectBatman';
+ const action = new ClonePanelAction(coreStart);
+ // @ts-ignore
+ const newPanel = await action.cloneEmbeddable(panel, embeddable.type);
+ expect(coreStart.savedObjects.client.get).toHaveBeenCalledTimes(1);
+ expect(coreStart.savedObjects.client.find).toHaveBeenCalledTimes(1);
+ expect(coreStart.savedObjects.client.create).toHaveBeenCalledTimes(1);
+ expect(newPanel.type).toEqual(embeddable.type);
+});
+
+test('Gets a unique title ', async () => {
+ coreStart.savedObjects.client.find = jest.fn().mockImplementation(({ search }) => {
+ if (search === '"testFirstTitle"') return { total: 1 };
+ else if (search === '"testSecondTitle"') return { total: 41 };
+ else if (search === '"testThirdTitle"') return { total: 90 };
+ });
+ const action = new ClonePanelAction(coreStart);
+ // @ts-ignore
+ expect(await action.getUniqueTitle('testFirstTitle', embeddable.type)).toEqual(
+ 'testFirstTitle (copy)'
+ );
+ // @ts-ignore
+ expect(await action.getUniqueTitle('testSecondTitle (copy 39)', embeddable.type)).toEqual(
+ 'testSecondTitle (copy 40)'
+ );
+ // @ts-ignore
+ expect(await action.getUniqueTitle('testSecondTitle (copy 20)', embeddable.type)).toEqual(
+ 'testSecondTitle (copy 40)'
+ );
+ // @ts-ignore
+ expect(await action.getUniqueTitle('testThirdTitle', embeddable.type)).toEqual(
+ 'testThirdTitle (copy 89)'
+ );
+ // @ts-ignore
+ expect(await action.getUniqueTitle('testThirdTitle (copy 10000)', embeddable.type)).toEqual(
+ 'testThirdTitle (copy 89)'
+ );
+});
diff --git a/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx
new file mode 100644
index 000000000000..4d15e7e899fa
--- /dev/null
+++ b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx
@@ -0,0 +1,158 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import { CoreStart } from 'src/core/public';
+import uuid from 'uuid';
+import { ActionByType, IncompatibleActionError } from '../../ui_actions_plugin';
+import { ViewMode, PanelState, IEmbeddable } from '../../embeddable_plugin';
+import { SavedObject } from '../../../../saved_objects/public';
+import { PanelNotFoundError, EmbeddableInput } from '../../../../embeddable/public';
+import {
+ placePanelBeside,
+ IPanelPlacementBesideArgs,
+} from '../embeddable/panel/dashboard_panel_placement';
+import { DashboardPanelState, DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '..';
+
+export const ACTION_CLONE_PANEL = 'clonePanel';
+
+export interface ClonePanelActionContext {
+ embeddable: IEmbeddable;
+}
+
+export class ClonePanelAction implements ActionByType {
+ public readonly type = ACTION_CLONE_PANEL;
+ public readonly id = ACTION_CLONE_PANEL;
+ public order = 11;
+
+ constructor(private core: CoreStart) {}
+
+ public getDisplayName({ embeddable }: ClonePanelActionContext) {
+ if (!embeddable.getRoot() || !embeddable.getRoot().isContainer) {
+ throw new IncompatibleActionError();
+ }
+ return i18n.translate('dashboard.panel.clonePanel', {
+ defaultMessage: 'Clone panel',
+ });
+ }
+
+ public getIconType({ embeddable }: ClonePanelActionContext) {
+ if (!embeddable.getRoot() || !embeddable.getRoot().isContainer) {
+ throw new IncompatibleActionError();
+ }
+ return 'copy';
+ }
+
+ public async isCompatible({ embeddable }: ClonePanelActionContext) {
+ return Boolean(
+ embeddable.getInput()?.viewMode !== ViewMode.VIEW &&
+ embeddable.getRoot() &&
+ embeddable.getRoot().isContainer &&
+ embeddable.getRoot().type === DASHBOARD_CONTAINER_TYPE
+ );
+ }
+
+ public async execute({ embeddable }: ClonePanelActionContext) {
+ if (!embeddable.getRoot() || !embeddable.getRoot().isContainer) {
+ throw new IncompatibleActionError();
+ }
+
+ const dashboard = embeddable.getRoot() as DashboardContainer;
+ const panelToClone = dashboard.getInput().panels[embeddable.id] as DashboardPanelState;
+ if (!panelToClone) {
+ throw new PanelNotFoundError();
+ }
+
+ dashboard.showPlaceholderUntil(
+ this.cloneEmbeddable(panelToClone, embeddable.type),
+ placePanelBeside,
+ {
+ width: panelToClone.gridData.w,
+ height: panelToClone.gridData.h,
+ currentPanels: dashboard.getInput().panels,
+ placeBesideId: panelToClone.explicitInput.id,
+ } as IPanelPlacementBesideArgs
+ );
+ }
+
+ private async getUniqueTitle(rawTitle: string, embeddableType: string): Promise {
+ const clonedTag = i18n.translate('dashboard.panel.title.clonedTag', {
+ defaultMessage: 'copy',
+ });
+ const cloneRegex = new RegExp(`\\(${clonedTag}\\)`, 'g');
+ const cloneNumberRegex = new RegExp(`\\(${clonedTag} [0-9]+\\)`, 'g');
+ const baseTitle = rawTitle
+ .replace(cloneNumberRegex, '')
+ .replace(cloneRegex, '')
+ .trim();
+
+ const similarSavedObjects = await this.core.savedObjects.client.find({
+ type: embeddableType,
+ perPage: 0,
+ fields: ['title'],
+ searchFields: ['title'],
+ search: `"${baseTitle}"`,
+ });
+ const similarBaseTitlesCount: number = similarSavedObjects.total - 1;
+
+ return similarBaseTitlesCount <= 0
+ ? baseTitle + ` (${clonedTag})`
+ : baseTitle + ` (${clonedTag} ${similarBaseTitlesCount})`;
+ }
+
+ private async cloneEmbeddable(
+ panelToClone: DashboardPanelState,
+ embeddableType: string
+ ): Promise> {
+ const panelState: PanelState = {
+ type: embeddableType,
+ explicitInput: {
+ ...panelToClone.explicitInput,
+ id: uuid.v4(),
+ },
+ };
+ let newTitle: string = '';
+ if (panelToClone.explicitInput.savedObjectId) {
+ // Fetch existing saved object
+ const savedObjectToClone = await this.core.savedObjects.client.get(
+ embeddableType,
+ panelToClone.explicitInput.savedObjectId
+ );
+
+ // Clone the saved object
+ newTitle = await this.getUniqueTitle(savedObjectToClone.attributes.title, embeddableType);
+ const clonedSavedObject = await this.core.savedObjects.client.create(
+ embeddableType,
+ {
+ ..._.cloneDeep(savedObjectToClone.attributes),
+ title: newTitle,
+ },
+ { references: _.cloneDeep(savedObjectToClone.references) }
+ );
+ panelState.explicitInput.savedObjectId = clonedSavedObject.id;
+ }
+ this.core.notifications.toasts.addSuccess({
+ title: i18n.translate('dashboard.panel.clonedToast', {
+ defaultMessage: 'Cloned panel',
+ }),
+ 'data-test-subj': 'addObjectToContainerSuccess',
+ });
+ return panelState;
+ }
+}
diff --git a/src/plugins/dashboard/public/application/actions/index.ts b/src/plugins/dashboard/public/application/actions/index.ts
index 23c26dbd280f..d7a84fb79f6a 100644
--- a/src/plugins/dashboard/public/application/actions/index.ts
+++ b/src/plugins/dashboard/public/application/actions/index.ts
@@ -27,3 +27,8 @@ export {
ReplacePanelActionContext,
ACTION_REPLACE_PANEL,
} from './replace_panel_action';
+export {
+ ClonePanelAction,
+ ClonePanelActionContext,
+ ACTION_CLONE_PANEL,
+} from './clone_panel_action';
diff --git a/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx b/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx
index 3ef87fda7236..8bf205b8cb50 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 = (
({
+ this.stateSyncRef = syncState({
storageKey: this.STATE_STORAGE_KEY,
stateContainer: {
...this.stateContainer,
- set: (state: DashboardAppState | null) => {
+ get: () => this.toUrlState(this.stateContainer.get()),
+ set: (state: DashboardAppStateInUrl | null) => {
// sync state required state container to be able to handle null
// overriding set() so it could handle null coming from url
if (state) {
@@ -558,9 +560,9 @@ export class DashboardStateManager {
*/
private saveState({ replace }: { replace: boolean }): boolean {
// schedules setting current state to url
- this.kbnUrlStateStorage.set(
+ this.kbnUrlStateStorage.set(
this.STATE_STORAGE_KEY,
- this.stateContainer.get()
+ this.toUrlState(this.stateContainer.get())
);
// immediately forces scheduled updates and changes location
return this.kbnUrlStateStorage.flush({ replace });
@@ -620,4 +622,13 @@ export class DashboardStateManager {
const current = _.omit(this.stateContainer.get(), propsToIgnore);
return !_.isEqual(initial, current);
}
+
+ private toUrlState(state: DashboardAppState): DashboardAppStateInUrl {
+ if (state.viewMode === ViewMode.VIEW) {
+ const { panels, ...stateWithoutPanels } = state;
+ return stateWithoutPanels;
+ }
+
+ return state;
+ }
}
diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx
index 50089f1f061f..8346fd900cae 100644
--- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx
+++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx
@@ -23,6 +23,7 @@ import { I18nProvider } from '@kbn/i18n/react';
import { RefreshInterval, TimeRange, Query, Filter } from 'src/plugins/data/public';
import { CoreStart } from 'src/core/public';
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
+import uuid from 'uuid';
import { UiActionsStart } from '../../ui_actions_plugin';
import {
Container,
@@ -32,6 +33,7 @@ import {
EmbeddableFactory,
IEmbeddable,
EmbeddableStart,
+ PanelState,
} from '../../embeddable_plugin';
import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants';
import { createPanelState } from './panel';
@@ -42,6 +44,8 @@ import {
KibanaReactContext,
KibanaReactContextValue,
} from '../../../../kibana_react/public';
+import { PLACEHOLDER_EMBEDDABLE } from './placeholder';
+import { PanelPlacementMethod, IPanelPlacementArgs } from './panel/dashboard_panel_placement';
export interface DashboardContainerInput extends ContainerInput {
viewMode: ViewMode;
@@ -120,7 +124,61 @@ export class DashboardContainer extends Container = {}
): DashboardPanelState {
const panelState = super.createNewPanelState(factory, partial);
- return createPanelState(panelState, Object.values(this.input.panels));
+ return createPanelState(panelState, this.input.panels);
+ }
+
+ public showPlaceholderUntil(
+ newStateComplete: Promise>,
+ placementMethod?: PanelPlacementMethod,
+ placementArgs?: TPlacementMethodArgs
+ ): void {
+ const originalPanelState = {
+ type: PLACEHOLDER_EMBEDDABLE,
+ explicitInput: {
+ id: uuid.v4(),
+ disabledActions: [
+ 'ACTION_CUSTOMIZE_PANEL',
+ 'CUSTOM_TIME_RANGE',
+ 'clonePanel',
+ 'replacePanel',
+ 'togglePanel',
+ ],
+ },
+ } as PanelState;
+ const placeholderPanelState = createPanelState(
+ originalPanelState,
+ this.input.panels,
+ placementMethod,
+ placementArgs
+ );
+ this.updateInput({
+ panels: {
+ ...this.input.panels,
+ [placeholderPanelState.explicitInput.id]: placeholderPanelState,
+ },
+ });
+ newStateComplete.then((newPanelState: Partial) => {
+ const finalPanels = { ...this.input.panels };
+ delete finalPanels[placeholderPanelState.explicitInput.id];
+ const newPanelId = newPanelState.explicitInput?.id
+ ? newPanelState.explicitInput.id
+ : uuid.v4();
+ finalPanels[newPanelId] = {
+ ...placeholderPanelState,
+ ...newPanelState,
+ gridData: {
+ ...placeholderPanelState.gridData,
+ i: newPanelId,
+ },
+ explicitInput: {
+ ...newPanelState.explicitInput,
+ id: newPanelId,
+ },
+ };
+ this.updateInput({
+ panels: finalPanels,
+ });
+ });
}
public render(dom: HTMLElement) {
diff --git a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts
index 409cae8b49a5..7c11ac8a5031 100644
--- a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts
+++ b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts
@@ -26,7 +26,7 @@ import { CONTACT_CARD_EMBEDDABLE } from '../../../embeddable_plugin_test_samples
interface TestInput extends EmbeddableInput {
test: string;
}
-const panels: DashboardPanelState[] = [];
+const panels: { [key: string]: DashboardPanelState } = {};
test('createPanelState adds a new panel state in 0,0 position', () => {
const panelState = createPanelState(
@@ -34,7 +34,7 @@ test('createPanelState adds a new panel state in 0,0 position', () => {
type: CONTACT_CARD_EMBEDDABLE,
explicitInput: { test: 'hi', id: '123' },
},
- []
+ panels
);
expect(panelState.explicitInput.test).toBe('hi');
expect(panelState.type).toBe(CONTACT_CARD_EMBEDDABLE);
@@ -44,7 +44,7 @@ test('createPanelState adds a new panel state in 0,0 position', () => {
expect(panelState.gridData.h).toBe(DEFAULT_PANEL_HEIGHT);
expect(panelState.gridData.w).toBe(DEFAULT_PANEL_WIDTH);
- panels.push(panelState);
+ panels[panelState.explicitInput.id] = panelState;
});
test('createPanelState adds a second new panel state', () => {
@@ -58,7 +58,7 @@ test('createPanelState adds a second new panel state', () => {
expect(panelState.gridData.h).toBe(DEFAULT_PANEL_HEIGHT);
expect(panelState.gridData.w).toBe(DEFAULT_PANEL_WIDTH);
- panels.push(panelState);
+ panels[panelState.explicitInput.id] = panelState;
});
test('createPanelState adds a third new panel state', () => {
@@ -74,17 +74,17 @@ test('createPanelState adds a third new panel state', () => {
expect(panelState.gridData.h).toBe(DEFAULT_PANEL_HEIGHT);
expect(panelState.gridData.w).toBe(DEFAULT_PANEL_WIDTH);
- panels.push(panelState);
+ panels[panelState.explicitInput.id] = panelState;
});
test('createPanelState adds a new panel state in the top most position', () => {
- const panelsWithEmptySpace = panels.filter(panel => panel.gridData.x === 0);
+ delete panels['456'];
const panelState = createPanelState(
{
type: CONTACT_CARD_EMBEDDABLE,
explicitInput: { test: 'bye', id: '987' },
},
- panelsWithEmptySpace
+ panels
);
expect(panelState.gridData.x).toBe(DEFAULT_PANEL_WIDTH);
expect(panelState.gridData.y).toBe(0);
diff --git a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts
index f3a48368fe1b..79116a57869d 100644
--- a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts
+++ b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts
@@ -19,98 +19,45 @@
import _ from 'lodash';
import { PanelState, EmbeddableInput } from '../../../embeddable_plugin';
-import {
- DASHBOARD_GRID_COLUMN_COUNT,
- DEFAULT_PANEL_HEIGHT,
- DEFAULT_PANEL_WIDTH,
-} from '../dashboard_constants';
+import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../dashboard_constants';
import { DashboardPanelState } from '../types';
-
-// Look for the smallest y and x value where the default panel will fit.
-function findTopLeftMostOpenSpace(
- width: number,
- height: number,
- currentPanels: DashboardPanelState[]
-) {
- let maxY = -1;
-
- currentPanels.forEach(panel => {
- maxY = Math.max(panel.gridData.y + panel.gridData.h, maxY);
- });
-
- // Handle case of empty grid.
- if (maxY < 0) {
- return { x: 0, y: 0 };
- }
-
- const grid = new Array(maxY);
- for (let y = 0; y < maxY; y++) {
- grid[y] = new Array(DASHBOARD_GRID_COLUMN_COUNT).fill(0);
- }
-
- currentPanels.forEach(panel => {
- for (let x = panel.gridData.x; x < panel.gridData.x + panel.gridData.w; x++) {
- for (let y = panel.gridData.y; y < panel.gridData.y + panel.gridData.h; y++) {
- const row = grid[y];
- if (row === undefined) {
- throw new Error(
- `Attempted to access a row that doesn't exist at ${y} for panel ${JSON.stringify(
- panel
- )}`
- );
- }
- grid[y][x] = 1;
- }
- }
- });
-
- for (let y = 0; y < maxY; y++) {
- for (let x = 0; x < DASHBOARD_GRID_COLUMN_COUNT; x++) {
- if (grid[y][x] === 1) {
- // Space is filled
- continue;
- } else {
- for (let h = y; h < Math.min(y + height, maxY); h++) {
- for (let w = x; w < Math.min(x + width, DASHBOARD_GRID_COLUMN_COUNT); w++) {
- const spaceIsEmpty = grid[h][w] === 0;
- const fitsPanelWidth = w === x + width - 1;
- // If the panel is taller than any other panel in the current grid, it can still fit in the space, hence
- // we check the minimum of maxY and the panel height.
- const fitsPanelHeight = h === Math.min(y + height - 1, maxY - 1);
-
- if (spaceIsEmpty && fitsPanelWidth && fitsPanelHeight) {
- // Found space
- return { x, y };
- } else if (grid[h][w] === 1) {
- // x, y spot doesn't work, break.
- break;
- }
- }
- }
- }
- }
- }
- return { x: 0, y: maxY };
-}
+import {
+ IPanelPlacementArgs,
+ findTopLeftMostOpenSpace,
+ PanelPlacementMethod,
+} from './dashboard_panel_placement';
/**
* Creates and initializes a basic panel state.
*/
-export function createPanelState(
+export function createPanelState<
+ TEmbeddableInput extends EmbeddableInput,
+ TPlacementMethodArgs extends IPanelPlacementArgs = IPanelPlacementArgs
+>(
panelState: PanelState,
- currentPanels: DashboardPanelState[]
+ currentPanels: { [key: string]: DashboardPanelState },
+ placementMethod?: PanelPlacementMethod,
+ placementArgs?: TPlacementMethodArgs
): DashboardPanelState {
- const { x, y } = findTopLeftMostOpenSpace(
- DEFAULT_PANEL_WIDTH,
- DEFAULT_PANEL_HEIGHT,
- currentPanels
- );
+ const defaultPlacementArgs = {
+ width: DEFAULT_PANEL_WIDTH,
+ height: DEFAULT_PANEL_HEIGHT,
+ currentPanels,
+ };
+ const finalPlacementArgs = placementArgs
+ ? {
+ ...defaultPlacementArgs,
+ ...placementArgs,
+ }
+ : defaultPlacementArgs;
+
+ const gridDataLocation = placementMethod
+ ? placementMethod(finalPlacementArgs as TPlacementMethodArgs)
+ : findTopLeftMostOpenSpace(defaultPlacementArgs);
+
return {
gridData: {
- w: DEFAULT_PANEL_WIDTH,
- h: DEFAULT_PANEL_HEIGHT,
- x,
- y,
+ ...gridDataLocation,
i: panelState.explicitInput.id,
},
...panelState,
diff --git a/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts b/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts
new file mode 100644
index 000000000000..70a6c8341858
--- /dev/null
+++ b/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts
@@ -0,0 +1,167 @@
+/*
+ * 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 { PanelNotFoundError } from '../../../embeddable_plugin';
+import { DashboardPanelState, GridData, DASHBOARD_GRID_COLUMN_COUNT } from '..';
+
+export type PanelPlacementMethod = (
+ args: PlacementArgs
+) => Omit;
+
+export interface IPanelPlacementArgs {
+ width: number;
+ height: number;
+ currentPanels: { [key: string]: DashboardPanelState };
+}
+
+export interface IPanelPlacementBesideArgs extends IPanelPlacementArgs {
+ placeBesideId: string;
+}
+
+// Look for the smallest y and x value where the default panel will fit.
+export function findTopLeftMostOpenSpace({
+ width,
+ height,
+ currentPanels,
+}: IPanelPlacementArgs): Omit {
+ let maxY = -1;
+
+ const currentPanelsArray = Object.values(currentPanels);
+ currentPanelsArray.forEach(panel => {
+ maxY = Math.max(panel.gridData.y + panel.gridData.h, maxY);
+ });
+
+ // Handle case of empty grid.
+ if (maxY < 0) {
+ return { x: 0, y: 0, w: width, h: height };
+ }
+
+ const grid = new Array(maxY);
+ for (let y = 0; y < maxY; y++) {
+ grid[y] = new Array(DASHBOARD_GRID_COLUMN_COUNT).fill(0);
+ }
+
+ currentPanelsArray.forEach(panel => {
+ for (let x = panel.gridData.x; x < panel.gridData.x + panel.gridData.w; x++) {
+ for (let y = panel.gridData.y; y < panel.gridData.y + panel.gridData.h; y++) {
+ const row = grid[y];
+ if (row === undefined) {
+ throw new Error(
+ `Attempted to access a row that doesn't exist at ${y} for panel ${JSON.stringify(
+ panel
+ )}`
+ );
+ }
+ grid[y][x] = 1;
+ }
+ }
+ });
+
+ for (let y = 0; y < maxY; y++) {
+ for (let x = 0; x < DASHBOARD_GRID_COLUMN_COUNT; x++) {
+ if (grid[y][x] === 1) {
+ // Space is filled
+ continue;
+ } else {
+ for (let h = y; h < Math.min(y + height, maxY); h++) {
+ for (let w = x; w < Math.min(x + width, DASHBOARD_GRID_COLUMN_COUNT); w++) {
+ const spaceIsEmpty = grid[h][w] === 0;
+ const fitsPanelWidth = w === x + width - 1;
+ // If the panel is taller than any other panel in the current grid, it can still fit in the space, hence
+ // we check the minimum of maxY and the panel height.
+ const fitsPanelHeight = h === Math.min(y + height - 1, maxY - 1);
+
+ if (spaceIsEmpty && fitsPanelWidth && fitsPanelHeight) {
+ // Found space
+ return { x, y, w: width, h: height };
+ } else if (grid[h][w] === 1) {
+ // x, y spot doesn't work, break.
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return { x: 0, y: maxY, w: width, h: height };
+}
+
+interface IplacementDirection {
+ grid: Omit;
+ fits: boolean;
+}
+
+export function placePanelBeside({
+ width,
+ height,
+ currentPanels,
+ placeBesideId,
+}: IPanelPlacementBesideArgs): Omit {
+ // const clonedPanels = _.cloneDeep(currentPanels);
+ if (!placeBesideId) {
+ throw new Error('Place beside method called without placeBesideId');
+ }
+ const panelToPlaceBeside = currentPanels[placeBesideId];
+ if (!panelToPlaceBeside) {
+ throw new PanelNotFoundError();
+ }
+ const beside = panelToPlaceBeside.gridData;
+ const otherPanels: GridData[] = [];
+ _.forOwn(currentPanels, (panel: DashboardPanelState, key: string | undefined) => {
+ otherPanels.push(panel.gridData);
+ });
+
+ const possiblePlacementDirections: IplacementDirection[] = [
+ { grid: { x: beside.x + beside.w, y: beside.y, w: width, h: height }, fits: true }, // right
+ { grid: { x: beside.x - width, y: beside.y, w: width, h: height }, fits: true }, // left
+ { grid: { x: beside.x, y: beside.y + beside.h, w: width, h: height }, fits: true }, // bottom
+ ];
+
+ for (const direction of possiblePlacementDirections) {
+ if (
+ direction.grid.x >= 0 &&
+ direction.grid.x + direction.grid.w <= DASHBOARD_GRID_COLUMN_COUNT &&
+ direction.grid.y >= 0
+ ) {
+ const intersection = otherPanels.some((currentPanelGrid: GridData) => {
+ return (
+ direction.grid.x + direction.grid.w > currentPanelGrid.x &&
+ direction.grid.x < currentPanelGrid.x + currentPanelGrid.w &&
+ direction.grid.y < currentPanelGrid.y + currentPanelGrid.h &&
+ direction.grid.y + direction.grid.h > currentPanelGrid.y
+ );
+ });
+ if (!intersection) {
+ return direction.grid;
+ }
+ } else {
+ direction.fits = false;
+ }
+ }
+ // if we get here that means there is no blank space around the panel we are placing beside. This means it's time to mess up the dashboard's groove. Fun!
+ const [, , bottomPlacement] = possiblePlacementDirections;
+ for (const currentPanelGrid of otherPanels) {
+ if (bottomPlacement.grid.y <= currentPanelGrid.y) {
+ const movedPanel = _.cloneDeep(currentPanels[currentPanelGrid.i]);
+ movedPanel.gridData.y = movedPanel.gridData.y + bottomPlacement.grid.h;
+ currentPanels[currentPanelGrid.i] = movedPanel;
+ }
+ }
+ return bottomPlacement.grid;
+}
diff --git a/src/plugins/dashboard/public/application/embeddable/placeholder/index.ts b/src/plugins/dashboard/public/application/embeddable/placeholder/index.ts
new file mode 100644
index 000000000000..7c65d50ce3fe
--- /dev/null
+++ b/src/plugins/dashboard/public/application/embeddable/placeholder/index.ts
@@ -0,0 +1,21 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export * from './placeholder_embeddable';
+export * from './placeholder_embeddable_factory';
diff --git a/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable.tsx b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable.tsx
new file mode 100644
index 000000000000..1a5c3386bded
--- /dev/null
+++ b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable.tsx
@@ -0,0 +1,52 @@
+/*
+ * 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 React from 'react';
+import ReactDOM from 'react-dom';
+import { EuiLoadingChart } from '@elastic/eui';
+import classNames from 'classnames';
+import { Embeddable, EmbeddableInput, IContainer } from '../../../embeddable_plugin';
+
+export const PLACEHOLDER_EMBEDDABLE = 'placeholder';
+
+export class PlaceholderEmbeddable extends Embeddable {
+ public readonly type = PLACEHOLDER_EMBEDDABLE;
+ private node?: HTMLElement;
+
+ constructor(initialInput: EmbeddableInput, parent?: IContainer) {
+ super(initialInput, {}, parent);
+ this.input = initialInput;
+ }
+ public render(node: HTMLElement) {
+ if (this.node) {
+ ReactDOM.unmountComponentAtNode(this.node);
+ }
+ this.node = node;
+
+ const classes = classNames('embPanel', 'embPanel-isLoading');
+ ReactDOM.render(
+
+
+
,
+ node
+ );
+ }
+
+ public reload() {}
+}
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/legacy.ts b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts
similarity index 53%
rename from src/legacy/core_plugins/vis_type_timelion/public/legacy.ts
rename to src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts
index f8de9f94dced..30a93989649a 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/legacy.ts
+++ b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts
@@ -17,19 +17,29 @@
* under the License.
*/
-import { PluginInitializerContext } from 'kibana/public';
+import { i18n } from '@kbn/i18n';
-import { npSetup, npStart } from './legacy_imports';
-import { TimelionVisSetupDependencies } from './plugin';
-import { plugin } from '.';
+import {
+ EmbeddableFactoryDefinition,
+ EmbeddableInput,
+ IContainer,
+} from '../../../embeddable_plugin';
+import { PlaceholderEmbeddable, PLACEHOLDER_EMBEDDABLE } from './placeholder_embeddable';
-const setupPlugins: Readonly = {
- expressions: npSetup.plugins.expressions,
- data: npSetup.plugins.data,
- visualizations: npSetup.plugins.visualizations,
-};
+export class PlaceholderEmbeddableFactory implements EmbeddableFactoryDefinition {
+ public readonly type = PLACEHOLDER_EMBEDDABLE;
-const pluginInstance = plugin({} as PluginInitializerContext);
+ public async isEditable() {
+ return false;
+ }
-export const setup = pluginInstance.setup(npSetup.core, setupPlugins);
-export const start = pluginInstance.start(npStart.core, npStart.plugins);
+ public async create(initialInput: EmbeddableInput, parent?: IContainer) {
+ return new PlaceholderEmbeddable(initialInput, parent);
+ }
+
+ public getDisplayName() {
+ return i18n.translate('dashboard.placeholder.factory.displayName', {
+ defaultMessage: 'placeholder',
+ });
+ }
+}
diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx
index 322d734d9f39..203c784d9df4 100644
--- a/src/plugins/dashboard/public/plugin.tsx
+++ b/src/plugins/dashboard/public/plugin.tsx
@@ -67,9 +67,12 @@ import {
ExpandPanelActionContext,
ReplacePanelAction,
ReplacePanelActionContext,
+ ClonePanelAction,
+ ClonePanelActionContext,
ACTION_EXPAND_PANEL,
ACTION_REPLACE_PANEL,
RenderDeps,
+ ACTION_CLONE_PANEL,
} from './application';
import {
DashboardAppLinkGeneratorState,
@@ -78,6 +81,7 @@ import {
} from './url_generator';
import { createSavedDashboardLoader } from './saved_dashboards';
import { DashboardConstants } from './dashboard_constants';
+import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder';
declare module '../../share/public' {
export interface UrlGeneratorStateMapping {
@@ -115,6 +119,7 @@ declare module '../../../plugins/ui_actions/public' {
export interface ActionContextMapping {
[ACTION_EXPAND_PANEL]: ExpandPanelActionContext;
[ACTION_REPLACE_PANEL]: ReplacePanelActionContext;
+ [ACTION_CLONE_PANEL]: ClonePanelActionContext;
}
}
@@ -173,6 +178,9 @@ export class DashboardPlugin
const factory = new DashboardContainerFactory(getStartServices);
embeddable.registerEmbeddableFactory(factory.type, factory);
+ const placeholderFactory = new PlaceholderEmbeddableFactory();
+ embeddable.registerEmbeddableFactory(placeholderFactory.type, placeholderFactory);
+
const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({
baseUrl: core.http.basePath.prepend('/app/kibana'),
defaultSubUrl: `#${DashboardConstants.LANDING_PAGE_PATH}`,
@@ -297,6 +305,11 @@ export class DashboardPlugin
);
uiActions.registerAction(changeViewAction);
uiActions.attachAction(CONTEXT_MENU_TRIGGER, changeViewAction);
+
+ const clonePanelAction = new ClonePanelAction(core);
+ uiActions.registerAction(clonePanelAction);
+ uiActions.attachAction(CONTEXT_MENU_TRIGGER, clonePanelAction);
+
const savedDashboardLoader = createSavedDashboardLoader({
savedObjectsClient: core.savedObjects.client,
indexPatterns,
diff --git a/src/plugins/dashboard/public/types.ts b/src/plugins/dashboard/public/types.ts
index c3008913dc31..21c6bbc1bfc5 100644
--- a/src/plugins/dashboard/public/types.ts
+++ b/src/plugins/dashboard/public/types.ts
@@ -96,6 +96,14 @@ export type DashboardAppStateDefaults = DashboardAppState & {
description?: string;
};
+/**
+ * In URL panels are optional,
+ * Panels are not added to the URL when in "view" mode
+ */
+export type DashboardAppStateInUrl = Omit & {
+ panels?: SavedDashboardPanel[];
+};
+
export interface DashboardAppStateTransitions {
set: (
state: DashboardAppState
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index 06a46065baa8..b62b728beca3 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -363,7 +363,6 @@ export {
SearchRequest,
SearchResponse,
SearchError,
- SearchStrategyProvider,
ISearchSource,
SearchSource,
createSearchSource,
diff --git a/src/plugins/data/public/index_patterns/fields/field.ts b/src/plugins/data/public/index_patterns/fields/field.ts
index 6370dcdf2db6..0fb92393d56f 100644
--- a/src/plugins/data/public/index_patterns/fields/field.ts
+++ b/src/plugins/data/public/index_patterns/fields/field.ts
@@ -44,6 +44,7 @@ export class Field implements IFieldType {
scripted?: boolean;
subType?: IFieldSubType;
displayName?: string;
+ indexPattern?: IndexPattern;
format: any;
$$spec: FieldSpec;
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index 62967a7071d8..427c4f786455 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -943,6 +943,8 @@ export class IndexPatternField implements IFieldType {
// (undocumented)
format: any;
// (undocumented)
+ indexPattern?: IndexPattern;
+ // (undocumented)
lang?: string;
// (undocumented)
name: string;
@@ -1722,21 +1724,6 @@ export interface SearchSourceFields {
version?: boolean;
}
-// Warning: (ae-missing-release-tag) "SearchStrategyProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export interface SearchStrategyProvider {
- // (undocumented)
- id: string;
- // (undocumented)
- isViable: (indexPattern: IndexPattern) => boolean;
- // Warning: (ae-forgotten-export) The symbol "SearchStrategySearchParams" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "SearchStrategyResponse" needs to be exported by the entry point index.d.ts
- //
- // (undocumented)
- search: (params: SearchStrategySearchParams) => SearchStrategyResponse;
-}
-
// Warning: (ae-missing-release-tag) "SortDirection" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -1887,21 +1874,21 @@ export type TSearchStrategyProvider = (context: ISearc
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getRoutes" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:405:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:413:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:404:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:405:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:408:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:33:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromEvent" needs to be exported by the entry point index.d.ts
diff --git a/src/plugins/data/public/search/aggs/test_helpers/mock_data_services.ts b/src/plugins/data/public/search/aggs/test_helpers/mock_data_services.ts
index d1d591771743..e807e084c683 100644
--- a/src/plugins/data/public/search/aggs/test_helpers/mock_data_services.ts
+++ b/src/plugins/data/public/search/aggs/test_helpers/mock_data_services.ts
@@ -18,8 +18,8 @@
*/
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { coreMock } from '../../../../../../../src/core/public/mocks';
-import { dataPluginMock } from '../../../../public/mocks';
+import { coreMock } from '../../../../../../core/public/mocks';
+import { dataPluginMock } from '../../../mocks';
import {
setFieldFormats,
setIndexPatterns,
@@ -29,7 +29,7 @@ import {
setQueryService,
setSearchService,
setUiSettings,
-} from '../../../../public/services';
+} from '../../../services';
/**
* Testing helper which calls all of the service setters used in the
@@ -49,4 +49,9 @@ export function mockDataServices() {
setQueryService(data.query);
setSearchService(data.search);
setUiSettings(core.uiSettings);
+
+ return {
+ core,
+ data,
+ };
}
diff --git a/src/plugins/data/public/search/fetch/get_search_params.test.ts b/src/plugins/data/public/search/fetch/get_search_params.test.ts
new file mode 100644
index 000000000000..edf18405e8ff
--- /dev/null
+++ b/src/plugins/data/public/search/fetch/get_search_params.test.ts
@@ -0,0 +1,70 @@
+/*
+ * 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 { getSearchParams } from './get_search_params';
+import { IUiSettingsClient } from 'kibana/public';
+
+function getConfigStub(config: any = {}) {
+ return {
+ get: key => config[key],
+ } as IUiSettingsClient;
+}
+
+describe('getSearchParams', () => {
+ test('includes rest_total_hits_as_int', () => {
+ const config = getConfigStub();
+ const searchParams = getSearchParams(config);
+ expect(searchParams.rest_total_hits_as_int).toBe(true);
+ });
+
+ test('includes ignore_unavailable', () => {
+ const config = getConfigStub();
+ const searchParams = getSearchParams(config);
+ expect(searchParams.ignore_unavailable).toBe(true);
+ });
+
+ test('includes ignore_throttled according to search:includeFrozen', () => {
+ let config = getConfigStub({ 'search:includeFrozen': true });
+ let searchParams = getSearchParams(config);
+ expect(searchParams.ignore_throttled).toBe(false);
+
+ config = getConfigStub({ 'search:includeFrozen': false });
+ searchParams = getSearchParams(config);
+ expect(searchParams.ignore_throttled).toBe(true);
+ });
+
+ test('includes max_concurrent_shard_requests according to courier:maxConcurrentShardRequests', () => {
+ let config = getConfigStub({ 'courier:maxConcurrentShardRequests': 0 });
+ let searchParams = getSearchParams(config);
+ expect(searchParams.max_concurrent_shard_requests).toBe(undefined);
+
+ config = getConfigStub({ 'courier:maxConcurrentShardRequests': 5 });
+ searchParams = getSearchParams(config);
+ expect(searchParams.max_concurrent_shard_requests).toBe(5);
+ });
+
+ test('includes timeout according to esShardTimeout if greater than 0', () => {
+ const config = getConfigStub();
+ let searchParams = getSearchParams(config, 0);
+ expect(searchParams.timeout).toBe(undefined);
+
+ searchParams = getSearchParams(config, 100);
+ expect(searchParams.timeout).toBe('100ms');
+ });
+});
diff --git a/src/plugins/data/public/search/search_strategy/get_search_params.ts b/src/plugins/data/public/search/fetch/get_search_params.ts
similarity index 81%
rename from src/plugins/data/public/search/search_strategy/get_search_params.ts
rename to src/plugins/data/public/search/fetch/get_search_params.ts
index 9fb8f2c728c6..f0c43bd2e74c 100644
--- a/src/plugins/data/public/search/search_strategy/get_search_params.ts
+++ b/src/plugins/data/public/search/fetch/get_search_params.ts
@@ -17,18 +17,10 @@
* under the License.
*/
-import { IUiSettingsClient } from '../../../../../core/public';
+import { IUiSettingsClient } from 'kibana/public';
const sessionId = Date.now();
-export function getMSearchParams(config: IUiSettingsClient) {
- return {
- rest_total_hits_as_int: true,
- ignore_throttled: getIgnoreThrottled(config),
- max_concurrent_shard_requests: getMaxConcurrentShardRequests(config),
- };
-}
-
export function getSearchParams(config: IUiSettingsClient, esShardTimeout: number = 0) {
return {
rest_total_hits_as_int: true,
@@ -40,11 +32,11 @@ export function getSearchParams(config: IUiSettingsClient, esShardTimeout: numbe
};
}
-function getIgnoreThrottled(config: IUiSettingsClient) {
+export function getIgnoreThrottled(config: IUiSettingsClient) {
return !config.get('search:includeFrozen');
}
-function getMaxConcurrentShardRequests(config: IUiSettingsClient) {
+export function getMaxConcurrentShardRequests(config: IUiSettingsClient) {
const maxConcurrentShardRequests = config.get('courier:maxConcurrentShardRequests');
return maxConcurrentShardRequests > 0 ? maxConcurrentShardRequests : undefined;
}
diff --git a/src/plugins/data/public/search/fetch/index.ts b/src/plugins/data/public/search/fetch/index.ts
index 8a80b716add3..39845ec31bfa 100644
--- a/src/plugins/data/public/search/fetch/index.ts
+++ b/src/plugins/data/public/search/fetch/index.ts
@@ -18,5 +18,14 @@
*/
export * from './types';
-export { fetchSoon } from './fetch_soon';
-export { RequestFailure } from './errors';
+export {
+ getSearchParams,
+ getPreference,
+ getTimeout,
+ getIgnoreThrottled,
+ getMaxConcurrentShardRequests,
+} from './get_search_params';
+
+export { SearchError, getSearchErrorType } from './search_error';
+export { RequestFailure } from './request_error';
+export { handleResponse } from './handle_response';
diff --git a/src/plugins/data/public/search/fetch/errors.ts b/src/plugins/data/public/search/fetch/request_error.ts
similarity index 100%
rename from src/plugins/data/public/search/fetch/errors.ts
rename to src/plugins/data/public/search/fetch/request_error.ts
diff --git a/src/plugins/data/public/search/search_strategy/search_error.ts b/src/plugins/data/public/search/fetch/search_error.ts
similarity index 100%
rename from src/plugins/data/public/search/search_strategy/search_error.ts
rename to src/plugins/data/public/search/fetch/search_error.ts
diff --git a/src/plugins/data/public/search/fetch/types.ts b/src/plugins/data/public/search/fetch/types.ts
index e8de0576b8a7..475b2abbc019 100644
--- a/src/plugins/data/public/search/fetch/types.ts
+++ b/src/plugins/data/public/search/fetch/types.ts
@@ -20,6 +20,9 @@
import { IUiSettingsClient } from '../../../../../core/public';
import { ISearchStart } from '../types';
+export type SearchRequest = any;
+export type SearchResponse = any;
+
export interface FetchOptions {
abortSignal?: AbortSignal;
searchStrategyId?: string;
diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts
index cce973d632f4..75c0eb8b6f02 100644
--- a/src/plugins/data/public/search/index.ts
+++ b/src/plugins/data/public/search/index.ts
@@ -44,9 +44,13 @@ export { esSearchStrategyProvider, getEsPreference } from './es_search';
export { IKibanaSearchResponse, IKibanaSearchRequest } from '../../common/search';
-export { LegacyApiCaller, SearchRequest, SearchResponse } from './es_client';
-
-export { SearchError, SearchStrategyProvider, getSearchErrorType } from './search_strategy';
+export {
+ SearchError,
+ FetchOptions,
+ SearchRequest,
+ SearchResponse,
+ getSearchErrorType,
+} from './fetch';
export {
ISearchSource,
@@ -59,5 +63,3 @@ export {
export { SearchInterceptor } from './search_interceptor';
export { RequestTimeoutError } from './request_timeout_error';
-
-export { FetchOptions } from './fetch';
diff --git a/src/plugins/data/public/search/fetch/call_client.test.ts b/src/plugins/data/public/search/legacy/call_client.test.ts
similarity index 91%
rename from src/plugins/data/public/search/fetch/call_client.test.ts
rename to src/plugins/data/public/search/legacy/call_client.test.ts
index 7a99b7c06451..f919187d4678 100644
--- a/src/plugins/data/public/search/fetch/call_client.test.ts
+++ b/src/plugins/data/public/search/legacy/call_client.test.ts
@@ -18,16 +18,17 @@
*/
import { callClient } from './call_client';
-import { handleResponse } from './handle_response';
-import { FetchHandlers } from './types';
-import { SearchStrategySearchParams, defaultSearchStrategy } from '../search_strategy';
+import { SearchStrategySearchParams } from './types';
+import { defaultSearchStrategy } from './default_search_strategy';
+import { FetchHandlers } from '../fetch';
+import { handleResponse } from '../fetch/handle_response';
const mockAbortFn = jest.fn();
-jest.mock('./handle_response', () => ({
+jest.mock('../fetch/handle_response', () => ({
handleResponse: jest.fn((request, response) => response),
}));
-jest.mock('../search_strategy', () => {
+jest.mock('./default_search_strategy', () => {
return {
defaultSearchStrategy: {
search: jest.fn(({ searchRequests }: SearchStrategySearchParams) => {
diff --git a/src/plugins/data/public/search/fetch/call_client.ts b/src/plugins/data/public/search/legacy/call_client.ts
similarity index 89%
rename from src/plugins/data/public/search/fetch/call_client.ts
rename to src/plugins/data/public/search/legacy/call_client.ts
index b3c4c682fa60..c484c46aa487 100644
--- a/src/plugins/data/public/search/fetch/call_client.ts
+++ b/src/plugins/data/public/search/legacy/call_client.ts
@@ -17,10 +17,9 @@
* under the License.
*/
-import { handleResponse } from './handle_response';
-import { FetchOptions, FetchHandlers } from './types';
-import { defaultSearchStrategy } from '../search_strategy';
-import { SearchRequest } from '..';
+import { FetchOptions, FetchHandlers, handleResponse } from '../fetch';
+import { defaultSearchStrategy } from './default_search_strategy';
+import { SearchRequest } from '../index';
export function callClient(
searchRequests: SearchRequest[],
diff --git a/src/plugins/data/public/search/search_strategy/default_search_strategy.test.ts b/src/plugins/data/public/search/legacy/default_search_strategy.test.ts
similarity index 83%
rename from src/plugins/data/public/search/search_strategy/default_search_strategy.test.ts
rename to src/plugins/data/public/search/legacy/default_search_strategy.test.ts
index 210a0e5fd1ac..835b02b3cd5c 100644
--- a/src/plugins/data/public/search/search_strategy/default_search_strategy.test.ts
+++ b/src/plugins/data/public/search/legacy/default_search_strategy.test.ts
@@ -17,10 +17,10 @@
* under the License.
*/
-import { IUiSettingsClient } from '../../../../../core/public';
-import { SearchStrategySearchParams } from './types';
+import { IUiSettingsClient } from 'kibana/public';
import { defaultSearchStrategy } from './default_search_strategy';
import { searchStartMock } from '../mocks';
+import { SearchStrategySearchParams } from './types';
const { search } = defaultSearchStrategy;
@@ -38,12 +38,6 @@ const searchMockResponse: any = Promise.resolve([]);
searchMockResponse.abort = jest.fn();
const searchMock = jest.fn().mockReturnValue(searchMockResponse);
-const newSearchMockResponse: any = Promise.resolve([]);
-newSearchMockResponse.abort = jest.fn();
-const newSearchMock = jest.fn().mockReturnValue({
- toPromise: () => searchMockResponse,
-});
-
describe('defaultSearchStrategy', function() {
describe('search', function() {
let searchArgs: MockedKeys>;
@@ -58,7 +52,6 @@ describe('defaultSearchStrategy', function() {
const searchService = searchStartMock;
searchService.aggs.calculateAutoTimeExpression = jest.fn().mockReturnValue('1d');
- searchService.search = newSearchMock;
searchService.__LEGACY.esClient.search = searchMock;
searchService.__LEGACY.esClient.msearch = msearchMock;
@@ -112,18 +105,5 @@ describe('defaultSearchStrategy', function() {
search({ ...searchArgs, config }).abort();
expect(msearchMockResponse.abort).toHaveBeenCalled();
});
-
- test('should call new search service', () => {
- const config = getConfigStub();
- search({ ...searchArgs, config });
- expect(newSearchMock).toHaveBeenCalledTimes(1);
- });
-
- test('should properly abort with new search service', async () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const config = getConfigStub({});
- search({ ...searchArgs, config }).abort();
- expect(abortSpy).toHaveBeenCalled();
- });
});
});
diff --git a/src/plugins/data/public/search/search_strategy/default_search_strategy.ts b/src/plugins/data/public/search/legacy/default_search_strategy.ts
similarity index 64%
rename from src/plugins/data/public/search/search_strategy/default_search_strategy.ts
rename to src/plugins/data/public/search/legacy/default_search_strategy.ts
index 2bd88f51587a..1552410f9090 100644
--- a/src/plugins/data/public/search/search_strategy/default_search_strategy.ts
+++ b/src/plugins/data/public/search/legacy/default_search_strategy.ts
@@ -17,23 +17,19 @@
* under the License.
*/
+import { getPreference, getTimeout } from '../fetch';
+import { getMSearchParams } from './get_msearch_params';
import { SearchStrategyProvider, SearchStrategySearchParams } from './types';
-import { isDefault } from '../../index_patterns';
-import { getSearchParams, getMSearchParams, getPreference, getTimeout } from './get_search_params';
+// @deprecated
export const defaultSearchStrategy: SearchStrategyProvider = {
id: 'default',
search: params => {
- return params.config.get('courier:batchSearches') ? msearch(params) : search(params);
- },
-
- isViable: indexPattern => {
- return indexPattern && isDefault(indexPattern);
+ return msearch(params);
},
};
-// @deprecated
function msearch({
searchRequests,
searchService,
@@ -65,29 +61,3 @@ function msearch({
abort: searching.abort,
};
}
-
-function search({
- searchRequests,
- searchService,
- config,
- esShardTimeout,
-}: SearchStrategySearchParams) {
- const abortController = new AbortController();
- const searchParams = getSearchParams(config, esShardTimeout);
- const promises = searchRequests.map(({ index, indexType, body }) => {
- const params = {
- index: index.title || index,
- body,
- ...searchParams,
- };
- const { signal } = abortController;
- return searchService
- .search({ params, indexType }, { signal })
- .toPromise()
- .then(({ rawResponse }) => rawResponse);
- });
- return {
- searching: Promise.all(promises),
- abort: () => abortController.abort(),
- };
-}
diff --git a/src/plugins/data/public/search/es_client/get_es_client.ts b/src/plugins/data/public/search/legacy/es_client/get_es_client.ts
similarity index 100%
rename from src/plugins/data/public/search/es_client/get_es_client.ts
rename to src/plugins/data/public/search/legacy/es_client/get_es_client.ts
diff --git a/src/plugins/data/public/search/es_client/index.ts b/src/plugins/data/public/search/legacy/es_client/index.ts
similarity index 91%
rename from src/plugins/data/public/search/es_client/index.ts
rename to src/plugins/data/public/search/legacy/es_client/index.ts
index b1e0ce311682..78ac83af642d 100644
--- a/src/plugins/data/public/search/es_client/index.ts
+++ b/src/plugins/data/public/search/legacy/es_client/index.ts
@@ -18,4 +18,4 @@
*/
export { getEsClient } from './get_es_client';
-export { SearchRequest, SearchResponse, LegacyApiCaller } from './types';
+export { LegacyApiCaller } from './types';
diff --git a/src/plugins/data/public/search/es_client/types.ts b/src/plugins/data/public/search/legacy/es_client/types.ts
similarity index 94%
rename from src/plugins/data/public/search/es_client/types.ts
rename to src/plugins/data/public/search/legacy/es_client/types.ts
index 3ca0513a1423..7a56b9b0cb00 100644
--- a/src/plugins/data/public/search/es_client/types.ts
+++ b/src/plugins/data/public/search/legacy/es_client/types.ts
@@ -17,8 +17,7 @@
* under the License.
*/
-export type SearchRequest = any;
-export type SearchResponse = any;
+import { SearchRequest, SearchResponse } from '../../fetch';
export interface LegacyApiCaller {
search: (searchRequest: SearchRequest) => LegacyApiCallerResponse;
diff --git a/src/plugins/data/public/search/fetch/fetch_soon.test.ts b/src/plugins/data/public/search/legacy/fetch_soon.test.ts
similarity index 96%
rename from src/plugins/data/public/search/fetch/fetch_soon.test.ts
rename to src/plugins/data/public/search/legacy/fetch_soon.test.ts
index a8d593c8501f..b2e17798ccc9 100644
--- a/src/plugins/data/public/search/fetch/fetch_soon.test.ts
+++ b/src/plugins/data/public/search/legacy/fetch_soon.test.ts
@@ -19,9 +19,9 @@
import { fetchSoon } from './fetch_soon';
import { callClient } from './call_client';
-import { IUiSettingsClient } from '../../../../../core/public';
-import { FetchHandlers, FetchOptions } from './types';
-import { SearchRequest, SearchResponse } from '..';
+import { IUiSettingsClient } from 'kibana/public';
+import { FetchHandlers, FetchOptions } from '../fetch/types';
+import { SearchRequest, SearchResponse } from '../index';
function getConfigStub(config: any = {}) {
return {
diff --git a/src/plugins/data/public/search/fetch/fetch_soon.ts b/src/plugins/data/public/search/legacy/fetch_soon.ts
similarity index 95%
rename from src/plugins/data/public/search/fetch/fetch_soon.ts
rename to src/plugins/data/public/search/legacy/fetch_soon.ts
index b1405747426e..18fa410a5bef 100644
--- a/src/plugins/data/public/search/fetch/fetch_soon.ts
+++ b/src/plugins/data/public/search/legacy/fetch_soon.ts
@@ -18,8 +18,8 @@
*/
import { callClient } from './call_client';
-import { FetchHandlers, FetchOptions } from './types';
-import { SearchRequest, SearchResponse } from '..';
+import { FetchHandlers, FetchOptions } from '../fetch/types';
+import { SearchRequest, SearchResponse } from '../index';
/**
* This function introduces a slight delay in the request process to allow multiple requests to queue
diff --git a/src/plugins/data/public/search/search_strategy/get_search_params.test.ts b/src/plugins/data/public/search/legacy/get_msearch_params.test.ts
similarity index 60%
rename from src/plugins/data/public/search/search_strategy/get_search_params.test.ts
rename to src/plugins/data/public/search/legacy/get_msearch_params.test.ts
index 76f3105d7f94..9f16d5b40817 100644
--- a/src/plugins/data/public/search/search_strategy/get_search_params.test.ts
+++ b/src/plugins/data/public/search/legacy/get_msearch_params.test.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { getMSearchParams, getSearchParams } from './get_search_params';
+import { getMSearchParams } from './get_msearch_params';
import { IUiSettingsClient } from '../../../../../core/public';
function getConfigStub(config: any = {}) {
@@ -64,46 +64,3 @@ describe('getMSearchParams', () => {
expect(msearchParams.hasOwnProperty('timeout')).toBe(false);
});
});
-
-describe('getSearchParams', () => {
- test('includes rest_total_hits_as_int', () => {
- const config = getConfigStub();
- const searchParams = getSearchParams(config);
- expect(searchParams.rest_total_hits_as_int).toBe(true);
- });
-
- test('includes ignore_unavailable', () => {
- const config = getConfigStub();
- const searchParams = getSearchParams(config);
- expect(searchParams.ignore_unavailable).toBe(true);
- });
-
- test('includes ignore_throttled according to search:includeFrozen', () => {
- let config = getConfigStub({ 'search:includeFrozen': true });
- let searchParams = getSearchParams(config);
- expect(searchParams.ignore_throttled).toBe(false);
-
- config = getConfigStub({ 'search:includeFrozen': false });
- searchParams = getSearchParams(config);
- expect(searchParams.ignore_throttled).toBe(true);
- });
-
- test('includes max_concurrent_shard_requests according to courier:maxConcurrentShardRequests', () => {
- let config = getConfigStub({ 'courier:maxConcurrentShardRequests': 0 });
- let searchParams = getSearchParams(config);
- expect(searchParams.max_concurrent_shard_requests).toBe(undefined);
-
- config = getConfigStub({ 'courier:maxConcurrentShardRequests': 5 });
- searchParams = getSearchParams(config);
- expect(searchParams.max_concurrent_shard_requests).toBe(5);
- });
-
- test('includes timeout according to esShardTimeout if greater than 0', () => {
- const config = getConfigStub();
- let searchParams = getSearchParams(config, 0);
- expect(searchParams.timeout).toBe(undefined);
-
- searchParams = getSearchParams(config, 100);
- expect(searchParams.timeout).toBe('100ms');
- });
-});
diff --git a/src/plugins/data/public/search/legacy/get_msearch_params.ts b/src/plugins/data/public/search/legacy/get_msearch_params.ts
new file mode 100644
index 000000000000..48d13903c972
--- /dev/null
+++ b/src/plugins/data/public/search/legacy/get_msearch_params.ts
@@ -0,0 +1,29 @@
+/*
+ * 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 { IUiSettingsClient } from 'kibana/public';
+import { getIgnoreThrottled, getMaxConcurrentShardRequests } from '../fetch';
+
+export function getMSearchParams(config: IUiSettingsClient) {
+ return {
+ rest_total_hits_as_int: true,
+ ignore_throttled: getIgnoreThrottled(config),
+ max_concurrent_shard_requests: getMaxConcurrentShardRequests(config),
+ };
+}
diff --git a/src/plugins/data/public/search/legacy/index.ts b/src/plugins/data/public/search/legacy/index.ts
new file mode 100644
index 000000000000..e2ae72824f3f
--- /dev/null
+++ b/src/plugins/data/public/search/legacy/index.ts
@@ -0,0 +1,21 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export { fetchSoon } from './fetch_soon';
+export { getEsClient, LegacyApiCaller } from './es_client';
diff --git a/src/plugins/data/public/search/search_strategy/types.ts b/src/plugins/data/public/search/legacy/types.ts
similarity index 89%
rename from src/plugins/data/public/search/search_strategy/types.ts
rename to src/plugins/data/public/search/legacy/types.ts
index 764370d8ff64..3812cec7a2aa 100644
--- a/src/plugins/data/public/search/search_strategy/types.ts
+++ b/src/plugins/data/public/search/legacy/types.ts
@@ -17,21 +17,20 @@
* under the License.
*/
-import { IndexPattern } from '../..';
-import { FetchHandlers } from '../fetch/types';
+import { FetchHandlers } from '../fetch';
import { SearchRequest, SearchResponse } from '..';
+export interface SearchStrategySearchParams extends FetchHandlers {
+ searchRequests: SearchRequest[];
+}
+
+// @deprecated
export interface SearchStrategyProvider {
id: string;
search: (params: SearchStrategySearchParams) => SearchStrategyResponse;
- isViable: (indexPattern: IndexPattern) => boolean;
}
export interface SearchStrategyResponse {
searching: Promise;
abort: () => void;
}
-
-export interface SearchStrategySearchParams extends FetchHandlers {
- searchRequests: SearchRequest[];
-}
diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts
index a539736991ad..916278a96659 100644
--- a/src/plugins/data/public/search/search_service.ts
+++ b/src/plugins/data/public/search/search_service.ts
@@ -22,12 +22,12 @@ import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/publ
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
import { ISearchSetup, ISearchStart, TSearchStrategyProvider, TSearchStrategiesMap } from './types';
import { TStrategyTypes } from './strategy_types';
-import { getEsClient, LegacyApiCaller } from './es_client';
+import { getEsClient, LegacyApiCaller } from './legacy';
import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search';
-import { esSearchStrategyProvider } from './es_search/es_search_strategy';
+import { esSearchStrategyProvider } from './es_search';
import { IndexPatternsContract } from '../index_patterns/index_patterns';
import { createSearchSource } from './search_source';
-import { QuerySetup } from '../query/query_service';
+import { QuerySetup } from '../query';
import { GetInternalStartServicesFn } from '../types';
import { SearchInterceptor } from './search_interceptor';
import {
diff --git a/src/plugins/data/public/search/search_source/search_source.test.ts b/src/plugins/data/public/search/search_source/search_source.test.ts
index 6bad093d3140..6e878844664a 100644
--- a/src/plugins/data/public/search/search_source/search_source.test.ts
+++ b/src/plugins/data/public/search/search_source/search_source.test.ts
@@ -20,8 +20,26 @@
import { SearchSource } from './search_source';
import { IndexPattern, SortDirection } from '../..';
import { mockDataServices } from '../aggs/test_helpers';
+import { setSearchService } from '../../services';
+import { searchStartMock } from '../mocks';
+import { fetchSoon } from '../legacy';
+import { CoreStart } from 'kibana/public';
+import { Observable } from 'rxjs';
-jest.mock('../fetch', () => ({
+// Setup search service mock
+searchStartMock.search = jest.fn(() => {
+ return new Observable(subscriber => {
+ setTimeout(() => {
+ subscriber.next({
+ rawResponse: '',
+ });
+ subscriber.complete();
+ }, 100);
+ });
+}) as any;
+setSearchService(searchStartMock);
+
+jest.mock('../legacy', () => ({
fetchSoon: jest.fn().mockResolvedValue({}),
}));
@@ -44,8 +62,11 @@ const indexPattern2 = ({
} as unknown) as IndexPattern;
describe('SearchSource', function() {
+ let uiSettingsMock: jest.Mocked;
beforeEach(() => {
- mockDataServices();
+ const { core } = mockDataServices();
+ uiSettingsMock = core.uiSettings;
+ jest.clearAllMocks();
});
describe('#setField()', function() {
@@ -151,6 +172,36 @@ describe('SearchSource', function() {
});
});
+ describe('#legacy fetch()', () => {
+ beforeEach(() => {
+ uiSettingsMock.get.mockImplementation(() => {
+ return true; // batchSearches = true
+ });
+ });
+
+ afterEach(() => {
+ uiSettingsMock.get.mockImplementation(() => {
+ return false; // batchSearches = false
+ });
+ });
+
+ it('should call msearch', async () => {
+ const searchSource = new SearchSource({ index: indexPattern });
+ const options = {};
+ await searchSource.fetch(options);
+ expect(fetchSoon).toBeCalledTimes(1);
+ });
+ });
+
+ describe('#search service fetch()', () => {
+ it('should call msearch', async () => {
+ const searchSource = new SearchSource({ index: indexPattern });
+ const options = {};
+ await searchSource.fetch(options);
+ expect(searchStartMock.search).toBeCalledTimes(1);
+ });
+ });
+
describe('#serialize', function() {
it('should reference index patterns', () => {
const indexPattern123 = { id: '123' } as IndexPattern;
diff --git a/src/plugins/data/public/search/search_source/search_source.ts b/src/plugins/data/public/search/search_source/search_source.ts
index c70db7bb82ef..9d2bb889953c 100644
--- a/src/plugins/data/public/search/search_source/search_source.ts
+++ b/src/plugins/data/public/search/search_source/search_source.ts
@@ -70,17 +70,19 @@
*/
import _ from 'lodash';
+import { map } from 'rxjs/operators';
import { SavedObjectReference } from 'kibana/public';
import { normalizeSortRequest } from './normalize_sort_request';
import { filterDocvalueFields } from './filter_docvalue_fields';
import { fieldWildcardFilter } from '../../../../kibana_utils/public';
import { IIndexPattern, SearchRequest } from '../..';
import { SearchSourceOptions, SearchSourceFields } from './types';
-import { fetchSoon, FetchOptions, RequestFailure } from '../fetch';
+import { FetchOptions, RequestFailure, getSearchParams, handleResponse } from '../fetch';
import { getSearchService, getUiSettings, getInjectedMetadata } from '../../services';
import { getEsQueryConfig, buildEsQuery, Filter } from '../../../common';
import { getHighlightRequest } from '../../../common/field_formats';
+import { fetchSoon } from '../legacy';
export type ISearchSource = Pick;
@@ -185,18 +187,29 @@ export class SearchSource {
}
/**
- * Fetch this source and reject the returned Promise on error
- *
- * @async
+ * Run a search using the search service
+ * @return {Observable>}
*/
- async fetch(options: FetchOptions = {}) {
- await this.requestIsStarting(options);
-
- const searchRequest = await this.flatten();
- this.history = [searchRequest];
+ private fetch$(searchRequest: SearchRequest, signal?: AbortSignal) {
+ const esShardTimeout = getInjectedMetadata().getInjectedVar('esShardTimeout') as number;
+ const searchParams = getSearchParams(getUiSettings(), esShardTimeout);
+ const params = {
+ index: searchRequest.index.title || searchRequest.index,
+ body: searchRequest.body,
+ ...searchParams,
+ };
+ return getSearchService()
+ .search({ params, indexType: searchRequest.indexType }, { signal })
+ .pipe(map(({ rawResponse }) => handleResponse(searchRequest, rawResponse)));
+ }
+ /**
+ * Run a search using the search service
+ * @return {Promise>}
+ */
+ private async legacyFetch(searchRequest: SearchRequest, options: FetchOptions) {
const esShardTimeout = getInjectedMetadata().getInjectedVar('esShardTimeout') as number;
- const response = await fetchSoon(
+ return await fetchSoon(
searchRequest,
{
...(this.searchStrategyId && { searchStrategyId: this.searchStrategyId }),
@@ -208,6 +221,24 @@ export class SearchSource {
esShardTimeout,
}
);
+ }
+ /**
+ * Fetch this source and reject the returned Promise on error
+ *
+ * @async
+ */
+ async fetch(options: FetchOptions = {}) {
+ await this.requestIsStarting(options);
+
+ const searchRequest = await this.flatten();
+ this.history = [searchRequest];
+
+ let response;
+ if (getUiSettings().get('courier:batchSearches')) {
+ response = await this.legacyFetch(searchRequest, options);
+ } else {
+ response = this.fetch$(searchRequest, options.abortSignal).toPromise();
+ }
if (response.error) {
throw new RequestFailure(null, response);
@@ -246,7 +277,6 @@ export class SearchSource {
/**
* Called by requests of this search source when they are started
- * @param {Courier.Request} request
* @param options
* @return {Promise}
*/
@@ -430,7 +460,6 @@ export class SearchSource {
* and `kibanaSavedObjectMeta.searchSourceJSON.filter[].meta.index`.
*
* Using `createSearchSource`, the instance can be re-created.
- * @param searchSource The search source to serialize
* @public */
public serialize() {
const references: SavedObjectReference[] = [];
diff --git a/src/plugins/data/public/search/search_strategy/no_op_search_strategy.ts b/src/plugins/data/public/search/search_strategy/no_op_search_strategy.ts
deleted file mode 100644
index dc7331e614a0..000000000000
--- a/src/plugins/data/public/search/search_strategy/no_op_search_strategy.ts
+++ /dev/null
@@ -1,55 +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 { i18n } from '@kbn/i18n';
-import { SearchError } from './search_error';
-import { SearchStrategyProvider } from './types';
-
-export const noOpSearchStrategy: SearchStrategyProvider = {
- id: 'noOp',
-
- search: () => {
- const searchError = new SearchError({
- status: '418', // "I'm a teapot" error
- title: i18n.translate(
- 'data.search.searchSource.noSearchStrategyRegisteredErrorMessageTitle',
- {
- defaultMessage: 'No search strategy registered',
- }
- ),
- message: i18n.translate(
- 'data.search.searchSource.noSearchStrategyRegisteredErrorMessageDescription',
- {
- defaultMessage: `Couldn't find a search strategy for the search request`,
- }
- ),
- type: 'NO_OP_SEARCH_STRATEGY',
- path: '',
- });
-
- return {
- searching: Promise.reject(searchError),
- abort: () => {},
- };
- },
-
- isViable: () => {
- return true;
- },
-};
diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts
index ba6e44f47b75..2122e4e82ec1 100644
--- a/src/plugins/data/public/search/types.ts
+++ b/src/plugins/data/public/search/types.ts
@@ -22,7 +22,7 @@ import { createSearchSource } from './search_source';
import { SearchAggsSetup, SearchAggsStart, SearchAggsStartLegacy } from './aggs';
import { ISearch, ISearchGeneric } from './i_search';
import { TStrategyTypes } from './strategy_types';
-import { LegacyApiCaller } from './es_client';
+import { LegacyApiCaller } from './legacy/es_client';
import { SearchInterceptor } from './search_interceptor';
export interface ISearchContext {
@@ -38,15 +38,6 @@ export interface ISearchStrategy {
search: ISearch;
}
-/**
- * Search strategy provider creates an instance of a search strategy with the request
- * handler context bound to it. This way every search strategy can use
- * whatever information they require from the request context.
- */
-export type TSearchStrategyProviderEnhanced = (
- search: ISearchGeneric
-) => Promise>;
-
export type TSearchStrategiesMap = {
[K in TStrategyTypes]?: TSearchStrategyProvider;
};
diff --git a/src/plugins/data/server/saved_objects/index_patterns.ts b/src/plugins/data/server/saved_objects/index_patterns.ts
index 9838071eee5a..497dbb7d6f63 100644
--- a/src/plugins/data/server/saved_objects/index_patterns.ts
+++ b/src/plugins/data/server/saved_objects/index_patterns.ts
@@ -23,7 +23,7 @@ import { indexPatternSavedObjectTypeMigrations } from './index_pattern_migration
export const indexPatternSavedObjectType: SavedObjectsType = {
name: 'index-pattern',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
management: {
icon: 'indexPatternApp',
defaultSearchField: 'title',
diff --git a/src/plugins/data/server/saved_objects/query.ts b/src/plugins/data/server/saved_objects/query.ts
index ff0a6cfde811..015cc7c6cb13 100644
--- a/src/plugins/data/server/saved_objects/query.ts
+++ b/src/plugins/data/server/saved_objects/query.ts
@@ -22,7 +22,7 @@ import { SavedObjectsType } from 'kibana/server';
export const querySavedObjectType: SavedObjectsType = {
name: 'query',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
management: {
icon: 'search',
defaultSearchField: 'title',
diff --git a/src/plugins/data/server/saved_objects/search.ts b/src/plugins/data/server/saved_objects/search.ts
index 8b30ff7d0820..54de35a90ae0 100644
--- a/src/plugins/data/server/saved_objects/search.ts
+++ b/src/plugins/data/server/saved_objects/search.ts
@@ -23,7 +23,7 @@ import { searchSavedObjectTypeMigrations } from './search_migrations';
export const searchSavedObjectType: SavedObjectsType = {
name: 'search',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
management: {
icon: 'discoverApp',
defaultSearchField: 'title',
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/saved_object_finder_create_new.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/saved_object_finder_create_new.tsx
index ac39eacab287..3656999d6c19 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/saved_object_finder_create_new.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/saved_object_finder_create_new.tsx
@@ -40,8 +40,8 @@ export function SavedObjectFinderCreateNew({ menuItems }: Props) {
button={
diff --git a/src/plugins/expressions/kibana.json b/src/plugins/expressions/kibana.json
index cba693dd4bc2..5d2112103e94 100644
--- a/src/plugins/expressions/kibana.json
+++ b/src/plugins/expressions/kibana.json
@@ -4,7 +4,6 @@
"server": true,
"ui": true,
"requiredPlugins": [
- "bfetch",
- "inspector"
+ "bfetch"
]
}
diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts
index fbe2f37c648d..418ff6fdf861 100644
--- a/src/plugins/expressions/public/loader.ts
+++ b/src/plugins/expressions/public/loader.ts
@@ -19,13 +19,14 @@
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { filter, map } from 'rxjs/operators';
-import { Adapters, InspectorSession } from '../../inspector/public';
-import { ExpressionRenderHandler } from './render';
+import { Adapters } from '../../inspector/public';
import { IExpressionLoaderParams } from './types';
import { ExpressionAstExpression } from '../common';
-import { getInspector, getExpressionsService } from './services';
import { ExecutionContract } from '../common/execution/execution_contract';
+import { ExpressionRenderHandler } from './render';
+import { getExpressionsService } from './services';
+
type Data = any;
export class ExpressionLoader {
@@ -120,15 +121,6 @@ export class ExpressionLoader {
return this.renderHandler.getElement();
}
- openInspector(title: string): InspectorSession | undefined {
- const inspector = this.inspect();
- if (inspector) {
- return getInspector().open(inspector, {
- title,
- });
- }
- }
-
inspect(): Adapters | undefined {
return this.execution ? (this.execution.inspect() as Adapters) : undefined;
}
diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx
index cb7089f81464..b8f2f693e9c7 100644
--- a/src/plugins/expressions/public/mocks.tsx
+++ b/src/plugins/expressions/public/mocks.tsx
@@ -22,7 +22,6 @@ import { ExpressionsSetup, ExpressionsStart, plugin as pluginInitializer } from
/* eslint-disable */
import { coreMock } from '../../../core/public/mocks';
-import { inspectorPluginMock } from '../../inspector/public/mocks';
import { bfetchPluginMock } from '../../bfetch/public/mocks';
/* eslint-enable */
@@ -89,7 +88,6 @@ const createPlugin = async () => {
const plugin = pluginInitializer(pluginInitializerContext);
const setup = await plugin.setup(coreSetup, {
bfetch: bfetchPluginMock.createSetupContract(),
- inspector: inspectorPluginMock.createSetupContract(),
});
return {
@@ -101,7 +99,6 @@ const createPlugin = async () => {
doStart: async () =>
await plugin.start(coreStart, {
bfetch: bfetchPluginMock.createStartContract(),
- inspector: inspectorPluginMock.createStartContract(),
}),
};
};
diff --git a/src/plugins/expressions/public/plugin.ts b/src/plugins/expressions/public/plugin.ts
index 7c0de271b770..720c3b701d50 100644
--- a/src/plugins/expressions/public/plugin.ts
+++ b/src/plugins/expressions/public/plugin.ts
@@ -29,11 +29,9 @@ import {
ExpressionsServiceStart,
ExecutionContext,
} from '../common';
-import { Setup as InspectorSetup, Start as InspectorStart } from '../../inspector/public';
import { BfetchPublicSetup, BfetchPublicStart } from '../../bfetch/public';
import {
setCoreStart,
- setInspector,
setInterpreter,
setRenderersRegistry,
setNotifications,
@@ -45,12 +43,10 @@ import { render, ExpressionRenderHandler } from './render';
export interface ExpressionsSetupDeps {
bfetch: BfetchPublicSetup;
- inspector: InspectorSetup;
}
export interface ExpressionsStartDeps {
bfetch: BfetchPublicStart;
- inspector: InspectorStart;
}
export interface ExpressionsSetup extends ExpressionsServiceSetup {
@@ -120,7 +116,7 @@ export class ExpressionsPublicPlugin
});
}
- public setup(core: CoreSetup, { inspector, bfetch }: ExpressionsSetupDeps): ExpressionsSetup {
+ public setup(core: CoreSetup, { bfetch }: ExpressionsSetupDeps): ExpressionsSetup {
this.configureExecutor(core);
const { expressions } = this;
@@ -180,9 +176,8 @@ export class ExpressionsPublicPlugin
return Object.freeze(setup);
}
- public start(core: CoreStart, { inspector, bfetch }: ExpressionsStartDeps): ExpressionsStart {
+ public start(core: CoreStart, { bfetch }: ExpressionsStartDeps): ExpressionsStart {
setCoreStart(core);
- setInspector(inspector);
setNotifications(core.notifications);
const { expressions } = this;
diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx
index 242a49c6d663..2c99f173c9f3 100644
--- a/src/plugins/expressions/public/react_expression_renderer.tsx
+++ b/src/plugins/expressions/public/react_expression_renderer.tsx
@@ -17,8 +17,7 @@
* under the License.
*/
-import { useRef, useEffect, useState, useLayoutEffect } from 'react';
-import React from 'react';
+import React, { useRef, useEffect, useState, useLayoutEffect } from 'react';
import classNames from 'classnames';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
diff --git a/src/plugins/expressions/public/render.ts b/src/plugins/expressions/public/render.ts
index ad4d16bcd132..4aaf0da60fc6 100644
--- a/src/plugins/expressions/public/render.ts
+++ b/src/plugins/expressions/public/render.ts
@@ -21,10 +21,11 @@ import * as Rx from 'rxjs';
import { Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
import { RenderError, RenderErrorHandlerFnType, IExpressionLoaderParams } from './types';
-import { getRenderersRegistry } from './services';
import { renderErrorHandler as defaultRenderErrorHandler } from './render_error_handler';
import { IInterpreterRenderHandlers, ExpressionAstExpression } from '../common';
+import { getRenderersRegistry } from './services';
+
export type IExpressionRendererExtraHandlers = Record;
export interface ExpressionRenderHandlerParams {
diff --git a/src/plugins/expressions/public/services.ts b/src/plugins/expressions/public/services.ts
index a203e8741457..016456c95666 100644
--- a/src/plugins/expressions/public/services.ts
+++ b/src/plugins/expressions/public/services.ts
@@ -20,14 +20,11 @@
import { NotificationsStart } from 'kibana/public';
import { createKibanaUtilsCore, createGetterSetter } from '../../kibana_utils/public';
import { ExpressionInterpreter } from './types';
-import { Start as IInspector } from '../../inspector/public';
import { ExpressionsSetup } from './plugin';
import { ExpressionsService } from '../common';
export const { getCoreStart, setCoreStart } = createKibanaUtilsCore();
-export const [getInspector, setInspector] = createGetterSetter('Inspector');
-
export const [getInterpreter, setInterpreter] = createGetterSetter(
'Interpreter'
);
diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts
index 9ad9f14ac565..9bec91b859ab 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 bc643373f5e6..7d720ec842a4 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 000000000000..9d9b21269e10
--- /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 000000000000..5e385eb5ed47
--- /dev/null
+++ b/src/plugins/kibana_utils/public/core/create_start_service_getter.ts
@@ -0,0 +1,98 @@
+/*
+ * 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
+>;
+
+/**
+ * Use this utility to create a synchronous *start* service getter in *setup*
+ * life-cycle of your plugin.
+ *
+ * Below is a usage example in a Kibana plugin.
+ *
+ * ```ts
+ * export interface MyPluginStartDeps {
+ * data: DataPublicPluginStart;
+ * expressions: ExpressionsStart;
+ * inspector: InspectorStart;
+ * uiActions: UiActionsStart;
+ * }
+ *
+ * class MyPlugin implements Plugin {
+ * setup(core: CoreSetup, plugins) {
+ * const start = createStartServicesGetter(core.getStartServices);
+ * plugins.expressions.registerFunction(myExpressionFunction(start));
+ * }
+ *
+ * start(core, plugins: MyPluginStartDeps) {
+ *
+ * }
+ * }
+ * ```
+ *
+ * In `myExpressionFunction` you can make sure you are picking only the dependencies
+ * your function needs using the `Pick` type.
+ *
+ * ```ts
+ * const myExpressionFunction =
+ * (start: StartServicesGetter>) => {
+ *
+ * start().plugins.indexPatterns.something(123);
+ * }
+ * ```
+ *
+ * @param accessor Asynchronous start service accessor provided by platform.
+ * @returns Returns a function which synchronously returns *start* core services
+ * and plugin contracts. If you call this function before the *start* life-cycle
+ * has started it will throw.
+ */
+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 3f08d591300a..8bbb2129071f 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/maps_legacy/public/__tests__/map/service_settings.js b/src/plugins/maps_legacy/public/__tests__/map/service_settings.js
index a9272ea39663..4cbe098501c6 100644
--- a/src/plugins/maps_legacy/public/__tests__/map/service_settings.js
+++ b/src/plugins/maps_legacy/public/__tests__/map/service_settings.js
@@ -143,24 +143,24 @@ describe('service_settings (FKA tilemaptest)', function() {
}
it('accepts an object', async () => {
- serviceSettings.addQueryParams({ foo: 'bar' });
+ serviceSettings.setQueryParams({ foo: 'bar' });
tilemapServices = await serviceSettings.getTMSServices();
await assertQuery({ foo: 'bar' });
});
it('merged additions with previous values', async () => {
// ensure that changes are always additive
- serviceSettings.addQueryParams({ foo: 'bar' });
- serviceSettings.addQueryParams({ bar: 'stool' });
+ serviceSettings.setQueryParams({ foo: 'bar' });
+ serviceSettings.setQueryParams({ bar: 'stool' });
tilemapServices = await serviceSettings.getTMSServices();
await assertQuery({ foo: 'bar', bar: 'stool' });
});
it('overwrites conflicting previous values', async () => {
// ensure that conflicts are overwritten
- serviceSettings.addQueryParams({ foo: 'bar' });
- serviceSettings.addQueryParams({ bar: 'stool' });
- serviceSettings.addQueryParams({ foo: 'tstool' });
+ serviceSettings.setQueryParams({ foo: 'bar' });
+ serviceSettings.setQueryParams({ bar: 'stool' });
+ serviceSettings.setQueryParams({ foo: 'tstool' });
tilemapServices = await serviceSettings.getTMSServices();
await assertQuery({ foo: 'tstool', bar: 'stool' });
});
@@ -168,7 +168,7 @@ describe('service_settings (FKA tilemaptest)', function() {
it('when overridden, should continue to work', async () => {
mapConfig.emsFileApiUrl = emsFileApiUrl2;
mapConfig.emsTileApiUrl = emsTileApiUrl2;
- serviceSettings.addQueryParams({ foo: 'bar' });
+ serviceSettings.setQueryParams({ foo: 'bar' });
tilemapServices = await serviceSettings.getTMSServices();
await assertQuery({ foo: 'bar' });
});
@@ -292,7 +292,7 @@ describe('service_settings (FKA tilemaptest)', function() {
describe('File layers', function() {
it('should load manifest (all props)', async function() {
- serviceSettings.addQueryParams({ foo: 'bar' });
+ serviceSettings.setQueryParams({ foo: 'bar' });
const fileLayers = await serviceSettings.getFileLayers();
expect(fileLayers.length).to.be(18);
const assertions = fileLayers.map(async function(fileLayer) {
diff --git a/src/plugins/maps_legacy/public/map/service_settings.js b/src/plugins/maps_legacy/public/map/service_settings.js
index 11c853d39e10..f4f0d66ee20d 100644
--- a/src/plugins/maps_legacy/public/map/service_settings.js
+++ b/src/plugins/maps_legacy/public/map/service_settings.js
@@ -69,6 +69,10 @@ export class ServiceSettings {
return origin === ORIGIN.EMS && this._showZoomMessage;
}
+ enableZoomMessage() {
+ this._showZoomMessage = true;
+ }
+
disableZoomMessage() {
this._showZoomMessage = false;
}
@@ -148,11 +152,12 @@ export class ServiceSettings {
}
/**
- * Add optional query-parameters to all requests
+ * Set optional query-parameters for all requests
*
* @param additionalQueryParams
*/
- addQueryParams(additionalQueryParams) {
+ setQueryParams(additionalQueryParams) {
+ // Functions more as a "set" than an "add" in ems-client
this._emsClient.addQueryParams(additionalQueryParams);
}
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 0d54d5d3e9c4..570699aa0c0e 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 92e267f17d08..3a05ce59f5d1 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 6b95048749fa..3625a3f91344 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 0c5b74915e58..747af3b9e57d 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 9f997ab7b5df..a0de79da565e 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 3f6e1836cac7..603742f612a6 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/config.ts b/src/plugins/telemetry/server/config.ts
index 9621a8b5619b..99dde0c3b3d9 100644
--- a/src/plugins/telemetry/server/config.ts
+++ b/src/plugins/telemetry/server/config.ts
@@ -36,8 +36,8 @@ export const configSchema = schema.object({
config: schema.string({ defaultValue: getConfigPath() }),
banner: schema.boolean({ defaultValue: true }),
url: schema.conditional(
- schema.contextRef('dev'),
- schema.literal(true),
+ schema.contextRef('dist'),
+ schema.literal(false), // Point to staging if it's not a distributable release
schema.string({
defaultValue: `https://telemetry-staging.elastic.co/xpack/${ENDPOINT_VERSION}/send`,
}),
@@ -46,8 +46,8 @@ export const configSchema = schema.object({
})
),
optInStatusUrl: schema.conditional(
- schema.contextRef('dev'),
- schema.literal(true),
+ schema.contextRef('dist'),
+ schema.literal(false), // Point to staging if it's not a distributable release
schema.string({
defaultValue: `https://telemetry-staging.elastic.co/opt_in_status/${ENDPOINT_VERSION}/send`,
}),
diff --git a/src/plugins/telemetry/server/plugin.ts b/src/plugins/telemetry/server/plugin.ts
index 1df6a665e4d7..d1530c272027 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/telemetry_collection_manager/server/encryption/encrypt.test.ts b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts
index c04625eb1dd4..6d64268569e0 100644
--- a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts
+++ b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts
@@ -22,14 +22,14 @@ import { encryptTelemetry, getKID } from './encrypt';
describe('getKID', () => {
it(`returns 'kibana_dev' kid for development`, async () => {
- const isProd = false;
- const kid = getKID(isProd);
+ const useProdKey = false;
+ const kid = getKID(useProdKey);
expect(kid).toBe('kibana_dev');
});
it(`returns 'kibana_prod' kid for development`, async () => {
- const isProd = true;
- const kid = getKID(isProd);
+ const useProdKey = true;
+ const kid = getKID(useProdKey);
expect(kid).toBe('kibana');
});
});
@@ -41,19 +41,19 @@ describe('encryptTelemetry', () => {
it('encrypts payload', async () => {
const payload = { some: 'value' };
- await encryptTelemetry(payload, { isProd: true });
+ await encryptTelemetry(payload, { useProdKey: true });
expect(createRequestEncryptor).toBeCalledWith(telemetryJWKS);
});
- it('uses kibana kid on { isProd: true }', async () => {
+ it('uses kibana kid on { useProdKey: true }', async () => {
const payload = { some: 'value' };
- await encryptTelemetry(payload, { isProd: true });
+ await encryptTelemetry(payload, { useProdKey: true });
expect(mockEncrypt).toBeCalledWith('kibana', payload);
});
- it('uses kibana_dev kid on { isProd: false }', async () => {
+ it('uses kibana_dev kid on { useProdKey: false }', async () => {
const payload = { some: 'value' };
- await encryptTelemetry(payload, { isProd: false });
+ await encryptTelemetry(payload, { useProdKey: false });
expect(mockEncrypt).toBeCalledWith('kibana_dev', payload);
});
});
diff --git a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts
index 44f053064cfc..89f34d794f05 100644
--- a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts
+++ b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts
@@ -20,12 +20,15 @@
import { createRequestEncryptor } from '@elastic/request-crypto';
import { telemetryJWKS } from './telemetry_jwks';
-export function getKID(isProd = false): string {
- return isProd ? 'kibana' : 'kibana_dev';
+export function getKID(useProdKey = false): string {
+ return useProdKey ? 'kibana' : 'kibana_dev';
}
-export async function encryptTelemetry(payload: any, { isProd = false } = {}): Promise {
- const kid = getKID(isProd);
+export async function encryptTelemetry(
+ payload: any,
+ { useProdKey = false } = {}
+): Promise {
+ const kid = getKID(useProdKey);
const encryptor = await createRequestEncryptor(telemetryJWKS);
const clusters = [].concat(payload);
return Promise.all(clusters.map((cluster: any) => encryptor.encrypt(kid, cluster)));
diff --git a/src/plugins/telemetry_collection_manager/server/plugin.ts b/src/plugins/telemetry_collection_manager/server/plugin.ts
index f2f20e215c53..0b57fae83c0f 100644
--- a/src/plugins/telemetry_collection_manager/server/plugin.ts
+++ b/src/plugins/telemetry_collection_manager/server/plugin.ts
@@ -50,12 +50,12 @@ export class TelemetryCollectionManagerPlugin
private readonly collections: Array> = [];
private usageGetterMethodPriority = -1;
private usageCollection?: UsageCollectionSetup;
- private readonly isDev: boolean;
+ private readonly isDistributable: boolean;
private readonly version: string;
constructor(initializerContext: PluginInitializerContext) {
this.logger = initializerContext.logger.get();
- this.isDev = initializerContext.env.mode.dev;
+ this.isDistributable = initializerContext.env.packageInfo.dist;
this.version = initializerContext.env.packageInfo.version;
}
@@ -158,7 +158,7 @@ export class TelemetryCollectionManagerPlugin
if (config.unencrypted) {
return optInStats;
}
- return encryptTelemetry(optInStats, { isProd: !this.isDev });
+ return encryptTelemetry(optInStats, { useProdKey: this.isDistributable });
}
} catch (err) {
this.logger.debug(`Failed to collect any opt in stats with registered collections.`);
@@ -176,7 +176,6 @@ export class TelemetryCollectionManagerPlugin
) => {
const context: StatsCollectionContext = {
logger: this.logger.get(collection.title),
- isDev: this.isDev,
version: this.version,
...collection.customContext,
};
@@ -206,7 +205,7 @@ export class TelemetryCollectionManagerPlugin
return usageData;
}
- return encryptTelemetry(usageData, { isProd: !this.isDev });
+ return encryptTelemetry(usageData, { useProdKey: this.isDistributable });
}
} catch (err) {
this.logger.debug(
@@ -225,7 +224,6 @@ export class TelemetryCollectionManagerPlugin
): Promise {
const context: StatsCollectionContext = {
logger: this.logger.get(collection.title),
- isDev: this.isDev,
version: this.version,
...collection.customContext,
};
diff --git a/src/plugins/telemetry_collection_manager/server/types.ts b/src/plugins/telemetry_collection_manager/server/types.ts
index e23d6a4c388f..d3a47694d38a 100644
--- a/src/plugins/telemetry_collection_manager/server/types.ts
+++ b/src/plugins/telemetry_collection_manager/server/types.ts
@@ -101,7 +101,6 @@ export interface ESLicense {
export interface StatsCollectionContext {
logger: Logger;
- isDev: boolean;
version: string;
}
diff --git a/src/plugins/timelion/kibana.json b/src/plugins/timelion/kibana.json
deleted file mode 100644
index dddfd6c67e65..000000000000
--- a/src/plugins/timelion/kibana.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "id": "timelion",
- "version": "8.0.0",
- "kibanaVersion": "kibana",
- "configPath": ["timelion"],
- "server": true,
- "ui": true
-}
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 c2ac63c98cbe..dac86249ebbb 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 f1963b94dcf9..1c2ddbc314f9 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 798da09f8e30..014c69f50d55 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 000000000000..6749bd83de39
--- /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 000000000000..d52e22118ccf
--- /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 ebae2d1936a9..ddb7fe3a6b0d 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 22dd61b6bda9..bc3b6ff7bf10 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 5f41840bac99..d6085804e74a 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 bbf2b7844c73..9f0809109e46 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 57ea6d9c9bb3..b84d9638eb97 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 3260e9f7d809..4e77bb196b71 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 0445d270c933..9365017a31ad 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 16b2749c34e1..6f6a80564ce0 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/plugins/timelion/server/index.ts b/src/plugins/vis_type_markdown/server/index.ts
similarity index 67%
rename from src/plugins/timelion/server/index.ts
rename to src/plugins/vis_type_markdown/server/index.ts
index 5d420327f961..73e1712353b1 100644
--- a/src/plugins/timelion/server/index.ts
+++ b/src/plugins/vis_type_markdown/server/index.ts
@@ -17,19 +17,18 @@
* under the License.
*/
-import { PluginInitializerContext } from '../../../../src/core/server';
-import { configSchema } from '../config';
-import { Plugin } from './plugin';
+import { PluginConfigDescriptor } from 'kibana/server';
-export { PluginSetupContract } from './plugin';
+import { configSchema, ConfigSchema } from '../config';
-export const config = {
+export const config: PluginConfigDescriptor = {
schema: configSchema,
- exposeToBrowser: {
- ui: {
- enabled: true,
- },
- },
+ deprecations: ({ renameFromRoot }) => [
+ renameFromRoot('markdown_vis.enabled', 'vis_type_markdown.enabled'),
+ ],
};
-export const plugin = (initializerContext: PluginInitializerContext) =>
- new Plugin(initializerContext);
+
+export const plugin = () => ({
+ setup() {},
+ start() {},
+});
diff --git a/src/plugins/vis_type_metric/config.ts b/src/plugins/vis_type_metric/config.ts
new file mode 100644
index 000000000000..6749bd83de39
--- /dev/null
+++ b/src/plugins/vis_type_metric/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_metric/kibana.json b/src/plugins/vis_type_metric/kibana.json
new file mode 100644
index 000000000000..24135d257b31
--- /dev/null
+++ b/src/plugins/vis_type_metric/kibana.json
@@ -0,0 +1,8 @@
+{
+ "id": "visTypeMetric",
+ "version": "8.0.0",
+ "kibanaVersion": "kibana",
+ "server": true,
+ "ui": true,
+ "requiredPlugins": ["data", "visualizations", "charts","expressions"]
+}
diff --git a/src/legacy/core_plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap b/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap
similarity index 100%
rename from src/legacy/core_plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap
rename to src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap
diff --git a/src/legacy/core_plugins/vis_type_metric/public/_metric_vis.scss b/src/plugins/vis_type_metric/public/_metric_vis.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_metric/public/_metric_vis.scss
rename to src/plugins/vis_type_metric/public/_metric_vis.scss
diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap b/src/plugins/vis_type_metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/vis_type_metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap
rename to src/plugins/vis_type_metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap
diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_component.test.tsx
similarity index 97%
rename from src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx
rename to src/plugins/vis_type_metric/public/components/metric_vis_component.test.tsx
index 2bd423656b0f..3969b28d7541 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx
+++ b/src/plugins/vis_type_metric/public/components/metric_vis_component.test.tsx
@@ -21,7 +21,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { MetricVisComponent, MetricVisComponentProps } from './metric_vis_component';
-import { ExprVis } from '../../../../../plugins/visualizations/public';
+import { ExprVis } from '../../../visualizations/public';
jest.mock('../services', () => ({
getFormatService: () => ({
diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx
similarity index 96%
rename from src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx
rename to src/plugins/vis_type_metric/public/components/metric_vis_component.tsx
index de2cc66a99c7..eb3986b6388f 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.tsx
+++ b/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx
@@ -22,12 +22,12 @@ import React, { Component } from 'react';
import { isColorDark } from '@elastic/eui';
import { MetricVisValue } from './metric_vis_value';
import { Input } from '../metric_vis_fn';
-import { FieldFormatsContentType, IFieldFormat } from '../../../../../plugins/data/public';
-import { KibanaDatatable } from '../../../../../plugins/expressions/public';
-import { getHeatmapColors } from '../../../../../plugins/charts/public';
+import { FieldFormatsContentType, IFieldFormat } from '../../../data/public';
+import { KibanaDatatable } from '../../../expressions/public';
+import { getHeatmapColors } from '../../../charts/public';
import { VisParams, MetricVisMetric } from '../types';
import { getFormatService } from '../services';
-import { SchemaConfig, ExprVis } from '../../../../../plugins/visualizations/public';
+import { SchemaConfig, ExprVis } from '../../../visualizations/public';
export interface MetricVisComponentProps {
visParams: VisParams;
diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx
similarity index 99%
rename from src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx
rename to src/plugins/vis_type_metric/public/components/metric_vis_options.tsx
index 56af0ee91878..009d63ded39b 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx
+++ b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx
@@ -38,7 +38,7 @@ import {
RangeOption,
SetColorSchemaOptionsValue,
SetColorRangeValue,
-} from '../../../../../plugins/charts/public';
+} from '../../../charts/public';
import { MetricVisParam, VisParams } from '../types';
function MetricVisOptions({
diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_value.test.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_value.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_value.test.tsx
rename to src/plugins/vis_type_metric/public/components/metric_vis_value.test.tsx
diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_value.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_value.tsx
similarity index 100%
rename from src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_value.tsx
rename to src/plugins/vis_type_metric/public/components/metric_vis_value.tsx
diff --git a/src/legacy/core_plugins/vis_type_metric/public/index.scss b/src/plugins/vis_type_metric/public/index.scss
similarity index 66%
rename from src/legacy/core_plugins/vis_type_metric/public/index.scss
rename to src/plugins/vis_type_metric/public/index.scss
index e4116b69bf0f..638f9ac1ef93 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/index.scss
+++ b/src/plugins/vis_type_metric/public/index.scss
@@ -1,5 +1,3 @@
-@import 'src/legacy/ui/public/styles/styling_constants';
-
// Prefix all styles with "mtr" to avoid conflicts.
// Examples
// mtrChart
@@ -7,4 +5,4 @@
// mtrChart__legend--small
// mtrChart__legend-isLoading
-@import './metric_vis';
+@import 'metric_vis';
diff --git a/src/legacy/core_plugins/vis_type_metric/public/index.ts b/src/plugins/vis_type_metric/public/index.ts
similarity index 92%
rename from src/legacy/core_plugins/vis_type_metric/public/index.ts
rename to src/plugins/vis_type_metric/public/index.ts
index 7499babef58a..3d3e1879a51d 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/index.ts
+++ b/src/plugins/vis_type_metric/public/index.ts
@@ -16,8 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-
-import { PluginInitializerContext } from '../../../../core/public';
+import './index.scss';
+import { PluginInitializerContext } from 'kibana/public';
import { MetricVisPlugin as Plugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts b/src/plugins/vis_type_metric/public/metric_vis_fn.test.ts
similarity index 90%
rename from src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts
rename to src/plugins/vis_type_metric/public/metric_vis_fn.test.ts
index 4094cd4eff06..3ed8f8f79a83 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts
+++ b/src/plugins/vis_type_metric/public/metric_vis_fn.test.ts
@@ -18,10 +18,7 @@
*/
import { createMetricVisFn } from './metric_vis_fn';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { functionWrapper } from '../../../../plugins/expressions/common/expression_functions/specs/tests/utils';
-
-jest.mock('ui/new_platform');
+import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils';
describe('interpreter/functions#metric', () => {
const fn = functionWrapper(createMetricVisFn());
diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts b/src/plugins/vis_type_metric/public/metric_vis_fn.ts
similarity index 98%
rename from src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts
rename to src/plugins/vis_type_metric/public/metric_vis_fn.ts
index 52784da1bd73..3d16fed0fa38 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts
+++ b/src/plugins/vis_type_metric/public/metric_vis_fn.ts
@@ -25,9 +25,9 @@ import {
Range,
Render,
Style,
-} from '../../../../plugins/expressions/public';
+} from '../../expressions/public';
import { visType, DimensionsVisParam, VisParams } from './types';
-import { ColorSchemas, vislibColorMaps, ColorModes } from '../../../../plugins/charts/public';
+import { ColorSchemas, vislibColorMaps, ColorModes } from '../../charts/public';
export type Input = KibanaDatatable;
diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts b/src/plugins/vis_type_metric/public/metric_vis_type.test.ts
similarity index 97%
rename from src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts
rename to src/plugins/vis_type_metric/public/metric_vis_type.test.ts
index 706693eff100..636118c692aa 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts
+++ b/src/plugins/vis_type_metric/public/metric_vis_type.test.ts
@@ -20,8 +20,6 @@
import { createMetricVisTypeDefinition } from './metric_vis_type';
import { MetricVisComponent } from './components/metric_vis_component';
-jest.mock('ui/new_platform');
-
describe('metric_vis - createMetricVisTypeDefinition', () => {
it('has metric vis component set', () => {
const def = createMetricVisTypeDefinition();
diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts b/src/plugins/vis_type_metric/public/metric_vis_type.ts
similarity index 93%
rename from src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts
rename to src/plugins/vis_type_metric/public/metric_vis_type.ts
index ab31c5692141..b7e9213283be 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts
+++ b/src/plugins/vis_type_metric/public/metric_vis_type.ts
@@ -21,9 +21,9 @@ import { i18n } from '@kbn/i18n';
import { MetricVisComponent } from './components/metric_vis_component';
import { MetricVisOptions } from './components/metric_vis_options';
-import { ColorSchemas, colorSchemas, ColorModes } from '../../../../plugins/charts/public';
-import { AggGroupNames } from '../../../../plugins/data/public';
-import { Schemas } from '../../../../plugins/vis_default_editor/public';
+import { ColorSchemas, colorSchemas, ColorModes } from '../../charts/public';
+import { AggGroupNames } from '../../data/public';
+import { Schemas } from '../../vis_default_editor/public';
export const createMetricVisTypeDefinition = () => ({
name: 'metric',
diff --git a/src/legacy/core_plugins/vis_type_metric/public/plugin.ts b/src/plugins/vis_type_metric/public/plugin.ts
similarity index 79%
rename from src/legacy/core_plugins/vis_type_metric/public/plugin.ts
rename to src/plugins/vis_type_metric/public/plugin.ts
index cb65d5cafbdd..a3951fa46c21 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/plugin.ts
+++ b/src/plugins/vis_type_metric/public/plugin.ts
@@ -17,15 +17,16 @@
* 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 'kibana/public';
+import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public';
+import { VisualizationsSetup } from '../../visualizations/public';
import { createMetricVisFn } from './metric_vis_fn';
import { createMetricVisTypeDefinition } from './metric_vis_type';
-import { ChartsPluginSetup } from '../../../../plugins/charts/public';
-import { DataPublicPluginStart } from '../../../../plugins/data/public';
+import { ChartsPluginSetup } from '../../charts/public';
+import { DataPublicPluginStart } from '../../data/public';
import { setFormatService } from './services';
+import { ConfigSchema } from '../config';
/** @internal */
export interface MetricVisPluginSetupDependencies {
@@ -41,9 +42,9 @@ export interface MetricVisPluginStartDependencies {
/** @internal */
export class MetricVisPlugin implements Plugin {
- initializerContext: PluginInitializerContext;
+ initializerContext: PluginInitializerContext;
- constructor(initializerContext: PluginInitializerContext) {
+ constructor(initializerContext: PluginInitializerContext) {
this.initializerContext = initializerContext;
}
diff --git a/src/legacy/core_plugins/vis_type_metric/public/services.ts b/src/plugins/vis_type_metric/public/services.ts
similarity index 86%
rename from src/legacy/core_plugins/vis_type_metric/public/services.ts
rename to src/plugins/vis_type_metric/public/services.ts
index b303ccd5aeed..681afbaf0b26 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/services.ts
+++ b/src/plugins/vis_type_metric/public/services.ts
@@ -17,8 +17,8 @@
* under the License.
*/
-import { createGetterSetter } from '../../../../plugins/kibana_utils/public';
-import { DataPublicPluginStart } from '../../../../plugins/data/public';
+import { createGetterSetter } from '../../kibana_utils/common';
+import { DataPublicPluginStart } from '../../data/public';
export const [getFormatService, setFormatService] = createGetterSetter<
DataPublicPluginStart['fieldFormats']
diff --git a/src/legacy/core_plugins/vis_type_metric/public/types.ts b/src/plugins/vis_type_metric/public/types.ts
similarity index 86%
rename from src/legacy/core_plugins/vis_type_metric/public/types.ts
rename to src/plugins/vis_type_metric/public/types.ts
index cdf62cba934d..e1f2c7721a42 100644
--- a/src/legacy/core_plugins/vis_type_metric/public/types.ts
+++ b/src/plugins/vis_type_metric/public/types.ts
@@ -17,9 +17,9 @@
* under the License.
*/
-import { Range } from '../../../../plugins/expressions/public';
-import { SchemaConfig } from '../../../../plugins/visualizations/public';
-import { ColorModes, ColorSchemas, Labels, Style } from '../../../../plugins/charts/public';
+import { Range } from '../../expressions/public';
+import { SchemaConfig } from '../../visualizations/public';
+import { ColorModes, Labels, Style, ColorSchemas } from '../../charts/public';
export const visType = 'metric';
diff --git a/src/plugins/timelion/public/index.ts b/src/plugins/vis_type_metric/server/index.ts
similarity index 69%
rename from src/plugins/timelion/public/index.ts
rename to src/plugins/vis_type_metric/server/index.ts
index b05c4f8a30b2..ce550dc81dd8 100644
--- a/src/plugins/timelion/public/index.ts
+++ b/src/plugins/vis_type_metric/server/index.ts
@@ -17,14 +17,18 @@
* under the License.
*/
-import { CoreStart, PluginInitializerContext } from 'kibana/public';
-import { ConfigSchema } from '../config';
+import { PluginConfigDescriptor } from 'kibana/server';
-export const plugin = (initializerContext: PluginInitializerContext) => ({
+import { configSchema, ConfigSchema } from '../config';
+
+export const config: PluginConfigDescriptor = {
+ schema: configSchema,
+ deprecations: ({ renameFromRoot }) => [
+ renameFromRoot('metric_vis.enabled', 'vis_type_metric.enabled'),
+ ],
+};
+
+export const plugin = () => ({
setup() {},
- start(core: CoreStart) {
- if (initializerContext.config.get().ui.enabled === false) {
- core.chrome.navLinks.update('timelion', { hidden: true });
- }
- },
+ start() {},
});
diff --git a/src/legacy/core_plugins/vis_type_timelion/README.md b/src/plugins/vis_type_timelion/README.md
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/README.md
rename to src/plugins/vis_type_timelion/README.md
diff --git a/src/plugins/timelion/common/chain.peg b/src/plugins/vis_type_timelion/common/chain.peg
similarity index 100%
rename from src/plugins/timelion/common/chain.peg
rename to src/plugins/vis_type_timelion/common/chain.peg
diff --git a/src/plugins/timelion/common/lib/calculate_interval.test.ts b/src/plugins/vis_type_timelion/common/lib/calculate_interval.test.ts
similarity index 100%
rename from src/plugins/timelion/common/lib/calculate_interval.test.ts
rename to src/plugins/vis_type_timelion/common/lib/calculate_interval.test.ts
diff --git a/src/plugins/timelion/common/lib/calculate_interval.ts b/src/plugins/vis_type_timelion/common/lib/calculate_interval.ts
similarity index 100%
rename from src/plugins/timelion/common/lib/calculate_interval.ts
rename to src/plugins/vis_type_timelion/common/lib/calculate_interval.ts
diff --git a/src/plugins/timelion/common/lib/index.ts b/src/plugins/vis_type_timelion/common/lib/index.ts
similarity index 100%
rename from src/plugins/timelion/common/lib/index.ts
rename to src/plugins/vis_type_timelion/common/lib/index.ts
diff --git a/src/plugins/timelion/common/lib/to_milliseconds.ts b/src/plugins/vis_type_timelion/common/lib/to_milliseconds.ts
similarity index 100%
rename from src/plugins/timelion/common/lib/to_milliseconds.ts
rename to src/plugins/vis_type_timelion/common/lib/to_milliseconds.ts
diff --git a/src/plugins/timelion/common/types.ts b/src/plugins/vis_type_timelion/common/types.ts
similarity index 100%
rename from src/plugins/timelion/common/types.ts
rename to src/plugins/vis_type_timelion/common/types.ts
diff --git a/src/plugins/timelion/config.ts b/src/plugins/vis_type_timelion/config.ts
similarity index 100%
rename from src/plugins/timelion/config.ts
rename to src/plugins/vis_type_timelion/config.ts
diff --git a/src/plugins/vis_type_timelion/kibana.json b/src/plugins/vis_type_timelion/kibana.json
new file mode 100644
index 000000000000..85c282c51a2e
--- /dev/null
+++ b/src/plugins/vis_type_timelion/kibana.json
@@ -0,0 +1,8 @@
+{
+ "id": "visTypeTimelion",
+ "version": "8.0.0",
+ "kibanaVersion": "kibana",
+ "server": true,
+ "ui": true,
+ "requiredPlugins": ["visualizations", "data", "expressions"]
+}
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js b/src/plugins/vis_type_timelion/public/_generated_/chain.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js
rename to src/plugins/vis_type_timelion/public/_generated_/chain.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/_timelion_editor.scss b/src/plugins/vis_type_timelion/public/_timelion_editor.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/_timelion_editor.scss
rename to src/plugins/vis_type_timelion/public/_timelion_editor.scss
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/_timelion_vis.scss b/src/plugins/vis_type_timelion/public/_timelion_vis.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/_timelion_vis.scss
rename to src/plugins/vis_type_timelion/public/_timelion_vis.scss
diff --git a/src/plugins/vis_type_timelion/public/components/_index.scss b/src/plugins/vis_type_timelion/public/components/_index.scss
new file mode 100644
index 000000000000..707c9dafebe2
--- /dev/null
+++ b/src/plugins/vis_type_timelion/public/components/_index.scss
@@ -0,0 +1,2 @@
+@import 'panel';
+@import 'timelion_expression_input';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/_panel.scss b/src/plugins/vis_type_timelion/public/components/_panel.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/_panel.scss
rename to src/plugins/vis_type_timelion/public/components/_panel.scss
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/_timelion_expression_input.scss b/src/plugins/vis_type_timelion/public/components/_timelion_expression_input.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/_timelion_expression_input.scss
rename to src/plugins/vis_type_timelion/public/components/_timelion_expression_input.scss
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/chart.tsx b/src/plugins/vis_type_timelion/public/components/chart.tsx
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/chart.tsx
rename to src/plugins/vis_type_timelion/public/components/chart.tsx
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/index.ts b/src/plugins/vis_type_timelion/public/components/index.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/index.ts
rename to src/plugins/vis_type_timelion/public/components/index.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/panel.tsx b/src/plugins/vis_type_timelion/public/components/panel.tsx
similarity index 98%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/panel.tsx
rename to src/plugins/vis_type_timelion/public/components/panel.tsx
index 3b42fa7dfcbb..8f796526e852 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/panel.tsx
+++ b/src/plugins/vis_type_timelion/public/components/panel.tsx
@@ -22,9 +22,9 @@ import $ from 'jquery';
import moment from 'moment-timezone';
import { debounce, compact, get, each, cloneDeep, last, map } from 'lodash';
-import { useKibana } from '../../../../../plugins/kibana_react/public';
+import { useKibana } from '../../../kibana_react/public';
import '../flot';
-import { DEFAULT_TIME_FORMAT } from '../../../../../plugins/timelion/common/lib';
+import { DEFAULT_TIME_FORMAT } from '../../common/lib';
import {
buildSeriesData,
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx b/src/plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
similarity index 96%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
rename to src/plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
index c317451b8201..999409ef3506 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
+++ b/src/plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
@@ -22,13 +22,10 @@ import { EuiFormLabel } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { monaco } from '@kbn/ui-shared-deps/monaco';
-import { CodeEditor, useKibana } from '../../../../../plugins/kibana_react/public';
+import { CodeEditor, useKibana } from '../../../kibana_react/public';
import { suggest, getSuggestion } from './timelion_expression_input_helpers';
import { getArgValueSuggestions } from '../helpers/arg_value_suggestions';
-import {
- ITimelionFunction,
- TimelionFunctionArgs,
-} from '../../../../../plugins/timelion/common/types';
+import { ITimelionFunction, TimelionFunctionArgs } from '../../common/types';
const LANGUAGE_ID = 'timelion_expression';
monaco.languages.register({ id: LANGUAGE_ID });
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts b/src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
similarity index 99%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
rename to src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
index 2f99256e2a19..2ff6809d1c83 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
+++ b/src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
@@ -22,7 +22,7 @@ import { getArgValueSuggestions } from '../helpers/arg_value_suggestions';
import { setIndexPatterns, setSavedObjectsClient } from '../helpers/plugin_services';
import { IndexPatternsContract } from 'src/plugins/data/public';
import { SavedObjectsClient } from 'kibana/public';
-import { ITimelionFunction } from '../../../../../plugins/timelion/common/types';
+import { ITimelionFunction } from '../../common/types';
describe('Timelion expression suggestions', () => {
setIndexPatterns({} as IndexPatternsContract);
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts b/src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
similarity index 98%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
rename to src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
index 6f23c864419e..04cb54306c90 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
+++ b/src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
@@ -27,10 +27,7 @@ import { Parser } from 'pegjs';
import { parse } from '../_generated_/chain';
import { ArgValueSuggestions, FunctionArg, Location } from '../helpers/arg_value_suggestions';
-import {
- ITimelionFunction,
- TimelionFunctionArgs,
-} from '../../../../../plugins/timelion/common/types';
+import { ITimelionFunction, TimelionFunctionArgs } from '../../common/types';
export enum SUGGESTION_TYPE {
ARGUMENTS = 'arguments',
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_interval.tsx b/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx
similarity index 96%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/timelion_interval.tsx
rename to src/plugins/vis_type_timelion/public/components/timelion_interval.tsx
index 8a8e1b22fb78..985ecaeaf3e5 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_interval.tsx
+++ b/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx
@@ -21,9 +21,9 @@ import React, { useMemo, useCallback } from 'react';
import { EuiFormRow, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { search } from '../../../../../plugins/data/public';
+import { search } from '../../../data/public';
const { isValidEsInterval } = search.aggs;
-import { useValidation } from '../../../../../plugins/vis_default_editor/public';
+import { useValidation } from '../../../vis_default_editor/public';
const intervalOptions = [
{
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_vis.tsx b/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx
similarity index 95%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/timelion_vis.tsx
rename to src/plugins/vis_type_timelion/public/components/timelion_vis.tsx
index 0fad0a164bf0..4bb07fe74ee8 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_vis.tsx
+++ b/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx
@@ -23,7 +23,7 @@ import { IUiSettingsClient } from 'kibana/public';
import { ChartComponent } from './chart';
import { VisParams } from '../timelion_vis_fn';
import { TimelionSuccessResponse } from '../helpers/timelion_request_handler';
-import { ExprVis } from '../../../../../plugins/visualizations/public';
+import { ExprVis } from '../../../visualizations/public';
export interface TimelionVisComponentProp {
config: IUiSettingsClient;
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/flot.js b/src/plugins/vis_type_timelion/public/flot.js
similarity index 72%
rename from src/legacy/core_plugins/vis_type_timelion/public/flot.js
rename to src/plugins/vis_type_timelion/public/flot.js
index d6ca6d96c34e..1ccb40c93a3d 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/flot.js
+++ b/src/plugins/vis_type_timelion/public/flot.js
@@ -17,10 +17,10 @@
* under the License.
*/
-require('jquery.flot');
-require('jquery.flot.time');
-require('jquery.flot.symbol');
-require('jquery.flot.crosshair');
-require('jquery.flot.selection');
-require('jquery.flot.stack');
-require('jquery.flot.axislabels');
+import './webpackShims/jquery.flot';
+import './webpackShims/jquery.flot.time';
+import './webpackShims/jquery.flot.symbol';
+import './webpackShims/jquery.flot.crosshair';
+import './webpackShims/jquery.flot.selection';
+import './webpackShims/jquery.flot.stack';
+import './webpackShims/jquery.flot.axislabels';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts b/src/plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
similarity index 97%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
rename to src/plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
index ea9532964d6f..76c25b9b9e8d 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
+++ b/src/plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
@@ -19,11 +19,8 @@
import { get } from 'lodash';
import { getIndexPatterns, getSavedObjectsClient } from './plugin_services';
-import { TimelionFunctionArgs } from '../../../../../plugins/timelion/common/types';
-import {
- indexPatterns as indexPatternsUtils,
- IndexPatternAttributes,
-} from '../../../../../plugins/data/public';
+import { TimelionFunctionArgs } from '../../common/types';
+import { indexPatterns as indexPatternsUtils, IndexPatternAttributes } from '../../../data/public';
export interface Location {
min: number;
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/get_timezone.ts b/src/plugins/vis_type_timelion/public/helpers/get_timezone.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/get_timezone.ts
rename to src/plugins/vis_type_timelion/public/helpers/get_timezone.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/panel_utils.ts b/src/plugins/vis_type_timelion/public/helpers/panel_utils.ts
similarity index 98%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/panel_utils.ts
rename to src/plugins/vis_type_timelion/public/helpers/panel_utils.ts
index f932e5ee4b2f..db29d9112be8 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/helpers/panel_utils.ts
+++ b/src/plugins/vis_type_timelion/public/helpers/panel_utils.ts
@@ -23,7 +23,7 @@ import moment, { Moment } from 'moment-timezone';
import { TimefilterContract } from 'src/plugins/data/public';
import { IUiSettingsClient } from 'kibana/public';
-import { calculateInterval } from '../../../../../plugins/timelion/common/lib';
+import { calculateInterval } from '../../common/lib';
import { xaxisFormatterProvider } from './xaxis_formatter';
import { Series } from './timelion_request_handler';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/plugin_services.ts b/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts
similarity index 93%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/plugin_services.ts
rename to src/plugins/vis_type_timelion/public/helpers/plugin_services.ts
index 5ba4ee5e4798..b055626934ee 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/helpers/plugin_services.ts
+++ b/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts
@@ -19,7 +19,7 @@
import { IndexPatternsContract } from 'src/plugins/data/public';
import { SavedObjectsClientContract } from 'kibana/public';
-import { createGetterSetter } from '../../../../../plugins/kibana_utils/public';
+import { createGetterSetter } from '../../../kibana_utils/public';
export const [getIndexPatterns, setIndexPatterns] = createGetterSetter(
'IndexPatterns'
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_formatters.test.ts b/src/plugins/vis_type_timelion/public/helpers/tick_formatters.test.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_formatters.test.ts
rename to src/plugins/vis_type_timelion/public/helpers/tick_formatters.test.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_formatters.ts b/src/plugins/vis_type_timelion/public/helpers/tick_formatters.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_formatters.ts
rename to src/plugins/vis_type_timelion/public/helpers/tick_formatters.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_generator.test.ts b/src/plugins/vis_type_timelion/public/helpers/tick_generator.test.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_generator.test.ts
rename to src/plugins/vis_type_timelion/public/helpers/tick_generator.test.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_generator.ts b/src/plugins/vis_type_timelion/public/helpers/tick_generator.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_generator.ts
rename to src/plugins/vis_type_timelion/public/helpers/tick_generator.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts b/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
similarity index 95%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
rename to src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
index 61e31420f73b..a654f7935af5 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
+++ b/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
@@ -19,8 +19,8 @@
import { i18n } from '@kbn/i18n';
import { KIBANA_CONTEXT_NAME } from 'src/plugins/expressions/public';
-import { VisParams } from '../../../../../plugins/visualizations/public';
-import { TimeRange, Filter, esQuery, Query } from '../../../../../plugins/data/public';
+import { VisParams } from '../../../visualizations/public';
+import { TimeRange, Filter, esQuery, Query } from '../../../data/public';
import { TimelionVisDependencies } from '../plugin';
import { getTimezone } from './get_timezone';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/xaxis_formatter.ts b/src/plugins/vis_type_timelion/public/helpers/xaxis_formatter.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/xaxis_formatter.ts
rename to src/plugins/vis_type_timelion/public/helpers/xaxis_formatter.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/index.scss b/src/plugins/vis_type_timelion/public/index.scss
similarity index 59%
rename from src/legacy/core_plugins/vis_type_timelion/public/index.scss
rename to src/plugins/vis_type_timelion/public/index.scss
index 313f14a8acf6..00e9a8852096 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/index.scss
+++ b/src/plugins/vis_type_timelion/public/index.scss
@@ -1,5 +1,3 @@
-@import 'src/legacy/ui/public/styles/styling_constants';
-
@import './timelion_vis';
@import './timelion_editor';
@import './components/index';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/index.ts b/src/plugins/vis_type_timelion/public/index.ts
similarity index 90%
rename from src/legacy/core_plugins/vis_type_timelion/public/index.ts
rename to src/plugins/vis_type_timelion/public/index.ts
index 6292e2ad3eb0..0aa5f3a81003 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/index.ts
+++ b/src/plugins/vis_type_timelion/public/index.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { PluginInitializerContext } from '../../../../core/public';
+import { PluginInitializerContext } from 'kibana/public';
import { TimelionVisPlugin as Plugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
@@ -25,3 +25,5 @@ export function plugin(initializerContext: PluginInitializerContext) {
}
export { getTimezone } from './helpers/get_timezone';
+
+export { VisTypeTimelionPluginStart } from './plugin';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/plugin.ts b/src/plugins/vis_type_timelion/public/plugin.ts
similarity index 67%
rename from src/legacy/core_plugins/vis_type_timelion/public/plugin.ts
rename to src/plugins/vis_type_timelion/public/plugin.ts
index b5aa64db19aa..060fec04deb3 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/plugin.ts
+++ b/src/plugins/vis_type_timelion/public/plugin.ts
@@ -26,16 +26,21 @@ import {
HttpSetup,
} from 'kibana/public';
import { Plugin as ExpressionsPlugin } from 'src/plugins/expressions/public';
-import { DataPublicPluginSetup, TimefilterContract } from 'src/plugins/data/public';
+import {
+ DataPublicPluginSetup,
+ DataPublicPluginStart,
+ TimefilterContract,
+} from 'src/plugins/data/public';
-import { PluginsStart } from './legacy_imports';
-import { VisualizationsSetup } from '../../../../plugins/visualizations/public';
+import { VisualizationsSetup } from '../../visualizations/public';
import { getTimelionVisualizationConfig } from './timelion_vis_fn';
import { getTimelionVisDefinition } from './timelion_vis_type';
import { setIndexPatterns, setSavedObjectsClient } from './helpers/plugin_services';
+import { ConfigSchema } from '../config';
-type TimelionVisCoreSetup = CoreSetup;
+import './index.scss';
+import { getArgValueSuggestions } from './helpers/arg_value_suggestions';
/** @internal */
export interface TimelionVisDependencies extends Partial {
@@ -52,11 +57,28 @@ export interface TimelionVisSetupDependencies {
}
/** @internal */
-export class TimelionVisPlugin implements Plugin {
- constructor(public initializerContext: PluginInitializerContext) {}
+export interface TimelionVisStartDependencies {
+ data: DataPublicPluginStart;
+}
+
+/** @public */
+export interface VisTypeTimelionPluginStart {
+ getArgValueSuggestions: typeof getArgValueSuggestions;
+}
+
+/** @internal */
+export class TimelionVisPlugin
+ implements
+ Plugin<
+ void,
+ VisTypeTimelionPluginStart,
+ TimelionVisSetupDependencies,
+ TimelionVisStartDependencies
+ > {
+ constructor(public initializerContext: PluginInitializerContext) {}
- public async setup(
- core: TimelionVisCoreSetup,
+ public setup(
+ core: CoreSetup,
{ expressions, visualizations, data }: TimelionVisSetupDependencies
) {
const dependencies: TimelionVisDependencies = {
@@ -69,8 +91,15 @@ export class TimelionVisPlugin implements Plugin {
visualizations.createReactVisualization(getTimelionVisDefinition(dependencies));
}
- public start(core: CoreStart, plugins: PluginsStart) {
+ public start(core: CoreStart, plugins: TimelionVisStartDependencies) {
setIndexPatterns(plugins.data.indexPatterns);
setSavedObjectsClient(core.savedObjects.client);
+ if (this.initializerContext.config.get().ui.enabled === false) {
+ core.chrome.navLinks.update('timelion', { hidden: true });
+ }
+
+ return {
+ getArgValueSuggestions,
+ };
}
}
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx b/src/plugins/vis_type_timelion/public/timelion_options.tsx
similarity index 95%
rename from src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx
rename to src/plugins/vis_type_timelion/public/timelion_options.tsx
index afffcf7ccaf7..dfe017d3a273 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx
+++ b/src/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_fn.ts b/src/plugins/vis_type_timelion/public/timelion_vis_fn.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_fn.ts
rename to src/plugins/vis_type_timelion/public/timelion_vis_fn.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx b/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
similarity index 84%
rename from src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx
rename to src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
index 5be77b3e51a6..52addb3c2d9d 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx
+++ b/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
@@ -20,11 +20,11 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
-import { KibanaContextProvider } from '../../../../plugins/kibana_react/public';
-import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public';
+import { KibanaContextProvider } from '../../kibana_react/public';
+import { DefaultEditorSize } from '../../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/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.axislabels.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.axislabels.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.axislabels.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.axislabels.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.crosshair.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.crosshair.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.crosshair.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.crosshair.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.selection.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.selection.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.selection.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.selection.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.stack.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.stack.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.stack.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.stack.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.symbol.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.symbol.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.symbol.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.symbol.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.time.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.time.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.time.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.time.js
diff --git a/src/plugins/timelion/server/fit_functions/average.js b/src/plugins/vis_type_timelion/server/fit_functions/average.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/average.js
rename to src/plugins/vis_type_timelion/server/fit_functions/average.js
diff --git a/src/plugins/timelion/server/fit_functions/average.test.js b/src/plugins/vis_type_timelion/server/fit_functions/average.test.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/average.test.js
rename to src/plugins/vis_type_timelion/server/fit_functions/average.test.js
diff --git a/src/plugins/timelion/server/fit_functions/carry.js b/src/plugins/vis_type_timelion/server/fit_functions/carry.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/carry.js
rename to src/plugins/vis_type_timelion/server/fit_functions/carry.js
diff --git a/src/plugins/timelion/server/fit_functions/carry.test.js b/src/plugins/vis_type_timelion/server/fit_functions/carry.test.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/carry.test.js
rename to src/plugins/vis_type_timelion/server/fit_functions/carry.test.js
diff --git a/src/plugins/timelion/server/fit_functions/nearest.js b/src/plugins/vis_type_timelion/server/fit_functions/nearest.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/nearest.js
rename to src/plugins/vis_type_timelion/server/fit_functions/nearest.js
diff --git a/src/plugins/timelion/server/fit_functions/none.js b/src/plugins/vis_type_timelion/server/fit_functions/none.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/none.js
rename to src/plugins/vis_type_timelion/server/fit_functions/none.js
diff --git a/src/plugins/timelion/server/fit_functions/scale.js b/src/plugins/vis_type_timelion/server/fit_functions/scale.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/scale.js
rename to src/plugins/vis_type_timelion/server/fit_functions/scale.js
diff --git a/src/plugins/timelion/server/handlers/chain_runner.js b/src/plugins/vis_type_timelion/server/handlers/chain_runner.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/chain_runner.js
rename to src/plugins/vis_type_timelion/server/handlers/chain_runner.js
diff --git a/src/plugins/timelion/server/handlers/lib/arg_type.js b/src/plugins/vis_type_timelion/server/handlers/lib/arg_type.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/arg_type.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/arg_type.js
diff --git a/src/plugins/timelion/server/handlers/lib/index_arguments.js b/src/plugins/vis_type_timelion/server/handlers/lib/index_arguments.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/index_arguments.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/index_arguments.js
diff --git a/src/plugins/timelion/server/handlers/lib/parse_sheet.js b/src/plugins/vis_type_timelion/server/handlers/lib/parse_sheet.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/parse_sheet.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/parse_sheet.js
diff --git a/src/plugins/timelion/server/handlers/lib/parse_sheet.test.js b/src/plugins/vis_type_timelion/server/handlers/lib/parse_sheet.test.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/parse_sheet.test.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/parse_sheet.test.js
diff --git a/src/plugins/timelion/server/handlers/lib/preprocess_chain.js b/src/plugins/vis_type_timelion/server/handlers/lib/preprocess_chain.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/preprocess_chain.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/preprocess_chain.js
diff --git a/src/plugins/timelion/server/handlers/lib/reposition_arguments.js b/src/plugins/vis_type_timelion/server/handlers/lib/reposition_arguments.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/reposition_arguments.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/reposition_arguments.js
diff --git a/src/plugins/timelion/server/handlers/lib/tl_config.js b/src/plugins/vis_type_timelion/server/handlers/lib/tl_config.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/tl_config.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/tl_config.js
diff --git a/src/plugins/timelion/server/handlers/lib/validate_arg.js b/src/plugins/vis_type_timelion/server/handlers/lib/validate_arg.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/validate_arg.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/validate_arg.js
diff --git a/src/plugins/timelion/server/handlers/lib/validate_time.js b/src/plugins/vis_type_timelion/server/handlers/lib/validate_time.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/validate_time.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/validate_time.js
diff --git a/src/plugins/vis_type_timelion/server/index.ts b/src/plugins/vis_type_timelion/server/index.ts
new file mode 100644
index 000000000000..b40ab2af2b0d
--- /dev/null
+++ b/src/plugins/vis_type_timelion/server/index.ts
@@ -0,0 +1,39 @@
+/*
+ * 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 { PluginConfigDescriptor, PluginInitializerContext } from '../../../../src/core/server';
+import { configSchema, ConfigSchema } from '../config';
+import { Plugin } from './plugin';
+
+export { PluginSetupContract } from './plugin';
+
+export const config: PluginConfigDescriptor = {
+ schema: configSchema,
+ exposeToBrowser: {
+ ui: true,
+ },
+ deprecations: ({ renameFromRoot }) => [
+ renameFromRoot('timelion_vis.enabled', 'vis_type_timelion.enabled'),
+ renameFromRoot('timelion.enabled', 'vis_type_timelion.enabled'),
+ renameFromRoot('timelion.graphiteUrls', 'vis_type_timelion.graphiteUrls'),
+ renameFromRoot('timelion.ui.enabled', 'vis_type_timelion.ui.enabled'),
+ ],
+};
+export const plugin = (initializerContext: PluginInitializerContext) =>
+ new Plugin(initializerContext);
diff --git a/src/plugins/timelion/server/lib/alter.js b/src/plugins/vis_type_timelion/server/lib/alter.js
similarity index 100%
rename from src/plugins/timelion/server/lib/alter.js
rename to src/plugins/vis_type_timelion/server/lib/alter.js
diff --git a/src/plugins/timelion/server/lib/as_sorted.js b/src/plugins/vis_type_timelion/server/lib/as_sorted.js
similarity index 100%
rename from src/plugins/timelion/server/lib/as_sorted.js
rename to src/plugins/vis_type_timelion/server/lib/as_sorted.js
diff --git a/src/plugins/timelion/server/lib/build_target.js b/src/plugins/vis_type_timelion/server/lib/build_target.js
similarity index 100%
rename from src/plugins/timelion/server/lib/build_target.js
rename to src/plugins/vis_type_timelion/server/lib/build_target.js
diff --git a/src/plugins/timelion/server/lib/classes/chainable.js b/src/plugins/vis_type_timelion/server/lib/classes/chainable.js
similarity index 100%
rename from src/plugins/timelion/server/lib/classes/chainable.js
rename to src/plugins/vis_type_timelion/server/lib/classes/chainable.js
diff --git a/src/plugins/timelion/server/lib/classes/datasource.js b/src/plugins/vis_type_timelion/server/lib/classes/datasource.js
similarity index 100%
rename from src/plugins/timelion/server/lib/classes/datasource.js
rename to src/plugins/vis_type_timelion/server/lib/classes/datasource.js
diff --git a/src/plugins/timelion/server/lib/classes/timelion_function.d.ts b/src/plugins/vis_type_timelion/server/lib/classes/timelion_function.d.ts
similarity index 100%
rename from src/plugins/timelion/server/lib/classes/timelion_function.d.ts
rename to src/plugins/vis_type_timelion/server/lib/classes/timelion_function.d.ts
diff --git a/src/plugins/timelion/server/lib/classes/timelion_function.js b/src/plugins/vis_type_timelion/server/lib/classes/timelion_function.js
similarity index 100%
rename from src/plugins/timelion/server/lib/classes/timelion_function.js
rename to src/plugins/vis_type_timelion/server/lib/classes/timelion_function.js
diff --git a/src/plugins/timelion/server/lib/config_manager.ts b/src/plugins/vis_type_timelion/server/lib/config_manager.ts
similarity index 100%
rename from src/plugins/timelion/server/lib/config_manager.ts
rename to src/plugins/vis_type_timelion/server/lib/config_manager.ts
diff --git a/src/plugins/timelion/server/lib/functions_md.js b/src/plugins/vis_type_timelion/server/lib/functions_md.js
similarity index 100%
rename from src/plugins/timelion/server/lib/functions_md.js
rename to src/plugins/vis_type_timelion/server/lib/functions_md.js
diff --git a/src/plugins/timelion/server/lib/get_namespaced_settings.js b/src/plugins/vis_type_timelion/server/lib/get_namespaced_settings.js
similarity index 100%
rename from src/plugins/timelion/server/lib/get_namespaced_settings.js
rename to src/plugins/vis_type_timelion/server/lib/get_namespaced_settings.js
diff --git a/src/plugins/timelion/server/lib/load_functions.d.ts b/src/plugins/vis_type_timelion/server/lib/load_functions.d.ts
similarity index 100%
rename from src/plugins/timelion/server/lib/load_functions.d.ts
rename to src/plugins/vis_type_timelion/server/lib/load_functions.d.ts
diff --git a/src/plugins/timelion/server/lib/load_functions.js b/src/plugins/vis_type_timelion/server/lib/load_functions.js
similarity index 100%
rename from src/plugins/timelion/server/lib/load_functions.js
rename to src/plugins/vis_type_timelion/server/lib/load_functions.js
diff --git a/src/plugins/timelion/server/lib/load_functions.test.js b/src/plugins/vis_type_timelion/server/lib/load_functions.test.js
similarity index 94%
rename from src/plugins/timelion/server/lib/load_functions.test.js
rename to src/plugins/vis_type_timelion/server/lib/load_functions.test.js
index ebe1a04532e0..b4f83611a777 100644
--- a/src/plugins/timelion/server/lib/load_functions.test.js
+++ b/src/plugins/vis_type_timelion/server/lib/load_functions.test.js
@@ -17,7 +17,7 @@
* under the License.
*/
-const fn = require(`src/plugins/timelion/server/lib/load_functions`);
+const fn = require(`src/plugins/vis_type_timelion/server/lib/load_functions`);
const expect = require('chai').expect;
diff --git a/src/plugins/timelion/server/lib/offset_time.js b/src/plugins/vis_type_timelion/server/lib/offset_time.js
similarity index 100%
rename from src/plugins/timelion/server/lib/offset_time.js
rename to src/plugins/vis_type_timelion/server/lib/offset_time.js
diff --git a/src/plugins/timelion/server/lib/offset_time.test.js b/src/plugins/vis_type_timelion/server/lib/offset_time.test.js
similarity index 100%
rename from src/plugins/timelion/server/lib/offset_time.test.js
rename to src/plugins/vis_type_timelion/server/lib/offset_time.test.js
diff --git a/src/plugins/timelion/server/lib/process_function_definition.js b/src/plugins/vis_type_timelion/server/lib/process_function_definition.js
similarity index 100%
rename from src/plugins/timelion/server/lib/process_function_definition.js
rename to src/plugins/vis_type_timelion/server/lib/process_function_definition.js
diff --git a/src/plugins/timelion/server/lib/reduce.js b/src/plugins/vis_type_timelion/server/lib/reduce.js
similarity index 100%
rename from src/plugins/timelion/server/lib/reduce.js
rename to src/plugins/vis_type_timelion/server/lib/reduce.js
diff --git a/src/plugins/timelion/server/lib/split_interval.js b/src/plugins/vis_type_timelion/server/lib/split_interval.js
similarity index 100%
rename from src/plugins/timelion/server/lib/split_interval.js
rename to src/plugins/vis_type_timelion/server/lib/split_interval.js
diff --git a/src/plugins/timelion/server/lib/unzip_pairs.js b/src/plugins/vis_type_timelion/server/lib/unzip_pairs.js
similarity index 100%
rename from src/plugins/timelion/server/lib/unzip_pairs.js
rename to src/plugins/vis_type_timelion/server/lib/unzip_pairs.js
diff --git a/src/plugins/timelion/server/plugin.ts b/src/plugins/vis_type_timelion/server/plugin.ts
similarity index 100%
rename from src/plugins/timelion/server/plugin.ts
rename to src/plugins/vis_type_timelion/server/plugin.ts
diff --git a/src/plugins/timelion/server/routes/functions.ts b/src/plugins/vis_type_timelion/server/routes/functions.ts
similarity index 100%
rename from src/plugins/timelion/server/routes/functions.ts
rename to src/plugins/vis_type_timelion/server/routes/functions.ts
diff --git a/src/plugins/timelion/server/routes/run.ts b/src/plugins/vis_type_timelion/server/routes/run.ts
similarity index 100%
rename from src/plugins/timelion/server/routes/run.ts
rename to src/plugins/vis_type_timelion/server/routes/run.ts
diff --git a/src/plugins/timelion/server/routes/validate_es.ts b/src/plugins/vis_type_timelion/server/routes/validate_es.ts
similarity index 100%
rename from src/plugins/timelion/server/routes/validate_es.ts
rename to src/plugins/vis_type_timelion/server/routes/validate_es.ts
diff --git a/src/plugins/timelion/server/series_functions/abs.js b/src/plugins/vis_type_timelion/server/series_functions/abs.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/abs.js
rename to src/plugins/vis_type_timelion/server/series_functions/abs.js
diff --git a/src/plugins/timelion/server/series_functions/abs.test.js b/src/plugins/vis_type_timelion/server/series_functions/abs.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/abs.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/abs.test.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/aggregate.test.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/aggregate.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/aggregate.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/aggregate.test.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/avg.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/avg.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/avg.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/avg.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/cardinality.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/cardinality.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/cardinality.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/cardinality.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/first.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/first.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/first.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/first.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/index.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/index.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/index.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/index.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/last.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/last.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/last.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/last.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/max.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/max.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/max.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/max.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/min.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/min.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/min.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/min.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/sum.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/sum.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/sum.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/sum.js
diff --git a/src/plugins/timelion/server/series_functions/bars.js b/src/plugins/vis_type_timelion/server/series_functions/bars.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/bars.js
rename to src/plugins/vis_type_timelion/server/series_functions/bars.js
diff --git a/src/plugins/timelion/server/series_functions/bars.test.js b/src/plugins/vis_type_timelion/server/series_functions/bars.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/bars.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/bars.test.js
diff --git a/src/plugins/timelion/server/series_functions/color.js b/src/plugins/vis_type_timelion/server/series_functions/color.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/color.js
rename to src/plugins/vis_type_timelion/server/series_functions/color.js
diff --git a/src/plugins/timelion/server/series_functions/color.test.js b/src/plugins/vis_type_timelion/server/series_functions/color.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/color.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/color.test.js
diff --git a/src/plugins/timelion/server/series_functions/condition.js b/src/plugins/vis_type_timelion/server/series_functions/condition.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/condition.js
rename to src/plugins/vis_type_timelion/server/series_functions/condition.js
diff --git a/src/plugins/timelion/server/series_functions/condition.test.js b/src/plugins/vis_type_timelion/server/series_functions/condition.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/condition.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/condition.test.js
diff --git a/src/plugins/timelion/server/series_functions/cusum.js b/src/plugins/vis_type_timelion/server/series_functions/cusum.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/cusum.js
rename to src/plugins/vis_type_timelion/server/series_functions/cusum.js
diff --git a/src/plugins/timelion/server/series_functions/cusum.test.js b/src/plugins/vis_type_timelion/server/series_functions/cusum.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/cusum.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/cusum.test.js
diff --git a/src/plugins/timelion/server/series_functions/derivative.js b/src/plugins/vis_type_timelion/server/series_functions/derivative.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/derivative.js
rename to src/plugins/vis_type_timelion/server/series_functions/derivative.js
diff --git a/src/plugins/timelion/server/series_functions/derivative.test.js b/src/plugins/vis_type_timelion/server/series_functions/derivative.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/derivative.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/derivative.test.js
diff --git a/src/plugins/timelion/server/series_functions/divide.js b/src/plugins/vis_type_timelion/server/series_functions/divide.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/divide.js
rename to src/plugins/vis_type_timelion/server/series_functions/divide.js
diff --git a/src/plugins/timelion/server/series_functions/divide.test.js b/src/plugins/vis_type_timelion/server/series_functions/divide.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/divide.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/divide.test.js
diff --git a/src/plugins/timelion/server/series_functions/es/es.test.js b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/es.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/es.test.js
diff --git a/src/plugins/timelion/server/series_functions/es/index.js b/src/plugins/vis_type_timelion/server/series_functions/es/index.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/index.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/index.js
diff --git a/src/plugins/timelion/server/series_functions/es/lib/agg_body.js b/src/plugins/vis_type_timelion/server/series_functions/es/lib/agg_body.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/lib/agg_body.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/lib/agg_body.js
diff --git a/src/plugins/timelion/server/series_functions/es/lib/agg_response_to_series_list.js b/src/plugins/vis_type_timelion/server/series_functions/es/lib/agg_response_to_series_list.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/lib/agg_response_to_series_list.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/lib/agg_response_to_series_list.js
diff --git a/src/plugins/timelion/server/series_functions/es/lib/build_request.js b/src/plugins/vis_type_timelion/server/series_functions/es/lib/build_request.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/lib/build_request.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/lib/build_request.js
diff --git a/src/plugins/timelion/server/series_functions/es/lib/create_date_agg.js b/src/plugins/vis_type_timelion/server/series_functions/es/lib/create_date_agg.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/lib/create_date_agg.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/lib/create_date_agg.js
diff --git a/src/plugins/timelion/server/series_functions/first.js b/src/plugins/vis_type_timelion/server/series_functions/first.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/first.js
rename to src/plugins/vis_type_timelion/server/series_functions/first.js
diff --git a/src/plugins/timelion/server/series_functions/first.test.js b/src/plugins/vis_type_timelion/server/series_functions/first.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/first.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/first.test.js
diff --git a/src/plugins/timelion/server/series_functions/fit.js b/src/plugins/vis_type_timelion/server/series_functions/fit.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/fit.js
rename to src/plugins/vis_type_timelion/server/series_functions/fit.js
diff --git a/src/plugins/timelion/server/series_functions/fit.test.js b/src/plugins/vis_type_timelion/server/series_functions/fit.test.js
similarity index 98%
rename from src/plugins/timelion/server/series_functions/fit.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/fit.test.js
index 75eaa2a50ea7..6622259a1fd8 100644
--- a/src/plugins/timelion/server/series_functions/fit.test.js
+++ b/src/plugins/vis_type_timelion/server/series_functions/fit.test.js
@@ -17,7 +17,7 @@
* under the License.
*/
-const fn = require(`src/plugins/timelion/server/series_functions/fit`);
+const fn = require(`src/plugins/vis_type_timelion/server/series_functions/fit`);
import moment from 'moment';
const expect = require('chai').expect;
import invoke from './helpers/invoke_series_fn.js';
diff --git a/src/plugins/timelion/server/series_functions/fixtures/bucket_list.js b/src/plugins/vis_type_timelion/server/series_functions/fixtures/bucket_list.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/fixtures/bucket_list.js
rename to src/plugins/vis_type_timelion/server/series_functions/fixtures/bucket_list.js
diff --git a/src/plugins/timelion/server/series_functions/fixtures/es_response.js b/src/plugins/vis_type_timelion/server/series_functions/fixtures/es_response.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/fixtures/es_response.js
rename to src/plugins/vis_type_timelion/server/series_functions/fixtures/es_response.js
diff --git a/src/plugins/timelion/server/series_functions/fixtures/series_list.js b/src/plugins/vis_type_timelion/server/series_functions/fixtures/series_list.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/fixtures/series_list.js
rename to src/plugins/vis_type_timelion/server/series_functions/fixtures/series_list.js
diff --git a/src/plugins/timelion/server/series_functions/fixtures/tl_config.js b/src/plugins/vis_type_timelion/server/series_functions/fixtures/tl_config.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/fixtures/tl_config.js
rename to src/plugins/vis_type_timelion/server/series_functions/fixtures/tl_config.js
diff --git a/src/plugins/timelion/server/series_functions/graphite.js b/src/plugins/vis_type_timelion/server/series_functions/graphite.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/graphite.js
rename to src/plugins/vis_type_timelion/server/series_functions/graphite.js
diff --git a/src/plugins/timelion/server/series_functions/graphite.test.js b/src/plugins/vis_type_timelion/server/series_functions/graphite.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/graphite.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/graphite.test.js
diff --git a/src/plugins/timelion/server/series_functions/helpers/get_series.js b/src/plugins/vis_type_timelion/server/series_functions/helpers/get_series.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/helpers/get_series.js
rename to src/plugins/vis_type_timelion/server/series_functions/helpers/get_series.js
diff --git a/src/plugins/timelion/server/series_functions/helpers/get_series_list.js b/src/plugins/vis_type_timelion/server/series_functions/helpers/get_series_list.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/helpers/get_series_list.js
rename to src/plugins/vis_type_timelion/server/series_functions/helpers/get_series_list.js
diff --git a/src/plugins/timelion/server/series_functions/helpers/get_single_series_list.js b/src/plugins/vis_type_timelion/server/series_functions/helpers/get_single_series_list.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/helpers/get_single_series_list.js
rename to src/plugins/vis_type_timelion/server/series_functions/helpers/get_single_series_list.js
diff --git a/src/plugins/timelion/server/series_functions/helpers/invoke_series_fn.js b/src/plugins/vis_type_timelion/server/series_functions/helpers/invoke_series_fn.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/helpers/invoke_series_fn.js
rename to src/plugins/vis_type_timelion/server/series_functions/helpers/invoke_series_fn.js
diff --git a/src/plugins/timelion/server/series_functions/hide.js b/src/plugins/vis_type_timelion/server/series_functions/hide.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/hide.js
rename to src/plugins/vis_type_timelion/server/series_functions/hide.js
diff --git a/src/plugins/timelion/server/series_functions/hide.test.js b/src/plugins/vis_type_timelion/server/series_functions/hide.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/hide.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/hide.test.js
diff --git a/src/plugins/timelion/server/series_functions/holt/index.js b/src/plugins/vis_type_timelion/server/series_functions/holt/index.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/holt/index.js
rename to src/plugins/vis_type_timelion/server/series_functions/holt/index.js
diff --git a/src/plugins/timelion/server/series_functions/holt/lib/des.js b/src/plugins/vis_type_timelion/server/series_functions/holt/lib/des.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/holt/lib/des.js
rename to src/plugins/vis_type_timelion/server/series_functions/holt/lib/des.js
diff --git a/src/plugins/timelion/server/series_functions/holt/lib/ses.js b/src/plugins/vis_type_timelion/server/series_functions/holt/lib/ses.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/holt/lib/ses.js
rename to src/plugins/vis_type_timelion/server/series_functions/holt/lib/ses.js
diff --git a/src/plugins/timelion/server/series_functions/holt/lib/tes.js b/src/plugins/vis_type_timelion/server/series_functions/holt/lib/tes.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/holt/lib/tes.js
rename to src/plugins/vis_type_timelion/server/series_functions/holt/lib/tes.js
diff --git a/src/plugins/timelion/server/series_functions/label.js b/src/plugins/vis_type_timelion/server/series_functions/label.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/label.js
rename to src/plugins/vis_type_timelion/server/series_functions/label.js
diff --git a/src/plugins/timelion/server/series_functions/label.test.js b/src/plugins/vis_type_timelion/server/series_functions/label.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/label.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/label.test.js
diff --git a/src/plugins/timelion/server/series_functions/legend.js b/src/plugins/vis_type_timelion/server/series_functions/legend.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/legend.js
rename to src/plugins/vis_type_timelion/server/series_functions/legend.js
diff --git a/src/plugins/timelion/server/series_functions/legend.test.js b/src/plugins/vis_type_timelion/server/series_functions/legend.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/legend.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/legend.test.js
diff --git a/src/plugins/timelion/server/series_functions/lines.js b/src/plugins/vis_type_timelion/server/series_functions/lines.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/lines.js
rename to src/plugins/vis_type_timelion/server/series_functions/lines.js
diff --git a/src/plugins/timelion/server/series_functions/lines.test.js b/src/plugins/vis_type_timelion/server/series_functions/lines.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/lines.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/lines.test.js
diff --git a/src/plugins/timelion/server/series_functions/log.js b/src/plugins/vis_type_timelion/server/series_functions/log.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/log.js
rename to src/plugins/vis_type_timelion/server/series_functions/log.js
diff --git a/src/plugins/timelion/server/series_functions/log.test.js b/src/plugins/vis_type_timelion/server/series_functions/log.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/log.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/log.test.js
diff --git a/src/plugins/timelion/server/series_functions/max.js b/src/plugins/vis_type_timelion/server/series_functions/max.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/max.js
rename to src/plugins/vis_type_timelion/server/series_functions/max.js
diff --git a/src/plugins/timelion/server/series_functions/max.test.js b/src/plugins/vis_type_timelion/server/series_functions/max.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/max.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/max.test.js
diff --git a/src/plugins/timelion/server/series_functions/min.js b/src/plugins/vis_type_timelion/server/series_functions/min.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/min.js
rename to src/plugins/vis_type_timelion/server/series_functions/min.js
diff --git a/src/plugins/timelion/server/series_functions/min.test.js b/src/plugins/vis_type_timelion/server/series_functions/min.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/min.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/min.test.js
diff --git a/src/plugins/timelion/server/series_functions/movingaverage.js b/src/plugins/vis_type_timelion/server/series_functions/movingaverage.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/movingaverage.js
rename to src/plugins/vis_type_timelion/server/series_functions/movingaverage.js
diff --git a/src/plugins/timelion/server/series_functions/movingaverage.test.js b/src/plugins/vis_type_timelion/server/series_functions/movingaverage.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/movingaverage.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/movingaverage.test.js
diff --git a/src/plugins/timelion/server/series_functions/movingstd.js b/src/plugins/vis_type_timelion/server/series_functions/movingstd.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/movingstd.js
rename to src/plugins/vis_type_timelion/server/series_functions/movingstd.js
diff --git a/src/plugins/timelion/server/series_functions/movingstd.test.js b/src/plugins/vis_type_timelion/server/series_functions/movingstd.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/movingstd.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/movingstd.test.js
diff --git a/src/plugins/timelion/server/series_functions/multiply.js b/src/plugins/vis_type_timelion/server/series_functions/multiply.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/multiply.js
rename to src/plugins/vis_type_timelion/server/series_functions/multiply.js
diff --git a/src/plugins/timelion/server/series_functions/multiply.test.js b/src/plugins/vis_type_timelion/server/series_functions/multiply.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/multiply.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/multiply.test.js
diff --git a/src/plugins/timelion/server/series_functions/points.js b/src/plugins/vis_type_timelion/server/series_functions/points.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/points.js
rename to src/plugins/vis_type_timelion/server/series_functions/points.js
diff --git a/src/plugins/timelion/server/series_functions/points.test.js b/src/plugins/vis_type_timelion/server/series_functions/points.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/points.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/points.test.js
diff --git a/src/plugins/timelion/server/series_functions/precision.js b/src/plugins/vis_type_timelion/server/series_functions/precision.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/precision.js
rename to src/plugins/vis_type_timelion/server/series_functions/precision.js
diff --git a/src/plugins/timelion/server/series_functions/precision.test.js b/src/plugins/vis_type_timelion/server/series_functions/precision.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/precision.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/precision.test.js
diff --git a/src/plugins/timelion/server/series_functions/props.js b/src/plugins/vis_type_timelion/server/series_functions/props.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/props.js
rename to src/plugins/vis_type_timelion/server/series_functions/props.js
diff --git a/src/plugins/timelion/server/series_functions/quandl.js b/src/plugins/vis_type_timelion/server/series_functions/quandl.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/quandl.js
rename to src/plugins/vis_type_timelion/server/series_functions/quandl.js
diff --git a/src/plugins/timelion/server/series_functions/quandl.test.js b/src/plugins/vis_type_timelion/server/series_functions/quandl.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/quandl.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/quandl.test.js
diff --git a/src/plugins/timelion/server/series_functions/range.js b/src/plugins/vis_type_timelion/server/series_functions/range.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/range.js
rename to src/plugins/vis_type_timelion/server/series_functions/range.js
diff --git a/src/plugins/timelion/server/series_functions/range.test.js b/src/plugins/vis_type_timelion/server/series_functions/range.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/range.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/range.test.js
diff --git a/src/plugins/timelion/server/series_functions/scale_interval.js b/src/plugins/vis_type_timelion/server/series_functions/scale_interval.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/scale_interval.js
rename to src/plugins/vis_type_timelion/server/series_functions/scale_interval.js
diff --git a/src/plugins/timelion/server/series_functions/scale_interval.test.js b/src/plugins/vis_type_timelion/server/series_functions/scale_interval.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/scale_interval.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/scale_interval.test.js
diff --git a/src/plugins/timelion/server/series_functions/static.js b/src/plugins/vis_type_timelion/server/series_functions/static.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/static.js
rename to src/plugins/vis_type_timelion/server/series_functions/static.js
diff --git a/src/plugins/timelion/server/series_functions/static.test.js b/src/plugins/vis_type_timelion/server/series_functions/static.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/static.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/static.test.js
diff --git a/src/plugins/timelion/server/series_functions/subtract.js b/src/plugins/vis_type_timelion/server/series_functions/subtract.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/subtract.js
rename to src/plugins/vis_type_timelion/server/series_functions/subtract.js
diff --git a/src/plugins/timelion/server/series_functions/subtract.test.js b/src/plugins/vis_type_timelion/server/series_functions/subtract.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/subtract.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/subtract.test.js
diff --git a/src/plugins/timelion/server/series_functions/sum.js b/src/plugins/vis_type_timelion/server/series_functions/sum.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/sum.js
rename to src/plugins/vis_type_timelion/server/series_functions/sum.js
diff --git a/src/plugins/timelion/server/series_functions/sum.test.js b/src/plugins/vis_type_timelion/server/series_functions/sum.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/sum.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/sum.test.js
diff --git a/src/plugins/timelion/server/series_functions/title.js b/src/plugins/vis_type_timelion/server/series_functions/title.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/title.js
rename to src/plugins/vis_type_timelion/server/series_functions/title.js
diff --git a/src/plugins/timelion/server/series_functions/title.test.js b/src/plugins/vis_type_timelion/server/series_functions/title.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/title.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/title.test.js
diff --git a/src/plugins/timelion/server/series_functions/trend/index.js b/src/plugins/vis_type_timelion/server/series_functions/trend/index.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/trend/index.js
rename to src/plugins/vis_type_timelion/server/series_functions/trend/index.js
diff --git a/src/plugins/timelion/server/series_functions/trend/lib/regress.js b/src/plugins/vis_type_timelion/server/series_functions/trend/lib/regress.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/trend/lib/regress.js
rename to src/plugins/vis_type_timelion/server/series_functions/trend/lib/regress.js
diff --git a/src/plugins/timelion/server/series_functions/trim.js b/src/plugins/vis_type_timelion/server/series_functions/trim.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/trim.js
rename to src/plugins/vis_type_timelion/server/series_functions/trim.js
diff --git a/src/plugins/timelion/server/series_functions/trim.test.js b/src/plugins/vis_type_timelion/server/series_functions/trim.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/trim.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/trim.test.js
diff --git a/src/plugins/timelion/server/series_functions/worldbank.js b/src/plugins/vis_type_timelion/server/series_functions/worldbank.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/worldbank.js
rename to src/plugins/vis_type_timelion/server/series_functions/worldbank.js
diff --git a/src/plugins/timelion/server/series_functions/worldbank_indicators.js b/src/plugins/vis_type_timelion/server/series_functions/worldbank_indicators.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/worldbank_indicators.js
rename to src/plugins/vis_type_timelion/server/series_functions/worldbank_indicators.js
diff --git a/src/plugins/timelion/server/series_functions/yaxis.js b/src/plugins/vis_type_timelion/server/series_functions/yaxis.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/yaxis.js
rename to src/plugins/vis_type_timelion/server/series_functions/yaxis.js
diff --git a/src/plugins/timelion/server/series_functions/yaxis.test.js b/src/plugins/vis_type_timelion/server/series_functions/yaxis.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/yaxis.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/yaxis.test.js
diff --git a/src/plugins/timelion/server/timelion.json b/src/plugins/vis_type_timelion/server/timelion.json
similarity index 100%
rename from src/plugins/timelion/server/timelion.json
rename to src/plugins/vis_type_timelion/server/timelion.json
diff --git a/src/plugins/timelion/server/types.ts b/src/plugins/vis_type_timelion/server/types.ts
similarity index 100%
rename from src/plugins/timelion/server/types.ts
rename to src/plugins/vis_type_timelion/server/types.ts
diff --git a/src/plugins/vis_type_timeseries/kibana.json b/src/plugins/vis_type_timeseries/kibana.json
index 305e159ac250..38662c6a7ff8 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 000000000000..f4668eff8fa0
--- /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 fa74b6e96597..f460257caf5e 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 6ef6362c6e37..05257cb79a75 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 77b49e824334..f18fa1e4cc2f 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/kibana.json b/src/plugins/visualizations/kibana.json
index cd22b1375ae1..f3f9cbd8341e 100644
--- a/src/plugins/visualizations/kibana.json
+++ b/src/plugins/visualizations/kibana.json
@@ -3,5 +3,5 @@
"version": "kibana",
"server": true,
"ui": true,
- "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "usageCollection"]
+ "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "usageCollection", "inspector"]
}
diff --git a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts
index bf2d174f594b..8e51bd4ac5d4 100644
--- a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts
+++ b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts
@@ -28,8 +28,9 @@ import {
getTimeFilter,
getCapabilities,
} from '../services';
+import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory';
-export const createVisEmbeddableFromObject = async (
+export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDeps) => async (
vis: Vis,
input: Partial & { id: string },
parent?: IContainer
@@ -58,6 +59,7 @@ export const createVisEmbeddableFromObject = async (
indexPatterns,
editUrl,
editable,
+ deps,
},
input,
parent
diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
index e64d20025179..ffb028ff131b 100644
--- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -42,6 +42,7 @@ import { buildPipeline } from '../legacy/build_pipeline';
import { Vis } from '../vis';
import { getExpressions, getUiActions } from '../services';
import { VIS_EVENT_TO_TRIGGER } from './events';
+import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory';
const getKeys = (o: T): Array => Object.keys(o) as Array;
@@ -50,6 +51,7 @@ export interface VisualizeEmbeddableConfiguration {
indexPatterns?: IIndexPattern[];
editUrl: string;
editable: boolean;
+ deps: VisualizeEmbeddableFactoryDeps;
}
export interface VisualizeInput extends EmbeddableInput {
@@ -84,10 +86,11 @@ export class VisualizeEmbeddable extends Embeddable {
- if (this.handler) {
- return this.handler.openInspector(this.getTitle() || '');
- }
+ if (!this.handler) return;
+
+ const adapters = this.handler.inspect();
+ if (!adapters) return;
+
+ this.deps.start().plugins.inspector.open(adapters, {
+ title: this.getTitle() || '',
+ });
};
/**
diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
index 4b7d01ae3b24..6ab1c9864598 100644
--- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
+++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
@@ -25,7 +25,7 @@ import {
EmbeddableOutput,
ErrorEmbeddable,
IContainer,
-} from '../../../../plugins/embeddable/public';
+} from '../../../embeddable/public';
import { DisabledLabEmbeddable } from './disabled_lab_embeddable';
import { VisualizeEmbeddable, VisualizeInput, VisualizeOutput } from './visualize_embeddable';
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
@@ -39,11 +39,17 @@ import {
import { showNewVisModal } from '../wizard';
import { convertToSerializedVis } from '../saved_visualizations/_saved_vis';
import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object';
+import { StartServicesGetter } from '../../../kibana_utils/public';
+import { VisualizationsStartDeps } from '../plugin';
interface VisualizationAttributes extends SavedObjectAttributes {
visState: string;
}
+export interface VisualizeEmbeddableFactoryDeps {
+ start: StartServicesGetter>;
+}
+
export class VisualizeEmbeddableFactory
implements
EmbeddableFactoryDefinition<
@@ -79,7 +85,8 @@ export class VisualizeEmbeddableFactory
return visType.stage !== 'experimental';
},
};
- constructor() {}
+
+ constructor(private readonly deps: VisualizeEmbeddableFactoryDeps) {}
public async isEditable() {
return getCapabilities().visualize.save as boolean;
@@ -101,7 +108,7 @@ export class VisualizeEmbeddableFactory
try {
const savedObject = await savedVisualizations.get(savedObjectId);
const vis = new Vis(savedObject.visState.type, await convertToSerializedVis(savedObject));
- return createVisEmbeddableFromObject(vis, input, parent);
+ return createVisEmbeddableFromObject(this.deps)(vis, input, parent);
} catch (e) {
console.error(e); // eslint-disable-line no-console
return new ErrorEmbeddable(e, input, parent);
diff --git a/src/plugins/visualizations/public/index.scss b/src/plugins/visualizations/public/index.scss
index eada763b63c4..2b61535f3e7f 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/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts
index 2aa346423297..d6eeffdb0145 100644
--- a/src/plugins/visualizations/public/mocks.ts
+++ b/src/plugins/visualizations/public/mocks.ts
@@ -26,6 +26,7 @@ import { expressionsPluginMock } from '../../../plugins/expressions/public/mocks
import { dataPluginMock } from '../../../plugins/data/public/mocks';
import { usageCollectionPluginMock } from '../../../plugins/usage_collection/public/mocks';
import { uiActionsPluginMock } from '../../../plugins/ui_actions/public/mocks';
+import { inspectorPluginMock } from '../../../plugins/inspector/public/mocks';
const createSetupContract = (): VisualizationsSetup => ({
createBaseVisualization: jest.fn(),
@@ -53,14 +54,16 @@ const createInstance = async () => {
const setup = plugin.setup(coreMock.createSetup(), {
data: dataPluginMock.createSetupContract(),
- expressions: expressionsPluginMock.createSetupContract(),
embeddable: embeddablePluginMock.createSetupContract(),
+ expressions: expressionsPluginMock.createSetupContract(),
+ inspector: inspectorPluginMock.createSetupContract(),
usageCollection: usageCollectionPluginMock.createSetupContract(),
});
const doStart = () =>
plugin.start(coreMock.createStart(), {
data: dataPluginMock.createStartContract(),
expressions: expressionsPluginMock.createStartContract(),
+ inspector: inspectorPluginMock.createStartContract(),
uiActions: uiActionsPluginMock.createStartContract(),
});
diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts
index 8fcb84b19a9b..b3e8c9b5b61b 100644
--- a/src/plugins/visualizations/public/plugin.ts
+++ b/src/plugins/visualizations/public/plugin.ts
@@ -43,18 +43,23 @@ import {
VisualizeEmbeddableFactory,
createVisEmbeddableFromObject,
} from './embeddable';
-import { ExpressionsSetup, ExpressionsStart } from '../../../plugins/expressions/public';
-import { EmbeddableSetup } from '../../../plugins/embeddable/public';
+import { ExpressionsSetup, ExpressionsStart } from '../../expressions/public';
+import { EmbeddableSetup } from '../../embeddable/public';
import { visualization as visualizationFunction } from './expressions/visualization_function';
import { visualization as visualizationRenderer } from './expressions/visualization_renderer';
import { range as rangeExpressionFunction } from './expression_functions/range';
import { visDimension as visDimensionExpressionFunction } from './expression_functions/vis_dimension';
import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../plugins/data/public';
-import { UsageCollectionSetup } from '../../../plugins/usage_collection/public';
+import {
+ Setup as InspectorSetup,
+ Start as InspectorStart,
+} from '../../../plugins/inspector/public';
+import { UsageCollectionSetup } from '../../usage_collection/public';
+import { createStartServicesGetter, StartServicesGetter } from '../../kibana_utils/public';
import { createSavedVisLoader, SavedVisualizationsLoader } from './saved_visualizations';
import { SerializedVis, Vis } from './vis';
import { showNewVisModal } from './wizard';
-import { UiActionsStart } from '../../../plugins/ui_actions/public';
+import { UiActionsStart } from '../../ui_actions/public';
import {
convertFromSerializedVis,
convertToSerializedVis,
@@ -74,19 +79,21 @@ export interface VisualizationsStart extends TypesStart {
convertToSerializedVis: typeof convertToSerializedVis;
convertFromSerializedVis: typeof convertFromSerializedVis;
showNewVisModal: typeof showNewVisModal;
- __LEGACY: { createVisEmbeddableFromObject: typeof createVisEmbeddableFromObject };
+ __LEGACY: { createVisEmbeddableFromObject: ReturnType };
}
export interface VisualizationsSetupDeps {
- expressions: ExpressionsSetup;
+ data: DataPublicPluginSetup;
embeddable: EmbeddableSetup;
+ expressions: ExpressionsSetup;
+ inspector: InspectorSetup;
usageCollection: UsageCollectionSetup;
- data: DataPublicPluginSetup;
}
export interface VisualizationsStartDeps {
data: DataPublicPluginStart;
expressions: ExpressionsStart;
+ inspector: InspectorStart;
uiActions: UiActionsStart;
}
@@ -107,13 +114,16 @@ export class VisualizationsPlugin
VisualizationsStartDeps
> {
private readonly types: TypesService = new TypesService();
+ private getStartServicesOrDie?: StartServicesGetter;
constructor(initializerContext: PluginInitializerContext) {}
public setup(
- core: CoreSetup,
+ core: CoreSetup,
{ expressions, embeddable, usageCollection, data }: VisualizationsSetupDeps
): VisualizationsSetup {
+ const start = (this.getStartServicesOrDie = createStartServicesGetter(core.getStartServices));
+
setUISettings(core.uiSettings);
setUsageCollector(usageCollection);
@@ -122,7 +132,7 @@ export class VisualizationsPlugin
expressions.registerFunction(rangeExpressionFunction);
expressions.registerFunction(visDimensionExpressionFunction);
- const embeddableFactory = new VisualizeEmbeddableFactory();
+ const embeddableFactory = new VisualizeEmbeddableFactory({ start });
embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory);
return {
@@ -171,7 +181,11 @@ export class VisualizationsPlugin
convertToSerializedVis,
convertFromSerializedVis,
savedVisualizationsLoader,
- __LEGACY: { createVisEmbeddableFromObject },
+ __LEGACY: {
+ createVisEmbeddableFromObject: createVisEmbeddableFromObject({
+ start: this.getStartServicesOrDie!,
+ }),
+ },
};
}
diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts
index 9f4782f3ec73..cd2211c18553 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 000000000000..a7afa0697a5e
--- /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 241397884c8f..9d8a1b98ef02 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 b6a63d50b205..972bdc1462b2 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 0dcacd30fba4..a031d70ef9a8 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 8384585108a5..d071df314d99 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 e6974af9af83..09609e3d7e36 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 c7937c856184..84e1ef9687cd 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 86f39ea76dd3..fe2a19b7315c 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 ef174dbaa586..874b69532ec1 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 9c00947d7663..c297326f2e26 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 a710d3e31874..7c5e3ce9408f 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 098633d04606..900c17fa394d 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 932ac8996e97..100becdd3a80 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 d5440c4677d8..765e9a82ae89 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 4ffbc307c69a..ab64e083a553 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/tasks/config/peg.js b/tasks/config/peg.js
index 1d8667840fab..2de6ff4b5cff 100644
--- a/tasks/config/peg.js
+++ b/tasks/config/peg.js
@@ -26,7 +26,7 @@ module.exports = {
},
},
timelion_chain: {
- src: 'src/legacy/core_plugins/vis_type_timelion/public/chain.peg',
- dest: 'src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js',
+ src: 'src/plugins/vis_type_timelion/public/chain.peg',
+ dest: 'src/plugins/vis_type_timelion/public/_generated_/chain.js',
},
};
diff --git a/test/api_integration/apis/saved_objects/migrations.js b/test/api_integration/apis/saved_objects/migrations.js
index bd207ccb41b2..9d908b95ca57 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/examples/embeddables/adding_children.ts b/test/examples/embeddables/adding_children.ts
index 5fe88b5dd33f..9ec4b6cffd31 100644
--- a/test/examples/embeddables/adding_children.ts
+++ b/test/examples/embeddables/adding_children.ts
@@ -25,7 +25,8 @@ export default function({ getService }: PluginFunctionalProviderContext) {
const testSubjects = getService('testSubjects');
const flyout = getService('flyout');
- describe('creating and adding children', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/58692
+ describe.skip('creating and adding children', () => {
before(async () => {
await testSubjects.click('embeddablePanelExamplae');
});
diff --git a/test/functional/apps/dashboard/create_and_add_embeddables.js b/test/functional/apps/dashboard/create_and_add_embeddables.js
index 3ce8e353e61f..410acdcb5680 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/dashboard_back_button.ts b/test/functional/apps/dashboard/dashboard_back_button.ts
new file mode 100644
index 000000000000..8a488c1780fc
--- /dev/null
+++ b/test/functional/apps/dashboard/dashboard_back_button.ts
@@ -0,0 +1,47 @@
+/*
+ * 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 expect from '@kbn/expect';
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function({ getService, getPageObjects }: FtrProviderContext) {
+ const esArchiver = getService('esArchiver');
+ const kibanaServer = getService('kibanaServer');
+ const PageObjects = getPageObjects(['dashboard', 'header', 'common', 'visualize', 'timePicker']);
+ const browser = getService('browser');
+
+ describe('dashboard back button', () => {
+ before(async () => {
+ await esArchiver.loadIfNeeded('dashboard/current/kibana');
+ await kibanaServer.uiSettings.replace({
+ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c',
+ });
+ await PageObjects.common.navigateToApp('dashboard');
+ await PageObjects.dashboard.preserveCrossAppState();
+ });
+
+ it('after navigation from listing page to dashboard back button works', async () => {
+ await PageObjects.dashboard.gotoDashboardLandingPage();
+ await PageObjects.dashboard.loadSavedDashboard('dashboard with everything');
+ await PageObjects.dashboard.waitForRenderComplete();
+ await browser.goBack();
+ expect(await PageObjects.dashboard.onDashboardLandingPage()).to.be(true);
+ });
+ });
+}
diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js
index 5e96a55b1901..6666ccc57d58 100644
--- a/test/functional/apps/dashboard/index.js
+++ b/test/functional/apps/dashboard/index.js
@@ -55,6 +55,7 @@ export default function({ getService, loadTestFile }) {
loadTestFile(require.resolve('./dashboard_options'));
loadTestFile(require.resolve('./data_shared_attributes'));
loadTestFile(require.resolve('./embed_mode'));
+ loadTestFile(require.resolve('./dashboard_back_button'));
// Note: This one must be last because it unloads some data for one of its tests!
// No, this isn't ideal, but loading/unloading takes so much time and these are all bunched
diff --git a/test/functional/apps/dashboard/panel_controls.js b/test/functional/apps/dashboard/panel_controls.js
index f30f58913bd9..6e24b9f3570a 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');
@@ -113,6 +113,50 @@ export default function({ getService, getPageObjects }) {
});
});
+ describe('panel cloning', function() {
+ before(async () => {
+ await PageObjects.dashboard.clickNewDashboard();
+ await PageObjects.timePicker.setHistoricalDataRange();
+ await dashboardAddPanel.addVisualization(PIE_CHART_VIS_NAME);
+ });
+
+ after(async function() {
+ await PageObjects.dashboard.gotoDashboardLandingPage();
+ });
+
+ it('clones a panel', async () => {
+ const initialPanelTitles = await PageObjects.dashboard.getPanelTitles();
+ await dashboardPanelActions.clonePanelByTitle(PIE_CHART_VIS_NAME);
+ await PageObjects.header.waitUntilLoadingHasFinished();
+ await PageObjects.dashboard.waitForRenderComplete();
+ const postPanelTitles = await PageObjects.dashboard.getPanelTitles();
+ expect(postPanelTitles.length).to.equal(initialPanelTitles.length + 1);
+ });
+
+ it('appends a clone title tag', async () => {
+ const panelTitles = await PageObjects.dashboard.getPanelTitles();
+ expect(panelTitles[1]).to.equal(PIE_CHART_VIS_NAME + ' (copy)');
+ });
+
+ it('retains original panel dimensions', async () => {
+ const panelDimensions = await PageObjects.dashboard.getPanelDimensions();
+ expect(panelDimensions[0]).to.eql(panelDimensions[1]);
+ });
+
+ it('gives a correct title to the clone of a clone', async () => {
+ const initialPanelTitles = await PageObjects.dashboard.getPanelTitles();
+ const clonedPanelName = initialPanelTitles[initialPanelTitles.length - 1];
+ await dashboardPanelActions.clonePanelByTitle(clonedPanelName);
+ await PageObjects.header.waitUntilLoadingHasFinished();
+ await PageObjects.dashboard.waitForRenderComplete();
+ const postPanelTitles = await PageObjects.dashboard.getPanelTitles();
+ expect(postPanelTitles.length).to.equal(initialPanelTitles.length + 1);
+ expect(postPanelTitles[postPanelTitles.length - 1]).to.equal(
+ PIE_CHART_VIS_NAME + ' (copy 1)'
+ );
+ });
+ });
+
describe('panel edit controls', function() {
before(async () => {
await PageObjects.dashboard.clickNewDashboard();
@@ -137,6 +181,7 @@ export default function({ getService, getPageObjects }) {
await dashboardPanelActions.expectExistsEditPanelAction();
await dashboardPanelActions.expectExistsReplacePanelAction();
+ await dashboardPanelActions.expectExistsDuplicatePanelAction();
await dashboardPanelActions.expectExistsRemovePanelAction();
});
@@ -151,6 +196,7 @@ export default function({ getService, getPageObjects }) {
await dashboardPanelActions.openContextMenu();
await dashboardPanelActions.expectExistsEditPanelAction();
await dashboardPanelActions.expectExistsReplacePanelAction();
+ await dashboardPanelActions.expectExistsDuplicatePanelAction();
await dashboardPanelActions.expectExistsRemovePanelAction();
// Get rid of the timestamp in the url.
@@ -166,6 +212,7 @@ export default function({ getService, getPageObjects }) {
await dashboardPanelActions.openContextMenu();
await dashboardPanelActions.expectMissingEditPanelAction();
await dashboardPanelActions.expectMissingReplacePanelAction();
+ await dashboardPanelActions.expectMissingDuplicatePanelAction();
await dashboardPanelActions.expectMissingRemovePanelAction();
});
@@ -174,6 +221,7 @@ export default function({ getService, getPageObjects }) {
await dashboardPanelActions.openContextMenu();
await dashboardPanelActions.expectExistsEditPanelAction();
await dashboardPanelActions.expectExistsReplacePanelAction();
+ await dashboardPanelActions.expectExistsDuplicatePanelAction();
await dashboardPanelActions.expectMissingRemovePanelAction();
await dashboardPanelActions.clickExpandPanelToggle();
});
diff --git a/test/functional/config.edge.js b/test/functional/config.edge.js
new file mode 100644
index 000000000000..ed68b41e8c89
--- /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 3b63fa68d71e..220c2d8f6b36 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 5017947e95d0..13d2365c0719 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/dashboard/panel_actions.js b/test/functional/services/dashboard/panel_actions.js
index baea2a52208c..b155d747f3b9 100644
--- a/test/functional/services/dashboard/panel_actions.js
+++ b/test/functional/services/dashboard/panel_actions.js
@@ -20,6 +20,7 @@
const REMOVE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-deletePanel';
const EDIT_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-editPanel';
const REPLACE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-replacePanel';
+const CLONE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-clonePanel';
const TOGGLE_EXPAND_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-togglePanel';
const CUSTOMIZE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-ACTION_CUSTOMIZE_PANEL';
const OPEN_CONTEXT_MENU_ICON_DATA_TEST_SUBJ = 'embeddablePanelToggleMenuIcon';
@@ -97,6 +98,16 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) {
await testSubjects.click(REPLACE_PANEL_DATA_TEST_SUBJ);
}
+ async clonePanelByTitle(title) {
+ log.debug(`clonePanel(${title})`);
+ let panelOptions = null;
+ if (title) {
+ panelOptions = await this.getPanelHeading(title);
+ }
+ await this.openContextMenu(panelOptions);
+ await testSubjects.click(CLONE_PANEL_DATA_TEST_SUBJ);
+ }
+
async openInspectorByTitle(title) {
const header = await this.getPanelHeading(title);
await this.openInspector(header);
@@ -123,7 +134,12 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) {
}
async expectExistsReplacePanelAction() {
- log.debug('expectExistsEditPanelAction');
+ log.debug('expectExistsReplacePanelAction');
+ await testSubjects.existOrFail(REPLACE_PANEL_DATA_TEST_SUBJ);
+ }
+
+ async expectExistsDuplicatePanelAction() {
+ log.debug('expectExistsDuplicatePanelAction');
await testSubjects.existOrFail(REPLACE_PANEL_DATA_TEST_SUBJ);
}
@@ -133,7 +149,12 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) {
}
async expectMissingReplacePanelAction() {
- log.debug('expectMissingEditPanelAction');
+ log.debug('expectMissingReplacePanelAction');
+ await testSubjects.missingOrFail(REPLACE_PANEL_DATA_TEST_SUBJ);
+ }
+
+ async expectMissingDuplicatePanelAction() {
+ log.debug('expectMissingDuplicatePanelAction');
await testSubjects.missingOrFail(REPLACE_PANEL_DATA_TEST_SUBJ);
}
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 157918df874c..8b57ecd3c823 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 46d81f1737a5..aa6e364d0a09 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 e571a1a7e555..933b08f7681e 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 3bf5b865aa7b..1b7ef2c1855d 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/kbn_tp_embeddable_explorer/tsconfig.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/tsconfig.json
index d8096d9aab27..544c27241f5c 100644
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/tsconfig.json
+++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/tsconfig.json
@@ -6,7 +6,8 @@
"types": [
"node",
"jest",
- "react"
+ "react",
+ "flot"
]
},
"include": [
@@ -16,4 +17,4 @@
"../../../../typings/**/*",
],
"exclude": []
-}
\ No newline at end of file
+}
diff --git a/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts
index 3f6a8e8773e0..f0b1cde24c6f 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 23807a6e98dc..c77ffe40db55 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 5055997df642..67d88b308ed9 100755
--- a/test/scripts/jenkins_xpack.sh
+++ b/test/scripts/jenkins_xpack.sh
@@ -17,7 +17,7 @@ if [[ -z "$CODE_COVERAGE" ]] ; then
echo " -> Running SIEM cyclic dependency test"
cd "$XPACK_DIR"
- checks-reporter-with-killswitch "X-Pack SIEM cyclic dependency test" node legacy/plugins/siem/scripts/check_circular_deps
+ checks-reporter-with-killswitch "X-Pack SIEM cyclic dependency test" node plugins/siem/scripts/check_circular_deps
echo ""
echo ""
@@ -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 01b13293c10b..a6e600630364 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/test/tsconfig.json b/test/tsconfig.json
index 285d3db64a87..5a3716e620fe 100644
--- a/test/tsconfig.json
+++ b/test/tsconfig.json
@@ -3,7 +3,8 @@
"compilerOptions": {
"types": [
"node",
- "mocha"
+ "mocha",
+ "flot"
],
"lib": [
"esnext",
diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json
index 50f36ddd21c9..c8715ac3447b 100644
--- a/x-pack/.i18nrc.json
+++ b/x-pack/.i18nrc.json
@@ -32,11 +32,11 @@
"xpack.remoteClusters": "plugins/remote_clusters",
"xpack.painlessLab": "plugins/painless_lab",
"xpack.reporting": ["plugins/reporting", "legacy/plugins/reporting"],
- "xpack.rollupJobs": "legacy/plugins/rollup",
+ "xpack.rollupJobs": ["legacy/plugins/rollup", "plugins/rollup"],
"xpack.searchProfiler": "plugins/searchprofiler",
"xpack.security": ["legacy/plugins/security", "plugins/security"],
"xpack.server": "legacy/server",
- "xpack.siem": "legacy/plugins/siem",
+ "xpack.siem": ["plugins/siem", "legacy/plugins/siem"],
"xpack.snapshotRestore": "plugins/snapshot_restore",
"xpack.spaces": ["legacy/plugins/spaces", "plugins/spaces"],
"xpack.taskManager": "legacy/plugins/task_manager",
diff --git a/x-pack/examples/README.md b/x-pack/examples/README.md
new file mode 100644
index 000000000000..babf744f9777
--- /dev/null
+++ b/x-pack/examples/README.md
@@ -0,0 +1,7 @@
+## Example plugins
+
+This folder contains X-Pack example plugins. To run the plugins in this folder, use the `--run-examples` flag, via
+
+```
+yarn start --run-examples
+```
diff --git a/x-pack/examples/ui_actions_enhanced_examples/README.md b/x-pack/examples/ui_actions_enhanced_examples/README.md
new file mode 100644
index 000000000000..c9f53137d868
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/README.md
@@ -0,0 +1,3 @@
+## Ui actions enhanced examples
+
+To run this example, use the command `yarn start --run-examples`.
diff --git a/x-pack/examples/ui_actions_enhanced_examples/kibana.json b/x-pack/examples/ui_actions_enhanced_examples/kibana.json
new file mode 100644
index 000000000000..f75852edced5
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/kibana.json
@@ -0,0 +1,10 @@
+{
+ "id": "uiActionsEnhancedExamples",
+ "version": "0.0.1",
+ "kibanaVersion": "kibana",
+ "configPath": ["ui_actions_enhanced_examples"],
+ "server": false,
+ "ui": true,
+ "requiredPlugins": ["uiActions", "data"],
+ "optionalPlugins": []
+}
diff --git a/x-pack/examples/ui_actions_enhanced_examples/package.json b/x-pack/examples/ui_actions_enhanced_examples/package.json
new file mode 100644
index 000000000000..a9f004b075ce
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "ui_actions_enhanced_examples",
+ "version": "1.0.0",
+ "main": "target/examples/ui_actions_enhanced_examples",
+ "kibana": {
+ "version": "kibana",
+ "templateVersion": "1.0.0"
+ },
+ "license": "Apache-2.0",
+ "scripts": {
+ "kbn": "node ../../scripts/kbn.js",
+ "build": "rm -rf './target' && tsc"
+ },
+ "devDependencies": {
+ "typescript": "3.7.2"
+ }
+}
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/index.ts b/x-pack/examples/ui_actions_enhanced_examples/public/index.ts
new file mode 100644
index 000000000000..7f3f36089d57
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/index.ts
@@ -0,0 +1,9 @@
+/*
+ * 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 { UiActionsEnhancedExamplesPlugin } from './plugin';
+
+export const plugin = () => new UiActionsEnhancedExamplesPlugin();
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/plugin.ts b/x-pack/examples/ui_actions_enhanced_examples/public/plugin.ts
new file mode 100644
index 000000000000..a4c43753c824
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/plugin.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 { Plugin, CoreSetup, CoreStart } from '../../../../src/core/public';
+import { UiActionsSetup, UiActionsStart } from '../../../../src/plugins/ui_actions/public';
+import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public';
+
+export interface SetupDependencies {
+ data: DataPublicPluginSetup;
+ uiActions: UiActionsSetup;
+}
+
+export interface StartDependencies {
+ data: DataPublicPluginStart;
+ uiActions: UiActionsStart;
+}
+
+export class UiActionsEnhancedExamplesPlugin
+ implements Plugin {
+ public setup(core: CoreSetup, plugins: SetupDependencies) {
+ // eslint-disable-next-line
+ console.log('ui_actions_enhanced_examples');
+ }
+
+ public start(core: CoreStart, plugins: StartDependencies) {}
+
+ public stop() {}
+}
diff --git a/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json b/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json
new file mode 100644
index 000000000000..d508076b3319
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./target",
+ "skipLibCheck": true
+ },
+ "include": [
+ "index.ts",
+ "public/**/*.ts",
+ "public/**/*.tsx",
+ "server/**/*.ts",
+ "../../typings/**/*",
+ ],
+ "exclude": []
+}
diff --git a/x-pack/index.js b/x-pack/index.js
index 61fd4f175231..7fbd992120ea 100644
--- a/x-pack/index.js
+++ b/x-pack/index.js
@@ -9,7 +9,6 @@ import { graph } from './legacy/plugins/graph';
import { monitoring } from './legacy/plugins/monitoring';
import { reporting } from './legacy/plugins/reporting';
import { security } from './legacy/plugins/security';
-import { tilemap } from './legacy/plugins/tilemap';
import { dashboardMode } from './legacy/plugins/dashboard_mode';
import { logstash } from './legacy/plugins/logstash';
import { beats } from './legacy/plugins/beats_management';
@@ -40,7 +39,6 @@ module.exports = function(kibana) {
reporting(kibana),
spaces(kibana),
security(kibana),
- tilemap(kibana),
dashboardMode(kibana),
logstash(kibana),
beats(kibana),
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 a1462c763735..cc5c62e25b49 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/components/app/ServiceMap/Cytoscape.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx
index e4b656ae8160..53c86f92ee55 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx
@@ -211,7 +211,9 @@ export function Cytoscape({
resetConnectedEdgeStyle(event.target);
};
const unselectHandler: cytoscape.EventHandler = event => {
- resetConnectedEdgeStyle();
+ resetConnectedEdgeStyle(
+ serviceName ? event.cy.getElementById(serviceName) : undefined
+ );
};
const debugHandler: cytoscape.EventHandler = event => {
const debugEnabled = sessionStorage.getItem('apm_debug') === 'true';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts
index 416eb879d597..d844ac8b5988 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts
@@ -166,6 +166,212 @@ describe('waterfall_helpers', () => {
expect(waterfall.errorsCount).toEqual(0);
expect(waterfall).toMatchSnapshot();
});
+ it('should reparent spans', () => {
+ const traceItems = [
+ {
+ processor: { event: 'transaction' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-node' },
+ transaction: {
+ duration: { us: 49660 },
+ name: 'GET /api',
+ id: 'myTransactionId1'
+ },
+ timestamp: { us: 1549324795784006 }
+ } as Transaction,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ timestamp: { us: 1549324795825633 },
+ span: {
+ duration: { us: 481 },
+ name: 'SELECT FROM products',
+ id: 'mySpanIdB'
+ },
+ child_ids: ['mySpanIdA', 'mySpanIdC']
+ } as Span,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 6161 },
+ name: 'Api::ProductsController#index',
+ id: 'mySpanIdA'
+ },
+ timestamp: { us: 1549324795824504 }
+ } as Span,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 532 },
+ name: 'SELECT FROM product',
+ id: 'mySpanIdC'
+ },
+ timestamp: { us: 1549324795827905 }
+ } as Span,
+ {
+ parent: { id: 'myTransactionId1' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-node' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 47557 },
+ name: 'GET opbeans-ruby:3000/api/products',
+ id: 'mySpanIdD'
+ },
+ timestamp: { us: 1549324795785760 }
+ } as Span
+ ];
+ const entryTransactionId = 'myTransactionId1';
+ const waterfall = getWaterfall(
+ {
+ trace: { items: traceItems, errorDocs: [], exceedsMax: false },
+ errorsPerTransaction: {}
+ },
+ entryTransactionId
+ );
+ const getIdAndParentId = (item: IWaterfallItem) => ({
+ id: item.id,
+ parentId: item.parent?.id
+ });
+ expect(waterfall.items.length).toBe(5);
+ expect(getIdAndParentId(waterfall.items[0])).toEqual({
+ id: 'myTransactionId1',
+ parentId: undefined
+ });
+ expect(getIdAndParentId(waterfall.items[1])).toEqual({
+ id: 'mySpanIdD',
+ parentId: 'myTransactionId1'
+ });
+ expect(getIdAndParentId(waterfall.items[2])).toEqual({
+ id: 'mySpanIdB',
+ parentId: 'mySpanIdD'
+ });
+ expect(getIdAndParentId(waterfall.items[3])).toEqual({
+ id: 'mySpanIdA',
+ parentId: 'mySpanIdB'
+ });
+ expect(getIdAndParentId(waterfall.items[4])).toEqual({
+ id: 'mySpanIdC',
+ parentId: 'mySpanIdB'
+ });
+ expect(waterfall.errorItems.length).toBe(0);
+ expect(waterfall.errorsCount).toEqual(0);
+ });
+ it("shouldn't reparent spans when child id isn't found", () => {
+ const traceItems = [
+ {
+ processor: { event: 'transaction' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-node' },
+ transaction: {
+ duration: { us: 49660 },
+ name: 'GET /api',
+ id: 'myTransactionId1'
+ },
+ timestamp: { us: 1549324795784006 }
+ } as Transaction,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ timestamp: { us: 1549324795825633 },
+ span: {
+ duration: { us: 481 },
+ name: 'SELECT FROM products',
+ id: 'mySpanIdB'
+ },
+ child_ids: ['incorrectId', 'mySpanIdC']
+ } as Span,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 6161 },
+ name: 'Api::ProductsController#index',
+ id: 'mySpanIdA'
+ },
+ timestamp: { us: 1549324795824504 }
+ } as Span,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 532 },
+ name: 'SELECT FROM product',
+ id: 'mySpanIdC'
+ },
+ timestamp: { us: 1549324795827905 }
+ } as Span,
+ {
+ parent: { id: 'myTransactionId1' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-node' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 47557 },
+ name: 'GET opbeans-ruby:3000/api/products',
+ id: 'mySpanIdD'
+ },
+ timestamp: { us: 1549324795785760 }
+ } as Span
+ ];
+ const entryTransactionId = 'myTransactionId1';
+ const waterfall = getWaterfall(
+ {
+ trace: { items: traceItems, errorDocs: [], exceedsMax: false },
+ errorsPerTransaction: {}
+ },
+ entryTransactionId
+ );
+ const getIdAndParentId = (item: IWaterfallItem) => ({
+ id: item.id,
+ parentId: item.parent?.id
+ });
+ expect(waterfall.items.length).toBe(5);
+ expect(getIdAndParentId(waterfall.items[0])).toEqual({
+ id: 'myTransactionId1',
+ parentId: undefined
+ });
+ expect(getIdAndParentId(waterfall.items[1])).toEqual({
+ id: 'mySpanIdD',
+ parentId: 'myTransactionId1'
+ });
+ expect(getIdAndParentId(waterfall.items[2])).toEqual({
+ id: 'mySpanIdA',
+ parentId: 'mySpanIdD'
+ });
+ expect(getIdAndParentId(waterfall.items[3])).toEqual({
+ id: 'mySpanIdB',
+ parentId: 'mySpanIdD'
+ });
+ expect(getIdAndParentId(waterfall.items[4])).toEqual({
+ id: 'mySpanIdC',
+ parentId: 'mySpanIdB'
+ });
+ expect(waterfall.errorItems.length).toBe(0);
+ expect(waterfall.errorsCount).toEqual(0);
+ });
});
describe('getWaterfallItems', () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
index 58d9134c4d78..8a873b2ddf1c 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
@@ -236,6 +236,29 @@ const getWaterfallItems = (items: TraceAPIResponse['trace']['items']) =>
}
});
+/**
+ * Changes the parent_id of items based on the child_ids property.
+ * Solves the problem of Inferred spans that are created as child of trace spans
+ * when it actually should be its parent.
+ * @param waterfallItems
+ */
+const reparentSpans = (waterfallItems: IWaterfallItem[]) => {
+ return waterfallItems.map(waterfallItem => {
+ if (waterfallItem.docType === 'span') {
+ const { child_ids: childIds } = waterfallItem.doc;
+ if (childIds) {
+ childIds.forEach(childId => {
+ const item = waterfallItems.find(_item => _item.id === childId);
+ if (item) {
+ item.parentId = waterfallItem.id;
+ }
+ });
+ }
+ }
+ return waterfallItem;
+ });
+};
+
const getChildrenGroupedByParentId = (waterfallItems: IWaterfallItem[]) =>
groupBy(waterfallItems, item => (item.parentId ? item.parentId : ROOT_ID));
@@ -306,7 +329,9 @@ export function getWaterfall(
const waterfallItems: IWaterfallItem[] = getWaterfallItems(trace.items);
- const childrenByParentId = getChildrenGroupedByParentId(waterfallItems);
+ const childrenByParentId = getChildrenGroupedByParentId(
+ reparentSpans(waterfallItems)
+ );
const entryWaterfallTransaction = getEntryWaterfallTransaction(
entryTransactionId,
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx
index 938962cc9dd1..f681f4dfc675 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx
@@ -14,7 +14,8 @@ import {
urlParams,
simpleTrace,
traceWithErrors,
- traceChildStartBeforeParent
+ traceChildStartBeforeParent,
+ inferredSpans
} from './waterfallContainer.stories.data';
import { getWaterfall } from './Waterfall/waterfall_helpers/waterfall_helpers';
@@ -74,3 +75,22 @@ storiesOf('app/TransactionDetails/Waterfall', module).add(
},
{ info: { source: false } }
);
+
+storiesOf('app/TransactionDetails/Waterfall', module).add(
+ 'inferred spans',
+ () => {
+ const waterfall = getWaterfall(
+ inferredSpans as TraceAPIResponse,
+ 'f2387d37260d00bd'
+ );
+ return (
+
+ );
+ },
+ { info: { source: false } }
+);
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts
index 835183e73b29..306c8e4f3fed 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts
@@ -1645,3 +1645,667 @@ export const traceChildStartBeforeParent = {
},
errorsPerTransaction: {}
};
+
+export const inferredSpans = {
+ trace: {
+ items: [
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ source: {
+ ip: '172.18.0.8'
+ },
+ processor: {
+ name: 'transaction',
+ event: 'transaction'
+ },
+ url: {
+ path: '/api/products/2',
+ scheme: 'http',
+ port: 3000,
+ domain: '172.18.0.7',
+ full: 'http://172.18.0.7:3000/api/products/2'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.786Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ client: {
+ ip: '172.18.0.8'
+ },
+ http: {
+ request: {
+ headers: {
+ Accept: ['*/*'],
+ 'User-Agent': ['Python/3.7 aiohttp/3.3.2'],
+ Host: ['172.18.0.7:3000'],
+ 'Accept-Encoding': ['gzip, deflate']
+ },
+ method: 'get',
+ socket: {
+ encrypted: false,
+ remote_address: '172.18.0.8'
+ }
+ },
+ response: {
+ headers: {
+ 'Transfer-Encoding': ['chunked'],
+ Date: ['Thu, 09 Apr 2020 11:36:01 GMT'],
+ 'Content-Type': ['application/json;charset=UTF-8']
+ },
+ status_code: 200,
+ finished: true,
+ headers_sent: true
+ },
+ version: '1.1'
+ },
+ user_agent: {
+ original: 'Python/3.7 aiohttp/3.3.2',
+ name: 'Other',
+ device: {
+ name: 'Other'
+ }
+ },
+ transaction: {
+ duration: {
+ us: 237537
+ },
+ result: 'HTTP 2xx',
+ name: 'APIRestController#product',
+ span_count: {
+ dropped: 0,
+ started: 3
+ },
+ id: 'f2387d37260d00bd',
+ type: 'request',
+ sampled: true
+ },
+ timestamp: {
+ us: 1586432160786001
+ }
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: 'f2387d37260d00bd'
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.810Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ span: {
+ duration: {
+ us: 204574
+ },
+ subtype: 'inferred',
+ name: 'ServletInvocableHandlerMethod#invokeAndHandle',
+ id: 'a5df600bd7bd5e38',
+ type: 'app'
+ },
+ timestamp: {
+ us: 1586432160810441
+ }
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: 'a5df600bd7bd5e38'
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ type: 'apm-server',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.810Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ timestamp: {
+ us: 1586432160810441
+ },
+ span: {
+ duration: {
+ us: 102993
+ },
+ stacktrace: [
+ {
+ library_frame: true,
+ exclude_from_grouping: false,
+ filename: 'InvocableHandlerMethod.java',
+ line: {
+ number: -1
+ },
+ function: 'doInvoke'
+ },
+ {
+ exclude_from_grouping: false,
+ library_frame: true,
+ filename: 'InvocableHandlerMethod.java',
+ line: {
+ number: -1
+ },
+ function: 'invokeForRequest'
+ }
+ ],
+ subtype: 'inferred',
+ name: 'APIRestController#product',
+ id: '808dc34fc41ce522',
+ type: 'app'
+ }
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: 'f2387d37260d00bd'
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ labels: {
+ productId: '2'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.832Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ timestamp: {
+ us: 1586432160832300
+ },
+ span: {
+ duration: {
+ us: 99295
+ },
+ name: 'OpenTracing product span',
+ id: '41226ae63af4f235',
+ type: 'unknown'
+ },
+ child_ids: ['8d80de06aa11a6fc']
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: '808dc34fc41ce522'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.859Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ timestamp: {
+ us: 1586432160859600
+ },
+ span: {
+ duration: {
+ us: 53835
+ },
+ subtype: 'inferred',
+ name: 'Loader#executeQueryStatement',
+ id: '8d80de06aa11a6fc',
+ type: 'app'
+ }
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: '41226ae63af4f235'
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ destination: {
+ address: 'postgres',
+ port: 5432
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.903Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ timestamp: {
+ us: 1586432160903236
+ },
+ span: {
+ duration: {
+ us: 10211
+ },
+ subtype: 'postgresql',
+ destination: {
+ service: {
+ resource: 'postgresql',
+ name: 'postgresql',
+ type: 'db'
+ }
+ },
+ name: 'SELECT FROM products',
+ action: 'query',
+ id: '3708d5623658182f',
+ type: 'db',
+ db: {
+ statement:
+ 'select product0_.id as col_0_0_, product0_.sku as col_1_0_, product0_.name as col_2_0_, product0_.description as col_3_0_, product0_.cost as col_4_0_, product0_.selling_price as col_5_0_, product0_.stock as col_6_0_, producttyp1_.id as col_7_0_, producttyp1_.name as col_8_0_, (select sum(orderline2_.amount) from order_lines orderline2_ where orderline2_.product_id=product0_.id) as col_9_0_ from products product0_ left outer join product_types producttyp1_ on product0_.type_id=producttyp1_.id where product0_.id=?',
+ type: 'sql',
+ user: {
+ name: 'postgres'
+ }
+ }
+ }
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: '41226ae63af4f235'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ destination: {
+ address: 'postgres',
+ port: 5432
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.859Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ timestamp: {
+ us: 1586432160859508
+ },
+ span: {
+ duration: {
+ us: 4503
+ },
+ subtype: 'postgresql',
+ destination: {
+ service: {
+ resource: 'postgresql',
+ name: 'postgresql',
+ type: 'db'
+ }
+ },
+ name: 'empty query',
+ action: 'query',
+ id: '9871cfd612368932',
+ type: 'db',
+ db: {
+ rows_affected: 0,
+ statement: '(empty query)',
+ type: 'sql',
+ user: {
+ name: 'postgres'
+ }
+ }
+ }
+ }
+ ],
+ exceedsMax: false,
+ errorDocs: []
+ },
+ errorsPerTransaction: {}
+};
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 1c618098b36e..0cd1bdf90753 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/canvas/canvas_plugin_src/renderers/embeddable/embeddable.scss b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.scss
index 04f2f393d1e8..ae26a1bee99a 100644
--- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.scss
+++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.scss
@@ -26,8 +26,4 @@
.euiTable {
background: none;
}
-
- .lnsExpressionRenderer {
- @include euiScrollBar;
- }
-}
\ No newline at end of file
+}
diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/services/api.js b/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/services/api.js
index b50c36aa8df9..24bc7e17356e 100644
--- a/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/services/api.js
+++ b/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/services/api.js
@@ -145,9 +145,35 @@ export const updateFollowerIndex = (id, followerIndex) => {
if (isUsingAdvancedSettings) {
uiMetrics.push(UIM_FOLLOWER_INDEX_USE_ADVANCED_OPTIONS);
}
+
+ const {
+ maxReadRequestOperationCount,
+ maxOutstandingReadRequests,
+ maxReadRequestSize,
+ maxWriteRequestOperationCount,
+ maxWriteRequestSize,
+ maxOutstandingWriteRequests,
+ maxWriteBufferCount,
+ maxWriteBufferSize,
+ maxRetryDelay,
+ readPollTimeout,
+ } = followerIndex;
+
const request = httpClient.put(`${API_BASE_PATH}/follower_indices/${encodeURIComponent(id)}`, {
- body: JSON.stringify(followerIndex),
+ body: JSON.stringify({
+ maxReadRequestOperationCount,
+ maxOutstandingReadRequests,
+ maxReadRequestSize,
+ maxWriteRequestOperationCount,
+ maxWriteRequestSize,
+ maxOutstandingWriteRequests,
+ maxWriteBufferCount,
+ maxWriteBufferSize,
+ maxRetryDelay,
+ readPollTimeout,
+ }),
});
+
return trackUserRequest(request, uiMetrics);
};
diff --git a/x-pack/legacy/plugins/cross_cluster_replication/server/np_ready/routes/api/follower_index.ts b/x-pack/legacy/plugins/cross_cluster_replication/server/np_ready/routes/api/follower_index.ts
index 3896e1c02c91..1d7dacf4a868 100644
--- a/x-pack/legacy/plugins/cross_cluster_replication/server/np_ready/routes/api/follower_index.ts
+++ b/x-pack/legacy/plugins/cross_cluster_replication/server/np_ready/routes/api/follower_index.ts
@@ -164,6 +164,18 @@ export const registerFollowerIndexRoutes = ({ router, __LEGACY }: RouteDependenc
path: `${API_BASE_PATH}/follower_indices/{id}`,
validate: {
params: schema.object({ id: schema.string() }),
+ body: schema.object({
+ maxReadRequestOperationCount: schema.maybe(schema.number()),
+ maxOutstandingReadRequests: schema.maybe(schema.number()),
+ maxReadRequestSize: schema.maybe(schema.string()), // byte value
+ maxWriteRequestOperationCount: schema.maybe(schema.number()),
+ maxWriteRequestSize: schema.maybe(schema.string()), // byte value
+ maxOutstandingWriteRequests: schema.maybe(schema.number()),
+ maxWriteBufferCount: schema.maybe(schema.number()),
+ maxWriteBufferSize: schema.maybe(schema.string()), // byte value
+ maxRetryDelay: schema.maybe(schema.string()), // time value
+ readPollTimeout: schema.maybe(schema.string()), // time value
+ }),
},
},
licensePreRoutingFactory({
diff --git a/x-pack/legacy/plugins/siem/scripts/check_circular_deps.js b/x-pack/legacy/plugins/maps/common/get_join_key.ts
similarity index 66%
rename from x-pack/legacy/plugins/siem/scripts/check_circular_deps.js
rename to x-pack/legacy/plugins/maps/common/get_join_key.ts
index 046cc010621d..004f12ca08d2 100644
--- a/x-pack/legacy/plugins/siem/scripts/check_circular_deps.js
+++ b/x-pack/legacy/plugins/maps/common/get_join_key.ts
@@ -4,5 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
-require('../../../../../src/setup_node_env');
-require('../dev_tools/circular_deps/run_check_circular_deps_cli');
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+export * from '../../../../plugins/maps/common/get_join_key';
diff --git a/x-pack/legacy/plugins/maps/common/migrations/join_agg_key.test.ts b/x-pack/legacy/plugins/maps/common/migrations/join_agg_key.test.ts
new file mode 100644
index 000000000000..d92bf0654143
--- /dev/null
+++ b/x-pack/legacy/plugins/maps/common/migrations/join_agg_key.test.ts
@@ -0,0 +1,140 @@
+/*
+ * 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 { LAYER_TYPE } from '../constants';
+import { migrateJoinAggKey } from './join_agg_key';
+
+describe('migrateJoinAggKey', () => {
+ const joins = [
+ {
+ leftField: 'machine.os',
+ right: {
+ id: '9055b4aa-136a-4b6d-90ab-9f94ccfe5eb5',
+ indexPatternTitle: 'kibana_sample_data_logs',
+ term: 'machine.os.keyword',
+ metrics: [
+ {
+ type: 'avg',
+ field: 'bytes',
+ },
+ {
+ type: 'count',
+ },
+ ],
+ whereQuery: {
+ query: 'bytes > 10000',
+ language: 'kuery',
+ },
+ indexPatternRefName: 'layer_1_join_0_index_pattern',
+ },
+ },
+ {
+ leftField: 'machine.os',
+ right: {
+ id: '9a7f4e71-9500-4512-82f1-b7eaee3d87ff',
+ indexPatternTitle: 'kibana_sample_data_logs',
+ term: 'machine.os.keyword',
+ whereQuery: {
+ query: 'bytes < 10000',
+ language: 'kuery',
+ },
+ metrics: [
+ {
+ type: 'avg',
+ field: 'bytes',
+ },
+ ],
+ indexPatternRefName: 'layer_1_join_1_index_pattern',
+ },
+ },
+ ];
+
+ test('Should handle missing layerListJSON attribute', () => {
+ const attributes = {
+ title: 'my map',
+ };
+ expect(migrateJoinAggKey({ attributes })).toEqual({
+ title: 'my map',
+ });
+ });
+
+ test('Should migrate vector styles from legacy join agg key to new join agg key', () => {
+ const layerListJSON = JSON.stringify([
+ {
+ type: LAYER_TYPE.VECTOR,
+ joins,
+ style: {
+ properties: {
+ fillColor: {
+ type: 'DYNAMIC',
+ options: {
+ color: 'Blues',
+ colorCategory: 'palette_0',
+ field: {
+ name:
+ '__kbnjoin__avg_of_bytes_groupby_kibana_sample_data_logs.machine.os.keyword',
+ origin: 'join',
+ },
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: 3,
+ },
+ type: 'ORDINAL',
+ },
+ },
+ lineColor: {
+ type: 'DYNAMIC',
+ options: {
+ color: 'Blues',
+ colorCategory: 'palette_0',
+ field: {
+ name: '__kbnjoin__count_groupby_kibana_sample_data_logs.machine.os.keyword',
+ origin: 'join',
+ },
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: 3,
+ },
+ type: 'ORDINAL',
+ },
+ },
+ lineWidth: {
+ type: 'DYNAMIC',
+ options: {
+ color: 'Blues',
+ colorCategory: 'palette_0',
+ field: {
+ name: 'mySourceField',
+ origin: 'source',
+ },
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: 3,
+ },
+ type: 'ORDINAL',
+ },
+ },
+ },
+ },
+ },
+ ]);
+ const attributes = {
+ title: 'my map',
+ layerListJSON,
+ };
+ const { layerListJSON: migratedLayerListJSON } = migrateJoinAggKey({ attributes });
+ const migratedLayerList = JSON.parse(migratedLayerListJSON!);
+ expect(migratedLayerList[0].style.properties.fillColor.options.field.name).toBe(
+ '__kbnjoin__avg_of_bytes__9055b4aa-136a-4b6d-90ab-9f94ccfe5eb5'
+ );
+ expect(migratedLayerList[0].style.properties.lineColor.options.field.name).toBe(
+ '__kbnjoin__count__9055b4aa-136a-4b6d-90ab-9f94ccfe5eb5'
+ );
+ expect(migratedLayerList[0].style.properties.lineWidth.options.field.name).toBe(
+ 'mySourceField'
+ );
+ });
+});
diff --git a/x-pack/legacy/plugins/maps/common/migrations/join_agg_key.ts b/x-pack/legacy/plugins/maps/common/migrations/join_agg_key.ts
new file mode 100644
index 000000000000..29661aedb550
--- /dev/null
+++ b/x-pack/legacy/plugins/maps/common/migrations/join_agg_key.ts
@@ -0,0 +1,121 @@
+/*
+ * 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 _ from 'lodash';
+import {
+ AGG_DELIMITER,
+ AGG_TYPE,
+ FIELD_ORIGIN,
+ JOIN_FIELD_NAME_PREFIX,
+ LAYER_TYPE,
+ VECTOR_STYLES,
+} from '../constants';
+import { getJoinAggKey } from '../get_join_key';
+import {
+ AggDescriptor,
+ JoinDescriptor,
+ LayerDescriptor,
+ VectorLayerDescriptor,
+} from '../descriptor_types';
+import { MapSavedObjectAttributes } from '../../../../../plugins/maps/common/map_saved_object_type';
+
+const GROUP_BY_DELIMITER = '_groupby_';
+
+function getLegacyAggKey({
+ aggType,
+ aggFieldName,
+ indexPatternTitle,
+ termFieldName,
+}: {
+ aggType: AGG_TYPE;
+ aggFieldName?: string;
+ indexPatternTitle: string;
+ termFieldName: string;
+}): string {
+ const metricKey =
+ aggType !== AGG_TYPE.COUNT ? `${aggType}${AGG_DELIMITER}${aggFieldName}` : aggType;
+ return `${JOIN_FIELD_NAME_PREFIX}${metricKey}${GROUP_BY_DELIMITER}${indexPatternTitle}.${termFieldName}`;
+}
+
+function parseLegacyAggKey(legacyAggKey: string): { aggType: AGG_TYPE; aggFieldName?: string } {
+ const groupBySplit = legacyAggKey
+ .substring(JOIN_FIELD_NAME_PREFIX.length)
+ .split(GROUP_BY_DELIMITER);
+ const metricKey = groupBySplit[0];
+ const metricKeySplit = metricKey.split(AGG_DELIMITER);
+ return {
+ aggType: metricKeySplit[0] as AGG_TYPE,
+ aggFieldName: metricKeySplit.length === 2 ? metricKeySplit[1] : undefined,
+ };
+}
+
+export function migrateJoinAggKey({
+ attributes,
+}: {
+ attributes: MapSavedObjectAttributes;
+}): MapSavedObjectAttributes {
+ if (!attributes || !attributes.layerListJSON) {
+ return attributes;
+ }
+
+ const layerList: LayerDescriptor[] = JSON.parse(attributes.layerListJSON);
+ layerList.forEach((layerDescriptor: LayerDescriptor) => {
+ if (
+ layerDescriptor.type === LAYER_TYPE.VECTOR ||
+ layerDescriptor.type === LAYER_TYPE.BLENDED_VECTOR
+ ) {
+ const vectorLayerDescriptor = layerDescriptor as VectorLayerDescriptor;
+
+ if (
+ !vectorLayerDescriptor.style ||
+ !vectorLayerDescriptor.joins ||
+ vectorLayerDescriptor.joins.length === 0
+ ) {
+ return;
+ }
+
+ const legacyJoinFields = new Map();
+ vectorLayerDescriptor.joins.forEach((joinDescriptor: JoinDescriptor) => {
+ _.get(joinDescriptor, 'right.metrics', []).forEach((aggDescriptor: AggDescriptor) => {
+ const legacyAggKey = getLegacyAggKey({
+ aggType: aggDescriptor.type,
+ aggFieldName: aggDescriptor.field,
+ indexPatternTitle: _.get(joinDescriptor, 'right.indexPatternTitle', ''),
+ termFieldName: _.get(joinDescriptor, 'right.term', ''),
+ });
+ // The legacy getAggKey implemenation has a naming collision bug where
+ // aggType, aggFieldName, indexPatternTitle, and termFieldName would result in the identical aggKey.
+ // The VectorStyle implemenation used the first matching join descriptor
+ // so, in the event of a name collision, the first join descriptor will be used here as well.
+ if (!legacyJoinFields.has(legacyAggKey)) {
+ legacyJoinFields.set(legacyAggKey, joinDescriptor);
+ }
+ });
+ });
+
+ Object.keys(vectorLayerDescriptor.style.properties).forEach(key => {
+ const style: any = vectorLayerDescriptor.style!.properties[key as VECTOR_STYLES];
+ if (_.get(style, 'options.field.origin') === FIELD_ORIGIN.JOIN) {
+ const joinDescriptor = legacyJoinFields.get(style.options.field.name);
+ if (joinDescriptor) {
+ const { aggType, aggFieldName } = parseLegacyAggKey(style.options.field.name);
+ // Update legacy join agg key to new join agg key
+ style.options.field.name = getJoinAggKey({
+ aggType,
+ aggFieldName,
+ rightSourceId: joinDescriptor.right.id!,
+ });
+ }
+ }
+ });
+ }
+ });
+
+ return {
+ ...attributes,
+ layerListJSON: JSON.stringify(layerList),
+ };
+}
diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js
index 1a7f478d3bba..f4e01efc05f4 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/migrations.js b/x-pack/legacy/plugins/maps/migrations.js
index 6a1f5bc93749..a8e69eef7a02 100644
--- a/x-pack/legacy/plugins/maps/migrations.js
+++ b/x-pack/legacy/plugins/maps/migrations.js
@@ -11,6 +11,7 @@ import { moveApplyGlobalQueryToSources } from './common/migrations/move_apply_gl
import { addFieldMetaOptions } from './common/migrations/add_field_meta_options';
import { migrateSymbolStyleDescriptor } from './common/migrations/migrate_symbol_style_descriptor';
import { migrateUseTopHitsToScalingType } from './common/migrations/scaling_type';
+import { migrateJoinAggKey } from './common/migrations/join_agg_key';
export const migrations = {
map: {
@@ -57,5 +58,13 @@ export const migrations = {
attributes: attributesPhase2,
};
},
+ '7.8.0': doc => {
+ const attributes = migrateJoinAggKey(doc);
+
+ return {
+ ...doc,
+ attributes,
+ };
+ },
},
};
diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.test.js b/x-pack/legacy/plugins/maps/public/actions/map_actions.test.js
index c280b8af7ab8..7e2a3c827fa8 100644
--- a/x-pack/legacy/plugins/maps/public/actions/map_actions.test.js
+++ b/x-pack/legacy/plugins/maps/public/actions/map_actions.test.js
@@ -5,7 +5,7 @@
*/
jest.mock('../selectors/map_selectors', () => ({}));
-jest.mock('../kibana_services', () => ({}));
+jest.mock('../../../../../plugins/maps/public/kibana_services', () => ({}));
import { mapExtentChanged, setMouseCoordinates } from './map_actions';
diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js
index 5e497ff0736b..686259aeaaba 100644
--- a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js
+++ b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js
@@ -4,11 +4,28 @@
* you may not use this file except in compliance with the Elastic License.
*/
import _ from 'lodash';
+// Import each layer type, even those not used, to init in registry
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import '../../../../../plugins/maps/public/layers/sources/wms_source';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import '../../../../../plugins/maps/public/layers/sources/ems_file_source';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import '../../../../../plugins/maps/public/layers/sources/es_search_source';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import '../../../../../plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import '../../../../../plugins/maps/public/layers/sources/kibana_regionmap_source';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import '../../../../../plugins/maps/public/layers/sources/es_geo_grid_source';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import '../../../../../plugins/maps/public/layers/sources/xyz_tms_source';
+
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { KibanaTilemapSource } from '../../../../../plugins/maps/public/layers/sources/kibana_tilemap_source';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { EMSTMSSource } from '../../../../../plugins/maps/public/layers/sources/ems_tms_source';
-import { getInjectedVarFunc } from '../kibana_services';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getInjectedVarFunc } from '../../../../../plugins/maps/public/kibana_services';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getKibanaTileMap } from '../../../../../plugins/maps/public/meta';
diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js
index 5334beaaf714..8c9185a16ea0 100644
--- a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js
+++ b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js
@@ -7,7 +7,7 @@
jest.mock('../../../../../plugins/maps/public/meta', () => {
return {};
});
-jest.mock('../kibana_services');
+jest.mock('../../../../../plugins/maps/public/kibana_services');
import { getInitialLayers } from './get_initial_layers';
@@ -15,7 +15,8 @@ const layerListNotProvided = undefined;
describe('Saved object has layer list', () => {
beforeEach(() => {
- require('../kibana_services').getInjectedVarFunc = () => jest.fn();
+ require('../../../../../plugins/maps/public/kibana_services').getInjectedVarFunc = () =>
+ jest.fn();
});
it('Should get initial layers from saved object', () => {
@@ -65,7 +66,7 @@ describe('EMS is enabled', () => {
require('../../../../../plugins/maps/public/meta').getKibanaTileMap = () => {
return null;
};
- require('../kibana_services').getInjectedVarFunc = () => key => {
+ require('../../../../../plugins/maps/public/kibana_services').getInjectedVarFunc = () => key => {
switch (key) {
case 'emsTileLayerId':
return {
@@ -110,7 +111,7 @@ describe('EMS is not enabled', () => {
return null;
};
- require('../kibana_services').getInjectedVarFunc = () => key => {
+ require('../../../../../plugins/maps/public/kibana_services').getInjectedVarFunc = () => key => {
switch (key) {
case 'isEmsEnabled':
return false;
diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_query.js b/x-pack/legacy/plugins/maps/public/angular/get_initial_query.js
index fc8305b252cc..c50ecb2b05dc 100644
--- a/x-pack/legacy/plugins/maps/public/angular/get_initial_query.js
+++ b/x-pack/legacy/plugins/maps/public/angular/get_initial_query.js
@@ -4,11 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import chrome from 'ui/chrome';
-
-const settings = chrome.getUiSettingsClient();
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getUiSettings } from '../../../../../plugins/maps/public/kibana_services';
export function getInitialQuery({ mapStateJSON, appState = {}, userQueryLanguage }) {
+ const settings = getUiSettings();
+
if (appState.query) {
return appState.query;
}
diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_refresh_config.js b/x-pack/legacy/plugins/maps/public/angular/get_initial_refresh_config.js
index 10a2580b78e6..8735d45debfc 100644
--- a/x-pack/legacy/plugins/maps/public/angular/get_initial_refresh_config.js
+++ b/x-pack/legacy/plugins/maps/public/angular/get_initial_refresh_config.js
@@ -3,11 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import chrome from 'ui/chrome';
-
-const uiSettings = chrome.getUiSettingsClient();
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getUiSettings } from '../../../../../plugins/maps/public/kibana_services';
export function getInitialRefreshConfig({ mapStateJSON, globalState = {} }) {
+ const uiSettings = getUiSettings();
+
if (mapStateJSON) {
const mapState = JSON.parse(mapStateJSON);
if (mapState.refreshConfig) {
diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_time_filters.js b/x-pack/legacy/plugins/maps/public/angular/get_initial_time_filters.js
index 82439175841b..74fbf603e99f 100644
--- a/x-pack/legacy/plugins/maps/public/angular/get_initial_time_filters.js
+++ b/x-pack/legacy/plugins/maps/public/angular/get_initial_time_filters.js
@@ -3,9 +3,8 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import chrome from 'ui/chrome';
-
-const uiSettings = chrome.getUiSettingsClient();
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getUiSettings } from '../../../../../plugins/maps/public/kibana_services';
export function getInitialTimeFilters({ mapStateJSON, globalState = {} }) {
if (mapStateJSON) {
@@ -15,6 +14,6 @@ export function getInitialTimeFilters({ mapStateJSON, globalState = {} }) {
}
}
- const defaultTime = uiSettings.get('timepicker:timeDefaults');
+ const defaultTime = getUiSettings().get('timepicker:timeDefaults');
return { ...defaultTime, ...globalState.time };
}
diff --git a/x-pack/legacy/plugins/maps/public/angular/map.html b/x-pack/legacy/plugins/maps/public/angular/map.html
index 2f34ffa660d6..7d7dcf6f9c9a 100644
--- a/x-pack/legacy/plugins/maps/public/angular/map.html
+++ b/x-pack/legacy/plugins/maps/public/angular/map.html
@@ -3,11 +3,11 @@
{
- const { filterManager } = npStart.plugins.data.query;
+ const savedQueryService = getData().query.savedQueries;
+ const { filterManager } = getData().query;
const savedMap = $route.current.locals.map;
$scope.screenTitle = savedMap.title;
let unsubscribe;
@@ -115,6 +129,14 @@ app.controller(
return _.get($state, 'filters', []);
}
+ const visibleSubscription = getCoreChrome()
+ .getIsVisible$()
+ .subscribe(isVisible => {
+ $scope.$evalAsync(() => {
+ $scope.isVisible = isVisible;
+ });
+ });
+
$scope.$listen(globalState, 'fetch_with_changes', diff => {
if (diff.includes('time') || diff.includes('filters')) {
onQueryChange({
@@ -169,10 +191,10 @@ app.controller(
});
/* Saved Queries */
- $scope.showSaveQuery = capabilities.get().maps.saveQuery;
+ $scope.showSaveQuery = getMapsCapabilities().saveQuery;
$scope.$watch(
- () => capabilities.get().maps.saveQuery,
+ () => getMapsCapabilities().saveQuery,
newCapability => {
$scope.showSaveQuery = newCapability;
}
@@ -342,7 +364,7 @@ app.controller(
// clear old UI state
store.dispatch(setSelectedLayer(null));
store.dispatch(updateFlyout(FLYOUT_STATE.NONE));
- store.dispatch(setReadOnly(!capabilities.get().maps.save));
+ store.dispatch(setReadOnly(!getMapsCapabilities().save));
handleStoreChanges(store);
unsubscribe = store.subscribe(() => {
@@ -446,6 +468,8 @@ app.controller(
$scope.$on('$destroy', () => {
window.removeEventListener('beforeunload', beforeUnload);
+ visibleSubscription.unsubscribe();
+ getCoreChrome().setIsVisible(true);
if (unsubscribe) {
unsubscribe();
@@ -457,7 +481,7 @@ app.controller(
});
const updateBreadcrumbs = () => {
- chrome.breadcrumbs.set([
+ getCoreChrome().setBreadcrumbs([
{
text: i18n.translate('xpack.maps.mapController.mapsBreadcrumbLabel', {
defaultMessage: 'Maps',
@@ -482,7 +506,7 @@ app.controller(
};
updateBreadcrumbs();
- addHelpMenuToAppChrome(chrome);
+ addHelpMenuToAppChrome();
async function doSave(saveOptions) {
await store.dispatch(clearTransientLayerStateAndCloseFlyout());
@@ -491,9 +515,9 @@ app.controller(
try {
id = await savedMap.save(saveOptions);
- docTitle.change(savedMap.title);
+ getCoreChrome().docTitle.change(savedMap.title);
} catch (err) {
- toastNotifications.addDanger({
+ getToasts().addDanger({
title: i18n.translate('xpack.maps.mapController.saveErrorMessage', {
defaultMessage: `Error on saving '{title}'`,
values: { title: savedMap.title },
@@ -505,7 +529,7 @@ app.controller(
}
if (id) {
- toastNotifications.addSuccess({
+ getToasts().addSuccess({
title: i18n.translate('xpack.maps.mapController.saveSuccessMessage', {
defaultMessage: `Saved '{title}'`,
values: { title: savedMap.title },
@@ -539,6 +563,7 @@ app.controller(
}),
testId: 'mapsFullScreenMode',
run() {
+ getCoreChrome().setIsVisible(false);
store.dispatch(enableFullScreen());
},
},
@@ -556,7 +581,7 @@ app.controller(
getInspector().open(inspectorAdapters, {});
},
},
- ...(capabilities.get().maps.save
+ ...(getMapsCapabilities().save
? [
{
id: 'save',
@@ -611,7 +636,7 @@ app.controller(
showDescription={false}
/>
);
- showSaveModal(saveModal, npStart.core.i18n.Context);
+ showSaveModal(saveModal, getCoreI18n().Context);
},
},
]
diff --git a/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js b/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js
index bc636c0b200f..710997a9c0d7 100644
--- a/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js
+++ b/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js
@@ -4,24 +4,27 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import _ from 'lodash';
import { createSavedGisMapClass } from './saved_gis_map';
-import { uiModules } from 'ui/modules';
import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public';
-import { npStart } from '../../../../../../../src/legacy/ui/public/new_platform';
+import {
+ getCoreChrome,
+ getSavedObjectsClient,
+ getIndexPatternService,
+ getCoreOverlays,
+ getData,
+ // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../../../plugins/maps/public/kibana_services';
-const module = uiModules.get('app/maps');
-
-// This is the only thing that gets injected into controllers
-module.service('gisMapSavedObjectLoader', function() {
- const savedObjectsClient = npStart.core.savedObjects.client;
+export const getMapsSavedObjectLoader = _.once(function() {
const services = {
- savedObjectsClient,
- indexPatterns: npStart.plugins.data.indexPatterns,
- search: npStart.plugins.data.search,
- chrome: npStart.core.chrome,
- overlays: npStart.core.overlays,
+ savedObjectsClient: getSavedObjectsClient(),
+ indexPatterns: getIndexPatternService(),
+ search: getData().search,
+ chrome: getCoreChrome(),
+ overlays: getCoreOverlays(),
};
const SavedGisMap = createSavedGisMapClass(services);
- return new SavedObjectLoader(SavedGisMap, npStart.core.savedObjects.client, npStart.core.chrome);
+ return new SavedObjectLoader(SavedGisMap, getSavedObjectsClient(), getCoreChrome());
});
diff --git a/x-pack/legacy/plugins/maps/public/components/map_listing.js b/x-pack/legacy/plugins/maps/public/components/map_listing.js
index 6fb5930e81a2..ef1d524cb91d 100644
--- a/x-pack/legacy/plugins/maps/public/components/map_listing.js
+++ b/x-pack/legacy/plugins/maps/public/components/map_listing.js
@@ -7,7 +7,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
-import { toastNotifications } from 'ui/notify';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getToasts } from '../../../../../plugins/maps/public/kibana_services';
import {
EuiTitle,
EuiFieldSearch,
@@ -27,7 +28,6 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { addHelpMenuToAppChrome } from '../help_menu_util';
-import chrome from 'ui/chrome';
export const EMPTY_FILTER = '';
@@ -55,7 +55,7 @@ export class MapListing extends React.Component {
componentDidMount() {
this.fetchItems();
- addHelpMenuToAppChrome(chrome);
+ addHelpMenuToAppChrome();
}
debouncedFetch = _.debounce(async filter => {
@@ -91,7 +91,7 @@ export class MapListing extends React.Component {
try {
await this.props.delete(this.state.selectedIds);
} catch (error) {
- toastNotifications.addDanger({
+ getToasts().addDanger({
title: i18n.translate('xpack.maps.mapListing.unableToDeleteToastTitle', {
defaultMessage: `Unable to delete map(s)`,
}),
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.js b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.js
index 39cb2c469e05..2d8265bae938 100644
--- a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.js
+++ b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.js
@@ -18,6 +18,8 @@ import {
getQueryableUniqueIndexPatternIds,
isToolbarOverlayHidden,
} from '../../selectors/map_selectors';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getCoreChrome } from '../../../../../../plugins/maps/public/kibana_services';
function mapStateToProps(state = {}) {
const flyoutDisplay = getFlyoutDisplay(state);
@@ -37,7 +39,10 @@ function mapStateToProps(state = {}) {
function mapDispatchToProps(dispatch) {
return {
triggerRefreshTimer: () => dispatch(triggerRefreshTimer()),
- exitFullScreen: () => dispatch(exitFullScreen()),
+ exitFullScreen: () => {
+ dispatch(exitFullScreen());
+ getCoreChrome().setIsVisible(true);
+ },
cancelAllInFlightRequests: () => dispatch(cancelAllInFlightRequests()),
};
}
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js
index 358313b8f5b6..06097ebea190 100644
--- a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js
+++ b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js
@@ -12,7 +12,8 @@ import { ToolbarOverlay } from '../toolbar_overlay/index';
import { LayerPanel } from '../layer_panel/index';
import { AddLayerPanel } from '../layer_addpanel/index';
import { EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui';
-import { ExitFullScreenButton } from 'ui/exit_full_screen';
+import { ExitFullScreenButton } from '../../../../../../../src/plugins/kibana_react/public';
+
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getIndexPatternsFromIds } from '../../../../../../plugins/maps/public/index_pattern_util';
import { ES_GEO_FIELD_TYPE } from '../../../common/constants';
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 a54df69471aa..92fcf01f3901 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/filter_editor/filter_editor.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js
index f6bcac0dfc33..40fdac38493d 100644
--- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js
+++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js
@@ -20,12 +20,14 @@ import {
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
-import { getIndexPatternService } from '../../../kibana_services';
+import {
+ getIndexPatternService,
+ getUiSettings,
+ getData,
+ // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../../../../plugins/maps/public/kibana_services';
import { GlobalFilterCheckbox } from '../../../components/global_filter_checkbox';
-import { npStart } from 'ui/new_platform';
-const { SearchBar } = npStart.plugins.data.ui;
-
export class FilterEditor extends Component {
state = {
isPopoverOpen: false,
@@ -84,7 +86,8 @@ export class FilterEditor extends Component {
_renderQueryPopover() {
const layerQuery = this.props.layer.getQuery();
- const { uiSettings } = npStart.core;
+ const uiSettings = getUiSettings();
+ const { SearchBar } = getData().ui;
return (
{
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/layer_panel/view.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js
index 1b269e388bea..2521318f0b3c 100644
--- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js
+++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js
@@ -30,12 +30,11 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public';
import { Storage } from '../../../../../../../src/plugins/kibana_utils/public';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getData, getCore } from '../../../../../../plugins/maps/public/kibana_services';
const localStorage = new Storage(window.localStorage);
-// This import will eventually become a dependency injected by the fully deangularized NP plugin.
-import { npStart } from 'ui/new_platform';
-
export class LayerPanel extends React.Component {
state = {
displayName: '',
@@ -168,8 +167,8 @@ export class LayerPanel extends React.Component {
services={{
appName: 'maps',
storage: localStorage,
- data: npStart.plugins.data,
- ...npStart.core,
+ data: getData(),
+ ...getCore(),
}}
>
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js
index 7063c50edad6..15824b82965e 100644
--- a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js
+++ b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js
@@ -12,7 +12,8 @@ import { i18n } from '@kbn/i18n';
import { createSpatialFilterWithGeometry } from '../../../../../../../plugins/maps/public/elasticsearch_geo_utils';
import { GEO_JSON_TYPE } from '../../../../common/constants';
import { GeometryFilterForm } from '../../../components/geometry_filter_form';
-import { UrlOverflowService } from 'ui/error_url_overflow';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { UrlOverflowService } from '../../../../../../../../src/plugins/kibana_legacy/public';
import rison from 'rison-node';
// over estimated and imprecise value to ensure filter has additional room for any meta keys added when filter is mapped.
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 fedc1902d80a..a36e1d7048e9 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,11 +14,15 @@ 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';
-import chrome from 'ui/chrome';
import { spritesheet } from '@elastic/maki';
import sprites1 from '@elastic/maki/dist/sprite@1.png';
import sprites2 from '@elastic/maki/dist/sprite@2.png';
@@ -29,6 +33,8 @@ import {
clampToLonBounds,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../../../plugins/maps/public/elasticsearch_geo_utils';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getInjectedVarFunc } from '../../../../../../../plugins/maps/public/kibana_services';
mapboxgl.workerUrl = mbWorkerUrl;
mapboxgl.setRTLTextPlugin(mbRtlPlugin);
@@ -129,8 +135,10 @@ export class MBMapContainer extends React.Component {
container: this.refs.mapContainer,
style: mbStyle,
scrollZoom: this.props.scrollZoom,
- preserveDrawingBuffer: chrome.getInjected('preserveDrawingBuffer', false),
+ 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/embeddable/map_embeddable.tsx b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx
index bdd2d863e692..b8e4c84ad56a 100644
--- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx
+++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx
@@ -11,7 +11,6 @@ import { render, unmountComponentAtNode } from 'react-dom';
import 'mapbox-gl/dist/mapbox-gl.css';
import { I18nContext } from 'ui/i18n';
-import { npStart } from 'ui/new_platform';
import { Subscription } from 'rxjs';
import { Unsubscribe } from 'redux';
import {
@@ -59,6 +58,8 @@ import { getMapCenter, getMapZoom, getHiddenLayerIds } from '../selectors/map_se
import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { RenderToolTipContent } from '../../../../../plugins/maps/public/layers/tooltips/tooltip_property';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getUiActions } from '../../../../../plugins/maps/public/kibana_services';
interface MapEmbeddableConfig {
editUrl?: string;
@@ -269,7 +270,7 @@ export class MapEmbeddable extends Embeddable {
- npStart.plugins.uiActions.executeTriggerActions(APPLY_FILTER_TRIGGER, {
+ getUiActions().executeTriggerActions(APPLY_FILTER_TRIGGER, {
embeddable: this,
filters,
});
diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts
index 5deb3057a449..96c3baf634a8 100644
--- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts
+++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts
@@ -5,14 +5,18 @@
*/
import _ from 'lodash';
-import chrome from 'ui/chrome';
-import { capabilities } from 'ui/capabilities';
import { i18n } from '@kbn/i18n';
import { npSetup, npStart } from 'ui/new_platform';
-import { SavedObjectLoader } from 'src/plugins/saved_objects/public';
import { IIndexPattern } from 'src/plugins/data/public';
+// @ts-ignore
+import { getMapsSavedObjectLoader } from '../angular/services/gis_map_saved_object_loader';
import { MapEmbeddable, MapEmbeddableInput } from './map_embeddable';
-import { getIndexPatternService } from '../kibana_services';
+import {
+ getIndexPatternService,
+ getHttp,
+ getMapsCapabilities,
+ // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../../plugins/maps/public/kibana_services';
import {
EmbeddableFactoryDefinition,
IContainer,
@@ -26,7 +30,6 @@ import { getQueryableUniqueIndexPatternIds } from '../selectors/map_selectors';
import { getInitialLayers } from '../angular/get_initial_layers';
import { mergeInputWithSavedMap } from './merge_input_with_saved_map';
import '../angular/services/gis_map_saved_object_loader';
-import { bindSetupCoreAndPlugins, bindStartCoreAndPlugins } from '../plugin';
// @ts-ignore
import {
bindSetupCoreAndPlugins as bindNpSetupCoreAndPlugins,
@@ -44,14 +47,12 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
};
constructor() {
// Init required services. Necessary while in legacy
- bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins);
bindNpSetupCoreAndPlugins(npSetup.core, npSetup.plugins);
- bindStartCoreAndPlugins(npStart.core, npStart.plugins);
bindNpStartCoreAndPlugins(npStart.core, npStart.plugins);
}
async isEditable() {
- return capabilities.get().maps.save as boolean;
+ return getMapsCapabilities().save as boolean;
}
// Not supported yet for maps types.
@@ -96,8 +97,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
}
async _fetchSavedMap(savedObjectId: string) {
- const $injector = await chrome.dangerouslyGetActiveInjector();
- const savedObjectLoader = $injector.get('gisMapSavedObjectLoader');
+ const savedObjectLoader = getMapsSavedObjectLoader();
return await savedObjectLoader.get(savedObjectId);
}
@@ -114,7 +114,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
{
layerList,
title: savedMap.title,
- editUrl: chrome.addBasePath(createMapPath(savedObjectId)),
+ editUrl: getHttp().basePath.prepend(createMapPath(savedObjectId)),
indexPatterns,
editable: await this.isEditable(),
},
diff --git a/x-pack/legacy/plugins/maps/public/help_menu_util.js b/x-pack/legacy/plugins/maps/public/help_menu_util.js
index 72d51cc180eb..70b9340b562c 100644
--- a/x-pack/legacy/plugins/maps/public/help_menu_util.js
+++ b/x-pack/legacy/plugins/maps/public/help_menu_util.js
@@ -3,10 +3,13 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getDocLinks, getCoreChrome } from '../../../../plugins/maps/public/kibana_services';
-export function addHelpMenuToAppChrome(chrome) {
- chrome.helpExtension.set({
+export function addHelpMenuToAppChrome() {
+ const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = getDocLinks();
+
+ getCoreChrome().setHelpExtension({
appName: 'Maps',
links: [
{
diff --git a/x-pack/legacy/plugins/maps/public/index.ts b/x-pack/legacy/plugins/maps/public/index.ts
index b69485e251be..8555594e909d 100644
--- a/x-pack/legacy/plugins/maps/public/index.ts
+++ b/x-pack/legacy/plugins/maps/public/index.ts
@@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import './kibana_services';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import '../../../../plugins/maps/public/kibana_services';
// import the uiExports that we want to "use"
import 'uiExports/inspectorViews';
diff --git a/x-pack/legacy/plugins/maps/public/kibana_services.d.ts b/x-pack/legacy/plugins/maps/public/kibana_services.d.ts
deleted file mode 100644
index 89b1fee1aa84..000000000000
--- a/x-pack/legacy/plugins/maps/public/kibana_services.d.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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 { IIndexPattern } from 'src/plugins/data/public';
-
-export function getIndexPatternService(): {
- get: (id: string) => IIndexPattern | undefined;
-};
-
-export function setLicenseId(args: unknown): void;
-export function setInspector(args: unknown): void;
-export function setFileUpload(args: unknown): void;
-export function setIndexPatternSelect(args: unknown): void;
-export function setHttp(args: unknown): void;
-export function setTimeFilter(args: unknown): void;
-export function setUiSettings(args: unknown): void;
-export function setInjectedVarFunc(args: unknown): void;
-export function setToasts(args: unknown): void;
-export function setIndexPatternService(args: unknown): void;
-export function setAutocompleteService(args: unknown): void;
diff --git a/x-pack/legacy/plugins/maps/public/kibana_services.js b/x-pack/legacy/plugins/maps/public/kibana_services.js
deleted file mode 100644
index a6491fe1aa6d..000000000000
--- a/x-pack/legacy/plugins/maps/public/kibana_services.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-
-let indexPatternService;
-export const setIndexPatternService = dataIndexPatterns =>
- (indexPatternService = dataIndexPatterns);
-export const getIndexPatternService = () => indexPatternService;
-
-let inspector;
-export const setInspector = newInspector => (inspector = newInspector);
-export const getInspector = () => {
- return inspector;
-};
-
-let getInjectedVar;
-export const setInjectedVarFunc = getInjectedVarFunc => (getInjectedVar = getInjectedVarFunc);
-export const getInjectedVarFunc = () => getInjectedVar;
-
-let indexPatternSelectComponent;
-export const setIndexPatternSelect = indexPatternSelect =>
- (indexPatternSelectComponent = indexPatternSelect);
-export const getIndexPatternSelectComponent = () => indexPatternSelectComponent;
-
-let dataTimeFilter;
-export const setTimeFilter = timeFilter => (dataTimeFilter = timeFilter);
-export const getTimeFilter = () => dataTimeFilter;
diff --git a/x-pack/legacy/plugins/maps/public/plugin.ts b/x-pack/legacy/plugins/maps/public/plugin.ts
index 0fa7e1106a6d..71f1a30c1fbe 100644
--- a/x-pack/legacy/plugins/maps/public/plugin.ts
+++ b/x-pack/legacy/plugins/maps/public/plugin.ts
@@ -4,28 +4,13 @@
* 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 { wrapInI18nContext } from 'ui/i18n';
-// @ts-ignore
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
// @ts-ignore
+import { wrapInI18nContext } from 'ui/i18n';
+// @ts-ignore
import { MapListing } from './components/map_listing';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import {
- setInspector,
- setIndexPatternSelect,
- setTimeFilter,
- setInjectedVarFunc,
- setIndexPatternService,
-} from './kibana_services';
// @ts-ignore
import {
bindSetupCoreAndPlugins as bindNpSetupCoreAndPlugins,
@@ -62,20 +47,6 @@ interface MapsPluginStartDependencies {
// file_upload TODO: Export type from file upload and use here
}
-export const bindSetupCoreAndPlugins = (core: CoreSetup, plugins: any) => {
- const { injectedMetadata } = core;
- setInjectedVarFunc(injectedMetadata.getInjectedVar);
- setInjectedVarFunc(core.injectedMetadata.getInjectedVar);
-};
-
-export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => {
- const { data, inspector } = plugins;
- setInspector(inspector);
- setIndexPatternSelect(data.ui.IndexPatternSelect);
- setTimeFilter(data.query.timefilter.timefilter);
- setIndexPatternService(data.indexPatterns);
-};
-
/** @internal */
export class MapsPlugin implements Plugin {
public setup(core: CoreSetup, { __LEGACY: { uiModules }, np }: MapsPluginSetupDependencies) {
@@ -85,14 +56,12 @@ export class MapsPlugin implements Plugin {
return reactDirective(wrapInI18nContext(MapListing));
});
- bindSetupCoreAndPlugins(core, np);
bindNpSetupCoreAndPlugins(core, np);
np.home.featureCatalogue.register(featureCatalogueEntry);
}
public start(core: CoreStart, plugins: MapsPluginStartDependencies) {
- bindStartCoreAndPlugins(core, plugins);
bindNpStartCoreAndPlugins(core, plugins);
}
}
diff --git a/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js b/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js
index 64a42173098e..9dc07bcb5dc0 100644
--- a/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js
+++ b/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js
@@ -4,12 +4,20 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import chrome from 'ui/chrome';
-import { npSetup } from '../../../../../src/legacy/ui/public/new_platform';
import { i18n } from '@kbn/i18n';
import { APP_ID, APP_ICON, MAP_BASE_URL } from '../common/constants';
+import {
+ getInjectedVarFunc,
+ getVisualizations,
+ // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../plugins/maps/public/kibana_services';
+import { npSetup } from 'ui/new_platform';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { bindSetupCoreAndPlugins } from '../../../../plugins/maps/public/plugin';
-const showMapVisualizationTypes = chrome.getInjected('showMapVisualizationTypes', false);
+bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins);
+
+const showMapVisualizationTypes = getInjectedVarFunc()('showMapVisualizationTypes', false);
const description = i18n.translate('xpack.maps.visTypeAlias.description', {
defaultMessage: 'Create and style maps with multiple layers and indices.',
@@ -23,7 +31,7 @@ The Maps app offers more functionality and is easier to use.`,
}
);
-npSetup.plugins.visualizations.registerAlias({
+getVisualizations().registerAlias({
aliasUrl: MAP_BASE_URL,
name: APP_ID,
title: i18n.translate('xpack.maps.visTypeAlias.title', {
@@ -37,5 +45,5 @@ npSetup.plugins.visualizations.registerAlias({
});
if (!showMapVisualizationTypes) {
- npSetup.plugins.visualizations.hideTypes(['region_map', 'tile_map']);
+ getVisualizations().hideTypes(['region_map', 'tile_map']);
}
diff --git a/x-pack/legacy/plugins/maps/public/routes.js b/x-pack/legacy/plugins/maps/public/routes.js
index 49705acb417e..c082e0e1352c 100644
--- a/x-pack/legacy/plugins/maps/public/routes.js
+++ b/x-pack/legacy/plugins/maps/public/routes.js
@@ -5,20 +5,23 @@
*/
import { i18n } from '@kbn/i18n';
-import { capabilities } from 'ui/capabilities';
-import chrome from 'ui/chrome';
import routes from 'ui/routes';
-import { docTitle } from 'ui/doc_title';
import listingTemplate from './angular/listing_ng_wrapper.html';
import mapTemplate from './angular/map.html';
-import { npStart } from 'ui/new_platform';
+import {
+ getSavedObjectsClient,
+ getCoreChrome,
+ getMapsCapabilities,
+ // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../plugins/maps/public/kibana_services';
+import { getMapsSavedObjectLoader } from './angular/services/gis_map_saved_object_loader';
routes.enable();
routes
.defaults(/.*/, {
- badge: uiCapabilities => {
- if (uiCapabilities.maps.save) {
+ badge: () => {
+ if (getMapsCapabilities().save) {
return undefined;
}
@@ -35,7 +38,8 @@ routes
})
.when('/', {
template: listingTemplate,
- controller($scope, gisMapSavedObjectLoader, config) {
+ controller($scope, config) {
+ const gisMapSavedObjectLoader = getMapsSavedObjectLoader();
$scope.listingLimit = config.get('savedObjects:listingLimit');
$scope.find = search => {
return gisMapSavedObjectLoader.find(search, $scope.listingLimit);
@@ -43,19 +47,17 @@ routes
$scope.delete = ids => {
return gisMapSavedObjectLoader.delete(ids);
};
- $scope.readOnly = !capabilities.get().maps.save;
+ $scope.readOnly = !getMapsCapabilities().save;
},
resolve: {
hasMaps: function(kbnUrl) {
- chrome
- .getSavedObjectsClient()
+ getSavedObjectsClient()
.find({ type: 'map', perPage: 1 })
.then(resp => {
// Do not show empty listing page, just redirect to a new map
if (resp.savedObjects.length === 0) {
kbnUrl.redirect('/map');
}
-
return true;
});
},
@@ -65,7 +67,8 @@ routes
template: mapTemplate,
controller: 'GisMapController',
resolve: {
- map: function(gisMapSavedObjectLoader, redirectWhenMissing) {
+ map: function(redirectWhenMissing) {
+ const gisMapSavedObjectLoader = getMapsSavedObjectLoader();
return gisMapSavedObjectLoader.get().catch(
redirectWhenMissing({
map: '/',
@@ -78,13 +81,14 @@ routes
template: mapTemplate,
controller: 'GisMapController',
resolve: {
- map: function(gisMapSavedObjectLoader, redirectWhenMissing, $route) {
+ map: function(redirectWhenMissing, $route) {
+ const gisMapSavedObjectLoader = getMapsSavedObjectLoader();
const id = $route.current.params.id;
return gisMapSavedObjectLoader
.get(id)
.then(savedMap => {
- npStart.core.chrome.recentlyAccessed.add(savedMap.getFullPath(), savedMap.title, id);
- docTitle.change(savedMap.title);
+ getCoreChrome().recentlyAccessed.add(savedMap.getFullPath(), savedMap.title, id);
+ getCoreChrome().docTitle.change(savedMap.title);
return savedMap;
})
.catch(
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 59346e4c6fb9..1e7102593551 100644
--- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js
+++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js
@@ -16,7 +16,10 @@ import { VectorLayer } from '../../../../../plugins/maps/public/layers/vector_la
import { HeatmapLayer } from '../../../../../plugins/maps/public/layers/heatmap_layer';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { BlendedVectorLayer } from '../../../../../plugins/maps/public/layers/blended_vector_layer';
-import { getTimeFilter } from '../kibana_services';
+// 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 {
@@ -50,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 77bd29259647..72cc74861754 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', () => {});
@@ -14,7 +15,7 @@ jest.mock('../../../../../plugins/maps/public/reducers/non_serializable_instance
return {};
},
}));
-jest.mock('../kibana_services', () => ({
+jest.mock('../../../../../plugins/maps/public/kibana_services', () => ({
getTimeFilter: () => ({
getTime: () => {
return {
diff --git a/x-pack/legacy/plugins/rollup/README.md b/x-pack/legacy/plugins/rollup/README.md
index 6d04973de591..3647be38b6a0 100644
--- a/x-pack/legacy/plugins/rollup/README.md
+++ b/x-pack/legacy/plugins/rollup/README.md
@@ -14,7 +14,7 @@ The rest of this doc dives into the implementation details of each of the above
## Create and manage rollup jobs
-The most straight forward part of this plugin! A new app called Rollup Jobs is registered in the Management section and follows a typical CRUD UI pattern. This app allows users to create, start, stop, clone, and delete rollup jobs. There is no way to edit an existing rollup job; instead, the UI offers a cloning ability. The client-side portion of this app lives [here](public/crud_app) and uses endpoints registered [here](server/routes/api/jobs.js).
+The most straight forward part of this plugin! A new app called Rollup Jobs is registered in the Management section and follows a typical CRUD UI pattern. This app allows users to create, start, stop, clone, and delete rollup jobs. There is no way to edit an existing rollup job; instead, the UI offers a cloning ability. The client-side portion of this app lives [here](../../../plugins/rollup/public/crud_app) and uses endpoints registered [here](server/routes/api/jobs.js).
Refer to the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/rollup-getting-started.html) to understand rollup indices and how to create rollup jobs.
@@ -22,22 +22,22 @@ Refer to the [Elasticsearch documentation](https://www.elastic.co/guide/en/elast
Kibana uses index patterns to consume and visualize rollup indices. Typically, Kibana can inspect the indices captured by an index pattern, identify its aggregations and fields, and determine how to consume the data. Rollup indices don't contain this type of information, so we predefine how to consume a rollup index pattern with the type and typeMeta fields on the index pattern saved object. All rollup index patterns have `type` defined as "rollup" and `typeMeta` defined as an object of the index pattern's capabilities.
-In the Index Pattern app, the "Create index pattern" button includes a context menu when a rollup index is detected. This menu offers items for creating a standard index pattern and a rollup index pattern. A [rollup config is registered to index pattern creation extension point](public/index_pattern_creation/rollup_index_pattern_creation_config.js). The context menu behavior in particular uses the `getIndexPatternCreationOption()` method. When the user chooses to create a rollup index pattern, this config changes the behavior of the index pattern creation wizard:
+In the Index Pattern app, the "Create index pattern" button includes a context menu when a rollup index is detected. This menu offers items for creating a standard index pattern and a rollup index pattern. A [rollup config is registered to index pattern creation extension point](../../../plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js). The context menu behavior in particular uses the `getIndexPatternCreationOption()` method. When the user chooses to create a rollup index pattern, this config changes the behavior of the index pattern creation wizard:
1. Adds a `Rollup` badge to rollup indices using `getIndexTags()`.
2. Enforces index pattern rules using `checkIndicesForErrors()`. Rollup index patterns must match **one** rollup index, and optionally, any number of regular indices. A rollup index pattern configured with one or more regular indices is known as a "hybrid" index pattern. This allows the user to visualize historical (rollup) data and live (regular) data in the same visualization.
3. Routes to this plugin's [rollup `_fields_for_wildcard` endpoint](server/routes/api/index_patterns.js), instead of the standard one, using `getFetchForWildcardOptions()`, so that the internal rollup data field names are mapped to the original field names.
4. Writes additional information about aggregations, fields, histogram interval, and date histogram interval and timezone to the rollup index pattern saved object using `getIndexPatternMappings()`. This collection of information is referred to as its "capabilities".
-Once a rollup index pattern is created, it is tagged with `Rollup` in the list of index patterns, and its details page displays capabilities information. This is done by registering [yet another config for the index pattern list](public/index_pattern_list/rollup_index_pattern_list_config.js) extension points.
+Once a rollup index pattern is created, it is tagged with `Rollup` in the list of index patterns, and its details page displays capabilities information. This is done by registering [yet another config for the index pattern list](../../../plugins/rollup/public/index_pattern_list/rollup_index_pattern_list_config.js) extension points.
## Create visualizations from rollup index patterns
This plugin enables the user to create visualizations from rollup data using the Visualize app, excluding TSVB, Vega, and Timelion. When Visualize sends search requests, this plugin routes the requests to the [Elasticsearch rollup search endpoint](https://www.elastic.co/guide/en/elasticsearch/reference/current/rollup-search.html), which searches the special document structure within rollup indices. The visualization options available to users are based on the capabilities of the rollup index pattern they're visualizing.
-Routing to the Elasticsearch rollup search endpoint is done by creating an extension point in Courier, effectively allowing multiple "search strategies" to be registered. A [rollup search strategy](public/search/register.js) is registered by this plugin that queries [this plugin's rollup search endpoint](server/routes/api/search.js).
+Routing to the Elasticsearch rollup search endpoint is done by creating an extension point in Courier, effectively allowing multiple "search strategies" to be registered. A [rollup search strategy](../../../plugins/rollup/public/search/register.js) is registered by this plugin that queries [this plugin's rollup search endpoint](server/routes/api/search.js).
-Limiting visualization editor options is done by [registering configs](public/visualize/index.js) to various vis extension points. These configs use information stored on the rollup index pattern to limit:
+Limiting visualization editor options is done by [registering configs](../../../plugins/rollup/public/visualize/index.js) to various vis extension points. These configs use information stored on the rollup index pattern to limit:
* Available aggregation types
* Available fields for a particular aggregation
* Default and base interval for histogram aggregation
@@ -47,6 +47,6 @@ Limiting visualization editor options is done by [registering configs](public/vi
In Index Management, similar to system indices, rollup indices are hidden by default. A toggle is provided to show rollup indices and add a badge to the table rows. This is done by using Index Management's extension points.
-The toggle and badge are registered on client-side [here](public/extend_index_management/index.js).
+The toggle and badge are registered on client-side [here](../../../plugins/rollup/public/extend_index_management/index.js).
Additional data needed to filter rollup indices in Index Management is provided with a [data enricher](rollup_data_enricher.js).
diff --git a/x-pack/legacy/plugins/rollup/common/index.ts b/x-pack/legacy/plugins/rollup/common/index.ts
index 422980346220..526af055a3ef 100644
--- a/x-pack/legacy/plugins/rollup/common/index.ts
+++ b/x-pack/legacy/plugins/rollup/common/index.ts
@@ -16,24 +16,4 @@ export const PLUGIN = {
},
};
-export const CONFIG_ROLLUPS = 'rollups:enableIndexPatterns';
-
-export const API_BASE_PATH = '/api/rollup';
-
-export {
- UIM_APP_NAME,
- UIM_APP_LOAD,
- UIM_JOB_CREATE,
- UIM_JOB_DELETE,
- UIM_JOB_DELETE_MANY,
- UIM_JOB_START,
- UIM_JOB_START_MANY,
- UIM_JOB_STOP,
- UIM_JOB_STOP_MANY,
- UIM_SHOW_DETAILS_CLICK,
- UIM_DETAIL_PANEL_SUMMARY_TAB_CLICK,
- UIM_DETAIL_PANEL_TERMS_TAB_CLICK,
- UIM_DETAIL_PANEL_HISTOGRAM_TAB_CLICK,
- UIM_DETAIL_PANEL_METRICS_TAB_CLICK,
- UIM_DETAIL_PANEL_JSON_TAB_CLICK,
-} from './ui_metric';
+export * from '../../../../plugins/rollup/common';
diff --git a/x-pack/legacy/plugins/rollup/index.ts b/x-pack/legacy/plugins/rollup/index.ts
index 2c8363cc397f..f33ae7cfee0a 100644
--- a/x-pack/legacy/plugins/rollup/index.ts
+++ b/x-pack/legacy/plugins/rollup/index.ts
@@ -4,43 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { resolve } from 'path';
-import { i18n } from '@kbn/i18n';
import { PluginInitializerContext } from 'src/core/server';
import { RollupSetup } from '../../../plugins/rollup/server';
-import { PLUGIN, CONFIG_ROLLUPS } from './common';
+import { PLUGIN } from './common';
import { plugin } from './server';
export function rollup(kibana: any) {
return new kibana.Plugin({
id: PLUGIN.ID,
configPrefix: 'xpack.rollup',
- publicDir: resolve(__dirname, 'public'),
require: ['kibana', 'elasticsearch', 'xpack_main'],
- uiExports: {
- styleSheetPaths: resolve(__dirname, 'public/index.scss'),
- managementSections: ['plugins/rollup/legacy'],
- uiSettingDefaults: {
- [CONFIG_ROLLUPS]: {
- name: i18n.translate('xpack.rollupJobs.rollupIndexPatternsTitle', {
- defaultMessage: 'Enable rollup index patterns',
- }),
- value: true,
- description: i18n.translate('xpack.rollupJobs.rollupIndexPatternsDescription', {
- defaultMessage: `Enable the creation of index patterns which capture rollup indices,
- which in turn enable visualizations based on rollup data. Refresh
- the page to apply the changes.`,
- }),
- category: ['rollups'],
- },
- },
- indexManagement: ['plugins/rollup/legacy'],
- visualize: ['plugins/rollup/legacy'],
- search: ['plugins/rollup/legacy'],
- },
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 +29,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 3df8bd7c187d..78458c9218be 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/public/legacy.ts b/x-pack/legacy/plugins/rollup/public/legacy.ts
deleted file mode 100644
index 83945110c2c7..000000000000
--- a/x-pack/legacy/plugins/rollup/public/legacy.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * 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 { npSetup, npStart } from 'ui/new_platform';
-import { RollupPlugin } from './plugin';
-
-const plugin = new RollupPlugin();
-
-export const setup = plugin.setup(npSetup.core, npSetup.plugins);
-export const start = plugin.start(npStart.core, npStart.plugins);
diff --git a/x-pack/legacy/plugins/rollup/public/search/rollup_search_strategy.ts b/x-pack/legacy/plugins/rollup/public/search/rollup_search_strategy.ts
deleted file mode 100644
index 4709c0aa498f..000000000000
--- a/x-pack/legacy/plugins/rollup/public/search/rollup_search_strategy.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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 { HttpSetup } from 'src/core/public';
-import {
- SearchError,
- getSearchErrorType,
- IIndexPattern,
- SearchStrategyProvider,
- SearchResponse,
- SearchRequest,
-} from '../../../../../../src/plugins/data/public';
-
-function serializeFetchParams(searchRequests: SearchRequest[]) {
- return JSON.stringify(
- searchRequests.map(searchRequestWithFetchParams => {
- const indexPattern =
- searchRequestWithFetchParams.index.title || searchRequestWithFetchParams.index;
- const {
- body: { size, aggs, query: _query },
- } = searchRequestWithFetchParams;
-
- const query = {
- size,
- aggregations: aggs,
- query: _query,
- };
-
- return { index: indexPattern, query };
- })
- );
-}
-
-// Rollup search always returns 0 hits, but visualizations expect search responses
-// to return hits > 0, otherwise they do not render. We fake the number of hits here
-// by counting the number of aggregation buckets/values returned by rollup search.
-function shimHitsInFetchResponse(response: SearchResponse[]) {
- return response.map(result => {
- const buckets = result.aggregations
- ? Object.keys(result.aggregations).reduce((allBuckets, agg) => {
- return allBuckets.concat(
- result.aggregations[agg].buckets || [result.aggregations[agg].value] || []
- );
- }, [])
- : [];
- return buckets && buckets.length
- ? {
- ...result,
- hits: {
- ...result.hits,
- total: buckets.length,
- },
- }
- : result;
- });
-}
-
-export const getRollupSearchStrategy = (fetch: HttpSetup['fetch']): SearchStrategyProvider => ({
- id: 'rollup',
-
- search: ({ searchRequests }) => {
- // Serialize the fetch params into a format suitable for the body of an ES query.
- const serializedFetchParams = serializeFetchParams(searchRequests);
-
- const controller = new AbortController();
- const promise = fetch('../api/rollup/search', {
- signal: controller.signal,
- method: 'POST',
- body: serializedFetchParams,
- });
-
- return {
- searching: promise.then(shimHitsInFetchResponse).catch(error => {
- const {
- body: { statusCode, error: title, message },
- res: { url },
- } = error;
-
- // Format fetch error as a SearchError.
- const searchError = new SearchError({
- status: statusCode,
- title,
- message: `Rollup search error: ${message}`,
- path: url,
- type: getSearchErrorType({ message }) || '',
- });
-
- return Promise.reject(searchError);
- }),
- abort: () => controller.abort(),
- };
- },
-
- isViable: (indexPattern: IIndexPattern) => {
- if (!indexPattern) {
- return false;
- }
-
- return indexPattern.type === 'rollup';
- },
-});
diff --git a/x-pack/legacy/plugins/rollup/server/plugin.ts b/x-pack/legacy/plugins/rollup/server/plugin.ts
index 5f29ad160e05..05c22b030fff 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/.gitattributes b/x-pack/legacy/plugins/siem/.gitattributes
index f40e829b6545..a4071d39e63c 100644
--- a/x-pack/legacy/plugins/siem/.gitattributes
+++ b/x-pack/legacy/plugins/siem/.gitattributes
@@ -1,6 +1,5 @@
# Auto-collapse generated files in GitHub
# https://help.github.com/en/articles/customizing-how-changed-files-appear-on-github
x-pack/legacy/plugins/siem/public/graphql/types.ts linguist-generated=true
-x-pack/legacy/plugins/siem/server/graphql/types.ts linguist-generated=true
x-pack/legacy/plugins/siem/public/graphql/introspection.json linguist-generated=true
diff --git a/x-pack/legacy/plugins/siem/cypress.json b/x-pack/legacy/plugins/siem/cypress.json
deleted file mode 100644
index a0333a106814..000000000000
--- a/x-pack/legacy/plugins/siem/cypress.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "baseUrl": "http://localhost:5601",
- "defaultCommandTimeout": 120000,
- "screenshotsFolder": "../../../../target/kibana-siem/cypress/screenshots",
- "trashAssetsBeforeRuns": false,
- "video": false,
- "videosFolder": "../../../../target/kibana-siem/cypress/videos"
-}
diff --git a/x-pack/legacy/plugins/siem/index.ts b/x-pack/legacy/plugins/siem/index.ts
index 3773283555b3..6e03583dda69 100644
--- a/x-pack/legacy/plugins/siem/index.ts
+++ b/x-pack/legacy/plugins/siem/index.ts
@@ -6,11 +6,10 @@
import { i18n } from '@kbn/i18n';
import { resolve } from 'path';
-import { Server } from 'hapi';
import { Root } from 'joi';
-import { plugin } from './server';
-import { savedObjectMappings } from './server/saved_objects';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { savedObjectMappings } from '../../../plugins/siem/server/saved_objects';
import {
APP_ID,
@@ -23,15 +22,13 @@ import {
DEFAULT_INTERVAL_VALUE,
DEFAULT_FROM,
DEFAULT_TO,
- DEFAULT_SIGNALS_INDEX,
ENABLE_NEWS_FEED_SETTING,
NEWS_FEED_URL_SETTING,
NEWS_FEED_URL_SETTING_DEFAULT,
- SIGNALS_INDEX_KEY,
IP_REPUTATION_LINKS_SETTING,
IP_REPUTATION_LINKS_SETTING_DEFAULT,
-} from './common/constants';
-import { defaultIndexPattern } from './default_index_pattern';
+ DEFAULT_INDEX_PATTERN,
+} from '../../../plugins/siem/common/constants';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -102,7 +99,7 @@ export const siem = (kibana: any) => {
name: i18n.translate('xpack.siem.uiSettings.defaultIndexLabel', {
defaultMessage: 'Elasticsearch indices',
}),
- value: defaultIndexPattern,
+ value: DEFAULT_INDEX_PATTERN,
description: i18n.translate('xpack.siem.uiSettings.defaultIndexDescription', {
defaultMessage:
'Comma-delimited list of Elasticsearch indices from which the SIEM app collects events.
',
@@ -162,31 +159,12 @@ export const siem = (kibana: any) => {
},
mappings: savedObjectMappings,
},
- init(server: Server) {
- const { coreContext, env, setup, start } = server.newPlatform;
- const initializerContext = { ...coreContext, env };
- const __legacy = {
- config: server.config,
- route: server.route.bind(server),
- };
-
- // @ts-ignore-next-line: NewPlatform shim is too loosely typed
- const pluginInstance = plugin(initializerContext);
- // @ts-ignore-next-line: NewPlatform shim is too loosely typed
- pluginInstance.setup(setup.core, setup.plugins, __legacy);
- // @ts-ignore-next-line: NewPlatform shim is too loosely typed
- pluginInstance.start(start.core, start.plugins);
- },
config(Joi: Root) {
- // See x-pack/plugins/siem/server/config.ts if you're adding another
- // value where the configuration has to be duplicated at the moment.
- // When we move over to the new platform completely this will be
- // removed and only server/config.ts should be used.
return Joi.object()
.keys({
enabled: Joi.boolean().default(true),
- [SIGNALS_INDEX_KEY]: Joi.string().default(DEFAULT_SIGNALS_INDEX),
})
+ .unknown(true)
.default();
},
});
diff --git a/x-pack/legacy/plugins/siem/package.json b/x-pack/legacy/plugins/siem/package.json
index 472a473842f0..3a93beef963a 100644
--- a/x-pack/legacy/plugins/siem/package.json
+++ b/x-pack/legacy/plugins/siem/package.json
@@ -1,16 +1,10 @@
{
"author": "Elastic",
- "name": "siem",
+ "name": "siem-legacy-ui",
"version": "8.0.0",
"private": true,
"license": "Elastic-License",
- "scripts": {
- "extract-mitre-attacks": "node scripts/extract_tactics_techniques_mitre.js & node ../../../../scripts/eslint ./public/pages/detection_engine/mitre/mitre_tactics_techniques.ts --fix",
- "build-graphql-types": "node scripts/generate_types_from_graphql.js",
- "cypress:open": "../../../node_modules/.bin/cypress open",
- "cypress:run": "../../../node_modules/.bin/cypress run --spec ./cypress/integration/**/*.spec.ts --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./reporter_config.json; status=$?; ../../../node_modules/.bin/mochawesome-merge --reportDir ../../../../target/kibana-siem/cypress/results > ../../../../target/kibana-siem/cypress/results/output.json; ../../../../node_modules/.bin/marge ../../../../target/kibana-siem/cypress/results/output.json --reportDir ../../../../target/kibana-siem/cypress/results; mkdir -p ../../../../target/junit && cp ../../../../target/kibana-siem/cypress/results/*.xml ../../../../target/junit/ && exit $status;",
- "cypress:run-as-ci": "node ../../../../scripts/functional_tests --config ../../../test/siem_cypress/config.ts"
- },
+ "scripts": {},
"devDependencies": {
"@types/lodash": "^4.14.110",
"@types/js-yaml": "^3.12.1",
diff --git a/x-pack/legacy/plugins/siem/public/app/app.tsx b/x-pack/legacy/plugins/siem/public/app/app.tsx
index 7413aeab549d..44c1c923cd6e 100644
--- a/x-pack/legacy/plugins/siem/public/app/app.tsx
+++ b/x-pack/legacy/plugins/siem/public/app/app.tsx
@@ -20,7 +20,7 @@ import { pluck } from 'rxjs/operators';
import { KibanaContextProvider, useKibana, useUiSetting$ } from '../lib/kibana';
import { Storage } from '../../../../../../src/plugins/kibana_utils/public';
-import { DEFAULT_DARK_MODE } from '../../common/constants';
+import { DEFAULT_DARK_MODE } from '../../../../../plugins/siem/common/constants';
import { ErrorToastDispatcher } from '../components/error_toast_dispatcher';
import { compose } from '../lib/compose/kibana_compose';
import { AppFrontendLibs, AppApolloClient } from '../lib/lib';
diff --git a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx
index 587002c24d52..778adc708d90 100644
--- a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx
@@ -6,11 +6,11 @@
import React, { useEffect, useCallback, useMemo } from 'react';
import numeral from '@elastic/numeral';
+import { DEFAULT_NUMBER_FORMAT } from '../../../../../../plugins/siem/common/constants';
import { AlertsComponentsQueryProps } from './types';
import { AlertsTable } from './alerts_table';
import * as i18n from './translations';
import { useUiSetting$ } from '../../lib/kibana';
-import { DEFAULT_NUMBER_FORMAT } from '../../../common/constants';
import { MatrixHistogramContainer } from '../matrix_histogram';
import { histogramConfigs } from './histogram_configs';
import { MatrixHisrogramConfigs } from '../matrix_histogram/types';
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
index 272c41833f36..635d48cca10f 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
@@ -4,15 +4,29 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { shallow, ShallowWrapper } from 'enzyme';
+import { Chart, BarSeries, Axis, ScaleType } from '@elastic/charts';
+import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
+import { mount, ReactWrapper, shallow, ShallowWrapper } from 'enzyme';
import React from 'react';
+import { ThemeProvider } from 'styled-components';
+
+import { escapeDataProviderId } from '../drag_and_drop/helpers';
+import { TestProviders } from '../../mock';
import { BarChartBaseComponent, BarChartComponent } from './barchart';
import { ChartSeriesData } from './common';
-import { Chart, BarSeries, Axis, ScaleType } from '@elastic/charts';
jest.mock('../../lib/kibana');
+jest.mock('uuid', () => {
+ return {
+ v1: jest.fn(() => 'uuid.v1()'),
+ v4: jest.fn(() => 'uuid.v4()'),
+ };
+});
+
+const theme = () => ({ eui: euiDarkVars, darkMode: true });
+
const customHeight = '100px';
const customWidth = '120px';
const chartDataSets = [
@@ -116,6 +130,19 @@ const mockConfig = {
customHeight: 324,
};
+// Suppress warnings about "react-beautiful-dnd"
+/* eslint-disable no-console */
+const originalError = console.error;
+const originalWarn = console.warn;
+beforeAll(() => {
+ console.warn = jest.fn();
+ console.error = jest.fn();
+});
+afterAll(() => {
+ console.error = originalError;
+ console.warn = originalWarn;
+});
+
describe('BarChartBaseComponent', () => {
let shallowWrapper: ShallowWrapper;
const mockBarChartData: ChartSeriesData[] = [
@@ -280,6 +307,91 @@ describe.each(chartDataSets)('BarChart with valid data [%o]', data => {
expect(shallowWrapper.find('BarChartBase')).toHaveLength(1);
expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0);
});
+
+ it('it does NOT render a draggable legend because stackByField is not provided', () => {
+ expect(shallowWrapper.find('[data-test-subj="draggable-legend"]').exists()).toBe(false);
+ });
+});
+
+describe.each(chartDataSets)('BarChart with stackByField', () => {
+ let wrapper: ReactWrapper;
+
+ const data = [
+ {
+ key: 'python.exe',
+ value: [
+ {
+ x: 1586754900000,
+ y: 9675,
+ g: 'python.exe',
+ },
+ ],
+ },
+ {
+ key: 'kernel',
+ value: [
+ {
+ x: 1586754900000,
+ y: 8708,
+ g: 'kernel',
+ },
+ {
+ x: 1586757600000,
+ y: 9282,
+ g: 'kernel',
+ },
+ ],
+ },
+ {
+ key: 'sshd',
+ value: [
+ {
+ x: 1586754900000,
+ y: 5907,
+ g: 'sshd',
+ },
+ ],
+ },
+ ];
+
+ const expectedColors = ['#1EA593', '#2B70F7', '#CE0060'];
+
+ const stackByField = 'process.name';
+
+ beforeAll(() => {
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ it('it renders a draggable legend', () => {
+ expect(wrapper.find('[data-test-subj="draggable-legend"]').exists()).toBe(true);
+ });
+
+ expectedColors.forEach((color, i) => {
+ test(`it renders the expected legend color ${color} for legend item ${i}`, () => {
+ expect(wrapper.find(`div [color="${color}"]`).exists()).toBe(true);
+ });
+ });
+
+ data.forEach(datum => {
+ test(`it renders the expected draggable legend text for datum ${datum.key}`, () => {
+ const dataProviderId = `draggableId.content.draggable-legend-item-uuid_v4()-${escapeDataProviderId(
+ stackByField
+ )}-${escapeDataProviderId(datum.key)}`;
+
+ expect(
+ wrapper
+ .find(`div [data-rbd-draggable-id="${dataProviderId}"]`)
+ .first()
+ .text()
+ ).toEqual(datum.key);
+ });
+ });
});
describe.each(chartHolderDataSets)('BarChart with invalid data [%o]', data => {
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
index 2ae0e05850a3..64d15cd6731c 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
@@ -4,13 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React from 'react';
+import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import React, { useMemo } from 'react';
import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/charts';
import { getOr, get, isNumber } from 'lodash/fp';
import deepmerge from 'deepmerge';
+import uuid from 'uuid';
+import styled from 'styled-components';
-import { useThrottledResizeObserver } from '../utils';
+import { escapeDataProviderId } from '../drag_and_drop/helpers';
import { useTimeZone } from '../../lib/kibana';
+import { defaultLegendColors } from '../matrix_histogram/utils';
+import { useThrottledResizeObserver } from '../utils';
+
import { ChartPlaceHolder } from './chart_place_holder';
import {
chartDefaultSettings,
@@ -22,6 +28,12 @@ import {
WrappedByAutoSizer,
useTheme,
} from './common';
+import { DraggableLegend } from './draggable_legend';
+import { LegendItem } from './draggable_legend_item';
+
+const LegendFlexItem = styled(EuiFlexItem)`
+ overview: hidden;
+`;
const checkIfAllTheDataInTheSeriesAreValid = (series: ChartSeriesData): series is ChartSeriesData =>
series != null &&
@@ -38,12 +50,14 @@ const checkIfAnyValidSeriesExist = (
// Bar chart rotation: https://ela.st/chart-rotations
export const BarChartBaseComponent = ({
data,
+ forceHiddenLegend = false,
...chartConfigs
}: {
data: ChartSeriesData[];
width: string | null | undefined;
height: string | null | undefined;
configs?: ChartSeriesConfigs | undefined;
+ forceHiddenLegend?: boolean;
}) => {
const theme = useTheme();
const timeZone = useTimeZone();
@@ -59,10 +73,10 @@ export const BarChartBaseComponent = ({
return chartConfigs.width && chartConfigs.height ? (
-
+
{data.map(series => {
const barSeriesKey = series.key;
- return checkIfAllTheDataInTheSeriesAreValid ? (
+ return checkIfAllTheDataInTheSeriesAreValid(series) ? (
= ({ barChart, configs }) => {
+const NO_LEGEND_DATA: LegendItem[] = [];
+
+export const BarChartComponent: React.FC = ({
+ barChart,
+ configs,
+ stackByField,
+}) => {
const { ref: measureRef, width, height } = useThrottledResizeObserver();
+ const legendItems: LegendItem[] = useMemo(
+ () =>
+ barChart != null && stackByField != null
+ ? barChart.map((d, i) => ({
+ color: d.color ?? i < defaultLegendColors.length ? defaultLegendColors[i] : undefined,
+ dataProviderId: escapeDataProviderId(
+ `draggable-legend-item-${uuid.v4()}-${stackByField}-${d.key}`
+ ),
+ field: stackByField,
+ value: d.key,
+ }))
+ : NO_LEGEND_DATA,
+ [barChart, stackByField]
+ );
+
const customHeight = get('customHeight', configs);
const customWidth = get('customWidth', configs);
const chartHeight = getChartHeight(customHeight, height);
const chartWidth = getChartWidth(customWidth, width);
return checkIfAnyValidSeriesExist(barChart) ? (
-
-
-
+
+
+
+
+
+
+
+
+
+
) : (
);
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx
index d8429cba1b4f..c7b40c50ffde 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx
@@ -19,8 +19,8 @@ import {
import React, { useMemo } from 'react';
import styled from 'styled-components';
+import { DEFAULT_DARK_MODE } from '../../../../../../plugins/siem/common/constants';
import { useUiSetting } from '../../lib/kibana';
-import { DEFAULT_DARK_MODE } from '../../../common/constants';
export const defaultChartHeight = '100%';
export const defaultChartWidth = '100%';
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.test.tsx
new file mode 100644
index 000000000000..0da0c2bdc35f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.test.tsx
@@ -0,0 +1,149 @@
+/*
+ * 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 euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
+import { mount, ReactWrapper } from 'enzyme';
+import React from 'react';
+import { ThemeProvider } from 'styled-components';
+
+import { TestProviders } from '../../mock';
+
+import { MIN_LEGEND_HEIGHT, DraggableLegend } from './draggable_legend';
+import { LegendItem } from './draggable_legend_item';
+
+const theme = () => ({ eui: euiDarkVars, darkMode: true });
+
+const allOthersDataProviderId =
+ 'draggable-legend-item-527adabe-8e1c-4a1f-965c-2f3d65dda9e1-event_dataset-All others';
+
+const legendItems: LegendItem[] = [
+ {
+ color: '#1EA593',
+ dataProviderId: 'draggable-legend-item-3207fda7-d008-402a-86a0-8ad632081bad-event_dataset-flow',
+ field: 'event.dataset',
+ value: 'flow',
+ },
+ {
+ color: '#2B70F7',
+ dataProviderId:
+ 'draggable-legend-item-83f6c824-811d-4ec8-b373-eba2b0de6398-event_dataset-suricata_eve',
+ field: 'event.dataset',
+ value: 'suricata.eve',
+ },
+ {
+ color: '#CE0060',
+ dataProviderId:
+ 'draggable-legend-item-ec57bb8f-82cd-4e07-bd38-1d11b3f0ee5f-event_dataset-traefik_access',
+ field: 'event.dataset',
+ value: 'traefik.access',
+ },
+ {
+ color: '#38007E',
+ dataProviderId:
+ 'draggable-legend-item-25d5fcd6-87ba-46b5-893e-c655d7d504e3-event_dataset-esensor',
+ field: 'event.dataset',
+ value: 'esensor',
+ },
+ {
+ color: '#F37020',
+ dataProviderId: allOthersDataProviderId,
+ field: 'event.dataset',
+ value: 'All others',
+ },
+];
+
+describe('DraggableLegend', () => {
+ const height = 400;
+
+ // Suppress warnings about "react-beautiful-dnd"
+ /* eslint-disable no-console */
+ const originalError = console.error;
+ const originalWarn = console.warn;
+ beforeAll(() => {
+ console.warn = jest.fn();
+ console.error = jest.fn();
+ });
+ afterAll(() => {
+ console.error = originalError;
+ console.warn = originalWarn;
+ });
+
+ describe('rendering', () => {
+ let wrapper: ReactWrapper;
+
+ beforeEach(() => {
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ it(`renders a container with the specified non-zero 'height'`, () => {
+ expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule(
+ 'height',
+ `${height}px`
+ );
+ });
+
+ it('scrolls when necessary', () => {
+ expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule(
+ 'overflow',
+ 'auto'
+ );
+ });
+
+ it('renders the legend items', () => {
+ legendItems.forEach(item =>
+ expect(
+ wrapper
+ .find(
+ item.dataProviderId !== allOthersDataProviderId
+ ? `[data-test-subj="legend-item-${item.dataProviderId}"]`
+ : '[data-test-subj="all-others-legend-item"]'
+ )
+ .first()
+ .text()
+ ).toEqual(item.value)
+ );
+ });
+
+ it('renders a spacer for every legend item', () => {
+ expect(wrapper.find('[data-test-subj="draggable-legend-spacer"]').hostNodes().length).toEqual(
+ legendItems.length
+ );
+ });
+ });
+
+ it('does NOT render the legend when an empty collection of legendItems is provided', () => {
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="draggable-legend"]').exists()).toBe(false);
+ });
+
+ it(`renders a legend with the minimum height when 'height' is zero`, () => {
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule(
+ 'height',
+ `${MIN_LEGEND_HEIGHT}px`
+ );
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.tsx b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.tsx
new file mode 100644
index 000000000000..ef3fbb8780d1
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.tsx
@@ -0,0 +1,68 @@
+/*
+ * 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 { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui';
+import { rgba } from 'polished';
+import React from 'react';
+import styled from 'styled-components';
+
+import { DraggableLegendItem, LegendItem } from './draggable_legend_item';
+
+export const MIN_LEGEND_HEIGHT = 175;
+
+const DraggableLegendContainer = styled.div<{ height: number }>`
+ height: ${({ height }) => `${height}px`};
+ overflow: auto;
+ scrollbar-width: thin;
+ width: 165px;
+
+ &::-webkit-scrollbar {
+ height: ${({ theme }) => theme.eui.euiScrollBar};
+ width: ${({ theme }) => theme.eui.euiScrollBar};
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background-clip: content-box;
+ background-color: ${({ theme }) => rgba(theme.eui.euiColorDarkShade, 0.5)};
+ border: ${({ theme }) => theme.eui.euiScrollBarCorner} solid transparent;
+ }
+
+ &::-webkit-scrollbar-corner,
+ &::-webkit-scrollbar-track {
+ background-color: transparent;
+ }
+`;
+
+const DraggableLegendComponent: React.FC<{
+ height: number;
+ legendItems: LegendItem[];
+}> = ({ height, legendItems }) => {
+ if (legendItems.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+
+ {legendItems.map(item => (
+
+
+
+
+ ))}
+
+
+
+ );
+};
+
+DraggableLegendComponent.displayName = 'DraggableLegendComponent';
+
+export const DraggableLegend = React.memo(DraggableLegendComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.test.tsx
new file mode 100644
index 000000000000..581952a8415f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.test.tsx
@@ -0,0 +1,143 @@
+/*
+ * 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 euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
+import { mount, ReactWrapper } from 'enzyme';
+import React from 'react';
+import { ThemeProvider } from 'styled-components';
+
+import { TestProviders } from '../../mock';
+
+import { DraggableLegendItem, LegendItem } from './draggable_legend_item';
+
+const theme = () => ({ eui: euiDarkVars, darkMode: true });
+
+describe('DraggableLegendItem', () => {
+ // Suppress warnings about "react-beautiful-dnd"
+ /* eslint-disable no-console */
+ const originalError = console.error;
+ const originalWarn = console.warn;
+ beforeAll(() => {
+ console.warn = jest.fn();
+ console.error = jest.fn();
+ });
+ afterAll(() => {
+ console.error = originalError;
+ console.warn = originalWarn;
+ });
+
+ describe('rendering a regular (non "All others") legend item', () => {
+ const legendItem: LegendItem = {
+ color: '#1EA593',
+ dataProviderId:
+ 'draggable-legend-item-3207fda7-d008-402a-86a0-8ad632081bad-event_dataset-flow',
+ field: 'event.dataset',
+ value: 'flow',
+ };
+
+ let wrapper: ReactWrapper;
+
+ beforeEach(() => {
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ it('renders a colored circle with the expected legend item color', () => {
+ expect(
+ wrapper
+ .find('[data-test-subj="legend-color"]')
+ .first()
+ .props().color
+ ).toEqual(legendItem.color);
+ });
+
+ it('renders draggable legend item text', () => {
+ expect(
+ wrapper
+ .find(`[data-test-subj="legend-item-${legendItem.dataProviderId}"]`)
+ .first()
+ .text()
+ ).toEqual(legendItem.value);
+ });
+
+ it('does NOT render a non-draggable "All others" legend item', () => {
+ expect(wrapper.find(`[data-test-subj="all-others-legend-item"]`).exists()).toBe(false);
+ });
+ });
+
+ describe('rendering an "All others" legend item', () => {
+ const allOthersLegendItem: LegendItem = {
+ color: '#F37020',
+ dataProviderId:
+ 'draggable-legend-item-527adabe-8e1c-4a1f-965c-2f3d65dda9e1-event_dataset-All others',
+ field: 'event.dataset',
+ value: 'All others',
+ };
+
+ let wrapper: ReactWrapper;
+
+ beforeEach(() => {
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ it('renders a colored circle with the expected legend item color', () => {
+ expect(
+ wrapper
+ .find('[data-test-subj="legend-color"]')
+ .first()
+ .props().color
+ ).toEqual(allOthersLegendItem.color);
+ });
+
+ it('does NOT render a draggable legend item', () => {
+ expect(
+ wrapper
+ .find(`[data-test-subj="legend-item-${allOthersLegendItem.dataProviderId}"]`)
+ .exists()
+ ).toBe(false);
+ });
+
+ it('renders NON-draggable `All others` legend item text', () => {
+ expect(
+ wrapper
+ .find(`[data-test-subj="all-others-legend-item"]`)
+ .first()
+ .text()
+ ).toEqual(allOthersLegendItem.value);
+ });
+ });
+
+ it('does NOT render a colored circle when the legend item has no color', () => {
+ const noColorLegendItem: LegendItem = {
+ // no `color` attribute for this `LegendItem`!
+ dataProviderId:
+ 'draggable-legend-item-3207fda7-d008-402a-86a0-8ad632081bad-event_dataset-flow',
+ field: 'event.dataset',
+ value: 'flow',
+ };
+
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="legend-color"]').exists()).toBe(false);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.tsx b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.tsx
new file mode 100644
index 000000000000..cdda1733932d
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.tsx
@@ -0,0 +1,62 @@
+/*
+ * 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 { EuiFlexGroup, EuiFlexItem, EuiHealth, EuiText } from '@elastic/eui';
+import React from 'react';
+import styled from 'styled-components';
+
+import { DefaultDraggable } from '../draggables';
+
+import * as i18n from './translation';
+
+// The "All others" legend item is not draggable
+const AllOthers = styled.span`
+ padding-left: 7px;
+`;
+
+export interface LegendItem {
+ color?: string;
+ dataProviderId: string;
+ field: string;
+ value: string;
+}
+
+const DraggableLegendItemComponent: React.FC<{
+ legendItem: LegendItem;
+}> = ({ legendItem }) => {
+ const { color, dataProviderId, field, value } = legendItem;
+
+ return (
+
+
+ {color != null && (
+
+
+
+ )}
+
+
+ {value !== i18n.ALL_OTHERS ? (
+
+ ) : (
+ <>
+ {value}
+ >
+ )}
+
+
+
+ );
+};
+
+DraggableLegendItemComponent.displayName = 'DraggableLegendItemComponent';
+
+export const DraggableLegendItem = React.memo(DraggableLegendItemComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/translation.ts b/x-pack/legacy/plugins/siem/public/components/charts/translation.ts
index 341cb7782f87..891f59fc97bd 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/translation.ts
+++ b/x-pack/legacy/plugins/siem/public/components/charts/translation.ts
@@ -13,3 +13,7 @@ export const ALL_VALUES_ZEROS_TITLE = i18n.translate('xpack.siem.chart.dataAllVa
export const DATA_NOT_AVAILABLE_TITLE = i18n.translate('xpack.siem.chart.dataNotAvailableTitle', {
defaultMessage: 'Chart Data Not Available',
});
+
+export const ALL_OTHERS = i18n.translate('xpack.siem.chart.allOthersGroupingLabel', {
+ defaultMessage: 'All others',
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx
index 11db33fff6d7..248ae671550e 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx
@@ -27,6 +27,9 @@ import {
draggableIsField,
} from './helpers';
+// @ts-ignore
+window['__react-beautiful-dnd-disable-dev-warnings'] = true;
+
interface Props {
browserFields: BrowserFields;
children: React.ReactNode;
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx
index 11891afabbf3..cd9e1dc95ff0 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx
@@ -7,12 +7,13 @@
import { shallow } from 'enzyme';
import React from 'react';
import { MockedProvider } from 'react-apollo/test-utils';
+import { DraggableStateSnapshot, DraggingStyle } from 'react-beautiful-dnd';
import { mockBrowserFields, mocksSource } from '../../containers/source/mock';
import { TestProviders } from '../../mock';
import { mockDataProviders } from '../timeline/data_providers/mock/mock_data_providers';
import { DragDropContextWrapper } from './drag_drop_context_wrapper';
-import { DraggableWrapper, ConditionalPortal } from './draggable_wrapper';
+import { ConditionalPortal, DraggableWrapper, getStyle } from './draggable_wrapper';
import { useMountAppended } from '../../utils/use_mount_appended';
describe('DraggableWrapper', () => {
@@ -48,6 +49,36 @@ describe('DraggableWrapper', () => {
expect(wrapper.text()).toEqual(message);
});
+
+ test('it does NOT render hover actions when the mouse is NOT over the draggable wrapper', () => {
+ const wrapper = mount(
+
+
+
+ message} />
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(false);
+ });
+
+ test('it renders hover actions when the mouse is over the draggable wrapper', () => {
+ const wrapper = mount(
+
+
+
+ message} />
+
+
+
+ );
+
+ wrapper.simulate('mouseenter');
+ wrapper.update();
+ expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true);
+ });
});
describe('text truncation styling', () => {
@@ -100,4 +131,36 @@ describe('ConditionalPortal', () => {
expect(props.registerProvider.mock.calls.length).toEqual(1);
});
+
+ describe('getStyle', () => {
+ const style: DraggingStyle = {
+ boxSizing: 'border-box',
+ height: 10,
+ left: 1,
+ pointerEvents: 'none',
+ position: 'fixed',
+ transition: 'none',
+ top: 123,
+ width: 50,
+ zIndex: 9999,
+ };
+
+ it('returns a style with no transitionDuration when the snapshot is not drop animating', () => {
+ const snapshot: DraggableStateSnapshot = {
+ isDragging: true,
+ isDropAnimating: false, // <-- NOT drop animating
+ };
+
+ expect(getStyle(style, snapshot)).not.toHaveProperty('transitionDuration');
+ });
+
+ it('returns a style with a transitionDuration when the snapshot is drop animating', () => {
+ const snapshot: DraggableStateSnapshot = {
+ isDragging: true,
+ isDropAnimating: true, // <-- it is drop animating
+ };
+
+ expect(getStyle(style, snapshot)).toHaveProperty('transitionDuration', '0.00000001s');
+ });
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx
index 3a6a4de7984d..c7da5b5c5895 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx
@@ -4,12 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useCallback, useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
Draggable,
DraggableProvided,
DraggableStateSnapshot,
+ DraggingStyle,
Droppable,
+ NotDraggingStyle,
} from 'react-beautiful-dnd';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
@@ -18,6 +20,9 @@ import deepEqual from 'fast-deep-equal';
import { dragAndDropActions } from '../../store/drag_and_drop';
import { DataProvider } from '../timeline/data_providers/data_provider';
import { TruncatableText } from '../truncatable_text';
+import { WithHoverActions } from '../with_hover_actions';
+
+import { DraggableWrapperHoverContent } from './draggable_wrapper_hover_content';
import { getDraggableId, getDroppableId } from './helpers';
import { ProviderContainer } from './provider_container';
@@ -67,23 +72,42 @@ type RenderFunctionProp = (
state: DraggableStateSnapshot
) => React.ReactNode;
-interface OwnProps {
+interface Props {
dataProvider: DataProvider;
inline?: boolean;
render: RenderFunctionProp;
truncate?: boolean;
+ onFilterAdded?: () => void;
}
-type Props = OwnProps;
-
/**
* Wraps a draggable component to handle registration / unregistration of the
* data provider associated with the item being dropped
*/
+export const getStyle = (
+ style: DraggingStyle | NotDraggingStyle | undefined,
+ snapshot: DraggableStateSnapshot
+) => {
+ if (!snapshot.isDropAnimating) {
+ return style;
+ }
+
+ return {
+ ...style,
+ transitionDuration: '0.00000001s', // cannot be 0, but can be a very short duration
+ };
+};
+
export const DraggableWrapper = React.memo(
- ({ dataProvider, render, truncate }) => {
+ ({ dataProvider, onFilterAdded, render, truncate }) => {
+ const [showTopN, setShowTopN] = useState(false);
+ const toggleTopN = useCallback(() => {
+ setShowTopN(!showTopN);
+ }, [setShowTopN, showTopN]);
+
const [providerRegistered, setProviderRegistered] = useState(false);
+
const dispatch = useDispatch();
const registerProvider = useCallback(() => {
@@ -105,65 +129,90 @@ export const DraggableWrapper = React.memo(
[]
);
- return (
-
-
- (
-
-
-
(
+
+ ),
+ [dataProvider, onFilterAdded, showTopN, toggleTopN]
+ );
+
+ const renderContent = useCallback(
+ () => (
+
+
+ (
+
+
- {render(dataProvider, provided, snapshot)}
-
-
-
- )}
- >
- {droppableProvided => (
-
-
- {(provided, snapshot) => (
-
- {truncate && !snapshot.isDragging ? (
-
- {render(dataProvider, provided, snapshot)}
-
- ) : (
-
- {render(dataProvider, provided, snapshot)}
-
- )}
-
- )}
-
- {droppableProvided.placeholder}
-
- )}
-
-
-
+ {render(dataProvider, provided, snapshot)}
+
+
+
+ )}
+ >
+ {droppableProvided => (
+
+
+ {(provided, snapshot) => (
+
+ {truncate && !snapshot.isDragging ? (
+
+ {render(dataProvider, provided, snapshot)}
+
+ ) : (
+
+ {render(dataProvider, provided, snapshot)}
+
+ )}
+
+ )}
+
+ {droppableProvided.placeholder}
+
+ )}
+
+
+
+ ),
+ [dataProvider, render, registerProvider, truncate]
+ );
+
+ return (
+
);
},
(prevProps, nextProps) =>
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx
new file mode 100644
index 000000000000..f8b5eb7209ff
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx
@@ -0,0 +1,559 @@
+/*
+ * 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 { mount, ReactWrapper } from 'enzyme';
+import React from 'react';
+import { MockedProvider } from 'react-apollo/test-utils';
+
+import { mocksSource } from '../../containers/source/mock';
+import { wait } from '../../lib/helpers';
+import { useKibana } from '../../lib/kibana';
+import { TestProviders } from '../../mock';
+import { createKibanaCoreStartMock } from '../../mock/kibana_core';
+import { FilterManager } from '../../../../../../../src/plugins/data/public';
+import { TimelineContext } from '../timeline/timeline_context';
+
+import { DraggableWrapperHoverContent } from './draggable_wrapper_hover_content';
+
+jest.mock('../../lib/kibana');
+
+jest.mock('uuid', () => {
+ return {
+ v1: jest.fn(() => 'uuid.v1()'),
+ v4: jest.fn(() => 'uuid.v4()'),
+ };
+});
+
+const mockUiSettingsForFilterManager = createKibanaCoreStartMock().uiSettings;
+const field = 'process.name';
+const value = 'nice';
+
+describe('DraggableWrapperHoverContent', () => {
+ // Suppress warnings about "react-beautiful-dnd"
+ /* eslint-disable no-console */
+ const originalError = console.error;
+ const originalWarn = console.warn;
+ beforeAll(() => {
+ console.warn = jest.fn();
+ console.error = jest.fn();
+ });
+ afterAll(() => {
+ console.error = originalError;
+ console.warn = originalWarn;
+ });
+
+ /**
+ * The tests for "Filter for value" and "Filter out value" are similar enough
+ * to combine them into "table tests" using this array
+ */
+ const forOrOut = ['for', 'out'];
+
+ forOrOut.forEach(hoverAction => {
+ describe(`Filter ${hoverAction} value`, () => {
+ test(`it renders the 'Filter ${hoverAction} value' button when showTopN is false`, () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test(`it does NOT render the 'Filter ${hoverAction} value' button when showTopN is true`, () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ describe('when run in the context of a timeline', () => {
+ let filterManager: FilterManager;
+ let wrapper: ReactWrapper;
+ let onFilterAdded: () => void;
+
+ beforeEach(() => {
+ filterManager = new FilterManager(mockUiSettingsForFilterManager);
+ filterManager.addFilters = jest.fn();
+ onFilterAdded = jest.fn();
+
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ test('when clicked, it adds a filter to the timeline when running in the context of a timeline', () => {
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(filterManager.addFilters).toBeCalledWith({
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: hoverAction === 'out' ? true : false,
+ params: { query: 'nice' },
+ type: 'phrase',
+ value: 'nice',
+ },
+ query: { match: { 'process.name': { query: 'nice', type: 'phrase' } } },
+ });
+ });
+
+ test('when clicked, invokes onFilterAdded when running in the context of a timeline', () => {
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(onFilterAdded).toBeCalled();
+ });
+ });
+
+ describe('when NOT run in the context of a timeline', () => {
+ let wrapper: ReactWrapper;
+ let onFilterAdded: () => void;
+ const kibana = useKibana();
+
+ beforeEach(() => {
+ kibana.services.data.query.filterManager.addFilters = jest.fn();
+ onFilterAdded = jest.fn();
+
+ wrapper = mount(
+
+
+
+ );
+ });
+
+ test('when clicked, it adds a filter to the global filters when NOT running in the context of a timeline', () => {
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(kibana.services.data.query.filterManager.addFilters).toBeCalledWith({
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: hoverAction === 'out' ? true : false,
+ params: { query: 'nice' },
+ type: 'phrase',
+ value: 'nice',
+ },
+ query: { match: { 'process.name': { query: 'nice', type: 'phrase' } } },
+ });
+ });
+
+ test('when clicked, invokes onFilterAdded when NOT running in the context of a timeline', () => {
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(onFilterAdded).toBeCalled();
+ });
+ });
+
+ describe('an empty string value when run in the context of a timeline', () => {
+ let filterManager: FilterManager;
+ let wrapper: ReactWrapper;
+ let onFilterAdded: () => void;
+
+ beforeEach(() => {
+ filterManager = new FilterManager(mockUiSettingsForFilterManager);
+ filterManager.addFilters = jest.fn();
+ onFilterAdded = jest.fn();
+
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ const expectedFilterTypeDescription =
+ hoverAction === 'for' ? 'a "NOT exists"' : 'an "exists"';
+ test(`when clicked, it adds ${expectedFilterTypeDescription} filter to the timeline when run in the context of a timeline`, () => {
+ const expected =
+ hoverAction === 'for'
+ ? {
+ exists: { field: 'process.name' },
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: true,
+ type: 'exists',
+ value: 'exists',
+ },
+ }
+ : {
+ exists: { field: 'process.name' },
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: false,
+ type: 'exists',
+ value: 'exists',
+ },
+ };
+
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(filterManager.addFilters).toBeCalledWith(expected);
+ });
+ });
+
+ describe('an empty string value when NOT run in the context of a timeline', () => {
+ let wrapper: ReactWrapper;
+ let onFilterAdded: () => void;
+ const kibana = useKibana();
+
+ beforeEach(() => {
+ kibana.services.data.query.filterManager.addFilters = jest.fn();
+ onFilterAdded = jest.fn();
+
+ wrapper = mount(
+
+
+
+ );
+ });
+
+ const expectedFilterTypeDescription =
+ hoverAction === 'for' ? 'a "NOT exists"' : 'an "exists"';
+ test(`when clicked, it adds ${expectedFilterTypeDescription} filter to the global filters when NOT running in the context of a timeline`, () => {
+ const expected =
+ hoverAction === 'for'
+ ? {
+ exists: { field: 'process.name' },
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: true,
+ type: 'exists',
+ value: 'exists',
+ },
+ }
+ : {
+ exists: { field: 'process.name' },
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: false,
+ type: 'exists',
+ value: 'exists',
+ },
+ };
+
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(kibana.services.data.query.filterManager.addFilters).toBeCalledWith(expected);
+ });
+ });
+ });
+ });
+
+ describe('Top N', () => {
+ test(`it renders the 'Show top field' button when showTopN is false and an aggregatable string field is provided`, async () => {
+ const aggregatableStringField = 'cloud.account.id';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait(); // https://github.com/apollographql/react-apollo/issues/1711
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="show-top-field"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test(`it renders the 'Show top field' button when showTopN is false and a whitelisted signal field is provided`, async () => {
+ const whitelistedField = 'signal.rule.name';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="show-top-field"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test(`it does NOT render the 'Show top field' button when showTopN is false and a field not known to BrowserFields is provided`, async () => {
+ const notKnownToBrowserFields = 'unknown.field';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="show-top-field"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test(`invokes the toggleTopN function when the 'Show top field' button is clicked`, async () => {
+ const toggleTopN = jest.fn();
+ const whitelistedField = 'signal.rule.name';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ wrapper
+ .find('[data-test-subj="show-top-field"]')
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(toggleTopN).toBeCalled();
+ });
+
+ test(`it does NOT render the Top N histogram when when showTopN is false`, async () => {
+ const whitelistedField = 'signal.rule.name';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="eventsByDatasetOverviewPanel"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test(`it does NOT render the 'Show top field' button when showTopN is true`, async () => {
+ const whitelistedField = 'signal.rule.name';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="show-top-field"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test(`it renders the Top N histogram when when showTopN is true`, async () => {
+ const whitelistedField = 'signal.rule.name';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="eventsByDatasetOverview-uuid.v4()Panel"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+ });
+
+ describe('Copy to Clipboard', () => {
+ test(`it renders the 'Copy to Clipboard' button when showTopN is false`, () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find(`[data-test-subj="copy-to-clipboard"]`)
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test(`it does NOT render the 'Copy to Clipboard' button when showTopN is true`, () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find(`[data-test-subj="copy-to-clipboard"]`)
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.tsx
new file mode 100644
index 000000000000..40725bea498f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.tsx
@@ -0,0 +1,145 @@
+/*
+ * 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 { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
+import React, { useCallback, useMemo } from 'react';
+
+import { getAllFieldsByName, WithSource } from '../../containers/source';
+import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard';
+import { useKibana } from '../../lib/kibana';
+import { createFilter } from '../page/add_filter_to_global_search_bar';
+import { useTimelineContext } from '../timeline/timeline_context';
+import { StatefulTopN } from '../top_n';
+
+import { allowTopN } from './helpers';
+import * as i18n from './translations';
+
+interface Props {
+ field: string;
+ onFilterAdded?: () => void;
+ showTopN: boolean;
+ toggleTopN: () => void;
+ value?: string[] | string | null;
+}
+
+const DraggableWrapperHoverContentComponent: React.FC = ({
+ field,
+ onFilterAdded,
+ showTopN,
+ toggleTopN,
+ value,
+}) => {
+ const kibana = useKibana();
+ const { filterManager: timelineFilterManager } = useTimelineContext();
+ const filterManager = useMemo(() => kibana.services.data.query.filterManager, [
+ kibana.services.data.query.filterManager,
+ ]);
+
+ const filterForValue = useCallback(() => {
+ const filter =
+ value?.length === 0 ? createFilter(field, undefined) : createFilter(field, value);
+ const activeFilterManager = timelineFilterManager ?? filterManager;
+
+ if (activeFilterManager != null) {
+ activeFilterManager.addFilters(filter);
+
+ if (onFilterAdded != null) {
+ onFilterAdded();
+ }
+ }
+ }, [field, value, timelineFilterManager, filterManager, onFilterAdded]);
+
+ const filterOutValue = useCallback(() => {
+ const filter =
+ value?.length === 0 ? createFilter(field, null, false) : createFilter(field, value, true);
+ const activeFilterManager = timelineFilterManager ?? filterManager;
+
+ if (activeFilterManager != null) {
+ activeFilterManager.addFilters(filter);
+
+ if (onFilterAdded != null) {
+ onFilterAdded();
+ }
+ }
+ }, [field, value, timelineFilterManager, filterManager, onFilterAdded]);
+
+ return (
+ <>
+ {!showTopN && value != null && (
+
+
+
+ )}
+
+ {!showTopN && value != null && (
+
+
+
+ )}
+
+
+ {({ browserFields }) => (
+ <>
+ {allowTopN({
+ browserField: getAllFieldsByName(browserFields)[field],
+ fieldName: field,
+ }) && (
+ <>
+ {!showTopN && (
+
+
+
+ )}
+
+ {showTopN && (
+
+ )}
+ >
+ )}
+ >
+ )}
+
+
+ {!showTopN && (
+
+
+
+ )}
+ >
+ );
+};
+
+DraggableWrapperHoverContentComponent.displayName = 'DraggableWrapperHoverContentComponent';
+
+export const DraggableWrapperHoverContent = React.memo(DraggableWrapperHoverContentComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.test.ts b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.test.ts
index af4b9b280f3c..753fa5b54ead 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.test.ts
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.test.ts
@@ -4,7 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { omit } from 'lodash/fp';
+
import {
+ allowTopN,
destinationIsTimelineButton,
destinationIsTimelineColumns,
destinationIsTimelineProviders,
@@ -717,4 +720,96 @@ describe('helpers', () => {
expect(escaped).toEqual('hello.how.are.you?');
});
});
+
+ describe('#allowTopN', () => {
+ const aggregatableAllowedType = {
+ category: 'cloud',
+ description:
+ 'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.',
+ example: '666777888999',
+ indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+ name: 'cloud.account.id',
+ searchable: true,
+ type: 'string',
+ aggregatable: true,
+ format: '',
+ };
+
+ test('it returns true for an aggregatable field that is an allowed type', () => {
+ expect(
+ allowTopN({
+ browserField: aggregatableAllowedType,
+ fieldName: aggregatableAllowedType.name,
+ })
+ ).toBe(true);
+ });
+
+ test('it returns true for a whitelisted non-BrowserField', () => {
+ expect(
+ allowTopN({
+ browserField: undefined,
+ fieldName: 'signal.rule.name',
+ })
+ ).toBe(true);
+ });
+
+ test('it returns false for a NON-aggregatable field that is an allowed type', () => {
+ const nonAggregatableAllowedType = {
+ ...aggregatableAllowedType,
+ aggregatable: false,
+ };
+
+ expect(
+ allowTopN({
+ browserField: nonAggregatableAllowedType,
+ fieldName: nonAggregatableAllowedType.name,
+ })
+ ).toBe(false);
+ });
+
+ test('it returns false for a aggregatable field that is NOT an allowed type', () => {
+ const aggregatableNotAllowedType = {
+ ...aggregatableAllowedType,
+ type: 'not-an-allowed-type',
+ };
+
+ expect(
+ allowTopN({
+ browserField: aggregatableNotAllowedType,
+ fieldName: aggregatableNotAllowedType.name,
+ })
+ ).toBe(false);
+ });
+
+ test('it returns false if the BrowserField is missing the aggregatable property', () => {
+ const missingAggregatable = omit('aggregatable', aggregatableAllowedType);
+
+ expect(
+ allowTopN({
+ browserField: missingAggregatable,
+ fieldName: missingAggregatable.name,
+ })
+ ).toBe(false);
+ });
+
+ test('it returns false if the BrowserField is missing the type property', () => {
+ const missingType = omit('type', aggregatableAllowedType);
+
+ expect(
+ allowTopN({
+ browserField: missingType,
+ fieldName: missingType.name,
+ })
+ ).toBe(false);
+ });
+
+ test('it returns false for a non-whitelisted field when a BrowserField is not provided', () => {
+ expect(
+ allowTopN({
+ browserField: undefined,
+ fieldName: 'non-whitelisted',
+ })
+ ).toBe(false);
+ });
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts
index 82ddd2c9f29d..cd3d7cc68d53 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts
@@ -9,7 +9,7 @@ import { DropResult } from 'react-beautiful-dnd';
import { Dispatch } from 'redux';
import { ActionCreator } from 'typescript-fsa';
-import { BrowserFields, getAllFieldsByName } from '../../containers/source';
+import { BrowserField, BrowserFields, getAllFieldsByName } from '../../containers/source';
import { IdToDataProvider } from '../../store/drag_and_drop/model';
import { ColumnHeaderOptions } from '../../store/timeline/model';
import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants';
@@ -227,3 +227,98 @@ export const IS_DRAGGING_CLASS_NAME = 'is-dragging';
/** This class is added to the document body while timeline field dragging */
export const IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME = 'is-timeline-field-dragging';
+
+export const allowTopN = ({
+ browserField,
+ fieldName,
+}: {
+ browserField: Partial | undefined;
+ fieldName: string;
+}): boolean => {
+ const isAggregatable = browserField?.aggregatable ?? false;
+ const fieldType = browserField?.type ?? '';
+ const isAllowedType = [
+ 'boolean',
+ 'geo-point',
+ 'geo-shape',
+ 'ip',
+ 'keyword',
+ 'number',
+ 'numeric',
+ 'string',
+ ].includes(fieldType);
+
+ // TODO: remove this explicit whitelist when the ECS documentation includes signals
+ const isWhitelistedNonBrowserField = [
+ 'signal.ancestors.depth',
+ 'signal.ancestors.id',
+ 'signal.ancestors.rule',
+ 'signal.ancestors.type',
+ 'signal.original_event.action',
+ 'signal.original_event.category',
+ 'signal.original_event.code',
+ 'signal.original_event.created',
+ 'signal.original_event.dataset',
+ 'signal.original_event.duration',
+ 'signal.original_event.end',
+ 'signal.original_event.hash',
+ 'signal.original_event.id',
+ 'signal.original_event.kind',
+ 'signal.original_event.module',
+ 'signal.original_event.original',
+ 'signal.original_event.outcome',
+ 'signal.original_event.provider',
+ 'signal.original_event.risk_score',
+ 'signal.original_event.risk_score_norm',
+ 'signal.original_event.sequence',
+ 'signal.original_event.severity',
+ 'signal.original_event.start',
+ 'signal.original_event.timezone',
+ 'signal.original_event.type',
+ 'signal.original_time',
+ 'signal.parent.depth',
+ 'signal.parent.id',
+ 'signal.parent.index',
+ 'signal.parent.rule',
+ 'signal.parent.type',
+ 'signal.rule.created_by',
+ 'signal.rule.description',
+ 'signal.rule.enabled',
+ 'signal.rule.false_positives',
+ 'signal.rule.filters',
+ 'signal.rule.from',
+ 'signal.rule.id',
+ 'signal.rule.immutable',
+ 'signal.rule.index',
+ 'signal.rule.interval',
+ 'signal.rule.language',
+ 'signal.rule.max_signals',
+ 'signal.rule.name',
+ 'signal.rule.note',
+ 'signal.rule.output_index',
+ 'signal.rule.query',
+ 'signal.rule.references',
+ 'signal.rule.risk_score',
+ 'signal.rule.rule_id',
+ 'signal.rule.saved_id',
+ 'signal.rule.severity',
+ 'signal.rule.size',
+ 'signal.rule.tags',
+ 'signal.rule.threat',
+ 'signal.rule.threat.tactic.id',
+ 'signal.rule.threat.tactic.name',
+ 'signal.rule.threat.tactic.reference',
+ 'signal.rule.threat.technique.id',
+ 'signal.rule.threat.technique.name',
+ 'signal.rule.threat.technique.reference',
+ 'signal.rule.timeline_id',
+ 'signal.rule.timeline_title',
+ 'signal.rule.to',
+ 'signal.rule.type',
+ 'signal.rule.updated_by',
+ 'signal.rule.version',
+ 'signal.status',
+ ].includes(fieldName);
+
+ return isWhitelistedNonBrowserField || (isAggregatable && isAllowedType);
+};
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/translations.ts b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/translations.ts
new file mode 100644
index 000000000000..61d036635a25
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/translations.ts
@@ -0,0 +1,33 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+
+export const COPY_TO_CLIPBOARD = i18n.translate('xpack.siem.dragAndDrop.copyToClipboardTooltip', {
+ defaultMessage: 'Copy to Clipboard',
+});
+
+export const FIELD = i18n.translate('xpack.siem.dragAndDrop.fieldLabel', {
+ defaultMessage: 'Field',
+});
+
+export const FILTER_FOR_VALUE = i18n.translate('xpack.siem.dragAndDrop.filterForValueHoverAction', {
+ defaultMessage: 'Filter for value',
+});
+
+export const FILTER_OUT_VALUE = i18n.translate('xpack.siem.dragAndDrop.filterOutValueHoverAction', {
+ defaultMessage: 'Filter out value',
+});
+
+export const CLOSE = i18n.translate('xpack.siem.dragAndDrop.closeButtonLabel', {
+ defaultMessage: 'Close',
+});
+
+export const SHOW_TOP = (fieldName: string) =>
+ i18n.translate('xpack.siem.overview.showTopTooltip', {
+ values: { fieldName },
+ defaultMessage: `Show top {fieldName}`,
+ });
diff --git a/x-pack/legacy/plugins/siem/public/components/draggables/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/draggables/__snapshots__/index.test.tsx.snap
index 63ba13306ecd..93608a181adf 100644
--- a/x-pack/legacy/plugins/siem/public/components/draggables/__snapshots__/index.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/draggables/__snapshots__/index.test.tsx.snap
@@ -10,6 +10,7 @@ exports[`draggables rendering it renders the default Badge 1`] = `
A child of this
diff --git a/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx b/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx
index 1fe6c936d282..b3811d05eea0 100644
--- a/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx
@@ -8,7 +8,6 @@ import { EuiBadge, EuiToolTip, IconType } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
-import { Omit } from '../../../common/utility_types';
import { DragEffects, DraggableWrapper } from '../drag_and_drop/draggable_wrapper';
import { escapeDataProviderId } from '../drag_and_drop/helpers';
import { getEmptyStringTag } from '../empty_value';
@@ -167,7 +166,7 @@ export const DraggableBadge = React.memo(
tooltipContent={tooltipContent}
queryValue={queryValue}
>
-
+
{children ? children : value !== '' ? value : getEmptyStringTag()}
diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx
index cbb4006bbf93..a7272593c2b2 100644
--- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx
@@ -14,7 +14,7 @@ import {
EmbeddablePanel,
ErrorEmbeddable,
} from '../../../../../../../src/plugins/embeddable/public';
-import { DEFAULT_INDEX_KEY } from '../../../common/constants';
+import { DEFAULT_INDEX_KEY } from '../../../../../../plugins/siem/common/constants';
import { getIndexPatternTitleIdMapping } from '../../hooks/api/helpers';
import { useIndexPatterns } from '../../hooks/use_index_patterns';
import { Loader } from '../loader';
diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx
index cd94a9fdcb5a..131a3a63bae3 100644
--- a/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx
@@ -21,7 +21,6 @@ import styled from 'styled-components';
import { BrowserFields } from '../../containers/source';
import { ToStringArray } from '../../graphql/types';
-import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard';
import { ColumnHeaderOptions } from '../../store/timeline/model';
import { DragEffects } from '../drag_and_drop/draggable_wrapper';
import { DroppableWrapper } from '../drag_and_drop/droppable_wrapper';
@@ -35,7 +34,6 @@ import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants';
import { MESSAGE_FIELD_NAME } from '../timeline/body/renderers/constants';
import { FormattedFieldValue } from '../timeline/body/renderers/formatted_field';
import { OnUpdateColumns } from '../timeline/events';
-import { WithHoverActions } from '../with_hover_actions';
import { getIconFromType, getExampleText, getColumnsWithTimestamp } from './helpers';
import * as i18n from './translations';
import { EventFieldsData } from './types';
@@ -172,29 +170,18 @@ export const getColumns = ({
component="span"
key={`event-details-value-flex-item-${contextId}-${eventId}-${data.field}-${i}-${value}`}
>
-
-
-
-
-
- }
- render={() =>
- data.field === MESSAGE_FIELD_NAME ? (
-
- ) : (
-
- )
- }
- />
+ {data.field === MESSAGE_FIELD_NAME ? (
+
+ ) : (
+
+ )}
))}
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
index ea2cb661763f..d210c749dae9 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
@@ -95,6 +95,7 @@ const EventsViewerComponent: React.FC = ({
}) => {
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
const kibana = useKibana();
+ const { filterManager } = useKibana().services.data.query;
const combinedQueries = combineQueries({
config: esQuery.getEsQueryConfig(kibana.services.uiSettings),
dataProviders,
@@ -168,7 +169,11 @@ const EventsViewerComponent: React.FC = ({
{utilityBar?.(refetch, totalCountMinusDeleted)}
-
+
{
});
});
- test('it renders a hover actions panel for the category name', () => {
- const wrapper = mount(
-
-
-
- );
- expect(
- wrapper
- .find('[data-test-subj="category-link"]')
- .first()
- .find('[data-test-subj="hover-actions-panel-container"]')
- .first()
- .exists()
- ).toBe(true);
- });
-
test('it renders the selected category with bold text', () => {
const selectedCategoryId = 'auditd';
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/category_columns.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/category_columns.tsx
index 0c7dd7e908ce..7133e9b848c5 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/category_columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/category_columns.tsx
@@ -6,15 +6,7 @@
/* eslint-disable react/display-name */
-import {
- EuiIcon,
- EuiFlexGroup,
- EuiFlexItem,
- EuiLink,
- EuiPanel,
- EuiText,
- EuiToolTip,
-} from '@elastic/eui';
+import { EuiIcon, EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, EuiToolTip } from '@elastic/eui';
import React, { useContext } from 'react';
import styled from 'styled-components';
@@ -35,22 +27,6 @@ const CategoryName = styled.span<{ bold: boolean }>`
CategoryName.displayName = 'CategoryName';
-const HoverActionsContainer = styled(EuiPanel)`
- cursor: default;
- left: 5px;
- padding: 8px;
- position: absolute;
- top: -8px;
-`;
-
-HoverActionsContainer.displayName = 'HoverActionsContainer';
-
-const HoverActionsFlexGroup = styled(EuiFlexGroup)`
- cursor: pointer;
-`;
-
-HoverActionsFlexGroup.displayName = 'HoverActionsFlexGroup';
-
const LinkContainer = styled.div`
width: 100%;
.euiLink {
@@ -71,7 +47,7 @@ interface ToolTipProps {
}
const ToolTip = React.memo(({ categoryId, browserFields, onUpdateColumns }) => {
- const isLoading = useContext(TimelineContext);
+ const { isLoading } = useContext(TimelineContext);
return (
{!isLoading ? (
@@ -127,25 +103,11 @@ export const getCategoryColumns = ({
-
-
-
-
-
-
+
}
render={() => (
{
expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true);
});
- test('it renders a view category action menu item a user hovers over the name', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper.simulate('mouseenter');
- wrapper.update();
- expect(wrapper.find('[data-test-subj="view-category"]').exists()).toBe(true);
- });
-
- test('it invokes onUpdateColumns when the view category action menu item is clicked', () => {
- const onUpdateColumns = jest.fn();
-
- const wrapper = mount(
-
-
-
- );
-
- wrapper.simulate('mouseenter');
- wrapper.update();
- wrapper
- .find('[data-test-subj="view-category"]')
- .first()
- .simulate('click');
-
- expect(onUpdateColumns).toBeCalledWith([
- {
- aggregatable: true,
- category: 'base',
- columnHeaderType: 'not-filtered',
- description:
- 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.',
- example: '2016-05-23T08:05:34.853Z',
- id: '@timestamp',
- type: 'date',
- width: 190,
- },
- ]);
- });
-
test('it highlights the text specified by the `highlight` prop', () => {
const highlight = 'stamp';
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_name.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_name.tsx
index fe434a6ad63c..fc9633b6f874 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_name.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_name.tsx
@@ -4,26 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- EuiButtonIcon,
- EuiHighlight,
- EuiIcon,
- EuiFlexGroup,
- EuiFlexItem,
- EuiPanel,
- EuiText,
- EuiToolTip,
-} from '@elastic/eui';
-import React, { useContext } from 'react';
+import { EuiButtonIcon, EuiHighlight, EuiIcon, EuiText, EuiToolTip } from '@elastic/eui';
+import React, { useCallback, useContext, useState, useMemo } from 'react';
import styled from 'styled-components';
-import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard';
import { ColumnHeaderOptions } from '../../store/timeline/model';
import { OnUpdateColumns } from '../timeline/events';
import { TimelineContext } from '../timeline/timeline_context';
import { WithHoverActions } from '../with_hover_actions';
import { LoadingSpinner } from './helpers';
import * as i18n from './translations';
+import { DraggableWrapperHoverContent } from '../drag_and_drop/draggable_wrapper_hover_content';
/**
* The name of a (draggable) field
@@ -82,22 +73,6 @@ export const FieldNameContainer = styled.span`
FieldNameContainer.displayName = 'FieldNameContainer';
-const HoverActionsContainer = styled(EuiPanel)`
- cursor: default;
- left: 5px;
- padding: 4px;
- position: absolute;
- top: -6px;
-`;
-
-HoverActionsContainer.displayName = 'HoverActionsContainer';
-
-const HoverActionsFlexGroup = styled(EuiFlexGroup)`
- cursor: pointer;
-`;
-
-HoverActionsFlexGroup.displayName = 'HoverActionsFlexGroup';
-
const ViewCategoryIcon = styled(EuiIcon)`
margin-left: 5px;
`;
@@ -112,7 +87,7 @@ interface ToolTipProps {
const ViewCategory = React.memo(
({ categoryId, onUpdateColumns, categoryColumns }) => {
- const isLoading = useContext(TimelineContext);
+ const { isLoading } = useContext(TimelineContext);
return (
{!isLoading ? (
@@ -142,48 +117,33 @@ export const FieldName = React.memo<{
fieldId: string;
highlight?: string;
onUpdateColumns: OnUpdateColumns;
-}>(({ categoryId, categoryColumns, fieldId, highlight = '', onUpdateColumns }) => (
-
-
-
-
-
-
-
-
- {categoryColumns.length > 0 && (
-
-
-
- )}
-
-
- }
- render={() => (
-
-
+}>(({ fieldId, highlight = '' }) => {
+ const [showTopN, setShowTopN] = useState(false);
+ const toggleTopN = useCallback(() => {
+ setShowTopN(!showTopN);
+ }, [setShowTopN, showTopN]);
+
+ const hoverContent = useMemo(
+ () => (
+
+ ),
+ [fieldId, showTopN, toggleTopN]
+ );
+
+ const render = useCallback(
+ () => (
+
+
{fieldId}
-
-
- )}
- />
-));
+
+
+ ),
+ [fieldId, highlight]
+ );
+
+ return ;
+});
FieldName.displayName = 'FieldName';
diff --git a/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx b/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx
index 98a1acf47162..abde602c1bda 100644
--- a/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx
@@ -7,7 +7,7 @@
import React from 'react';
import numeral from '@elastic/numeral';
-import { DEFAULT_BYTES_FORMAT } from '../../../common/constants';
+import { DEFAULT_BYTES_FORMAT } from '../../../../../../plugins/siem/common/constants';
import { useUiSetting$ } from '../../lib/kibana';
type Bytes = string | number;
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/title.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/title.tsx
index a1f3cfd85714..59039ddd6a23 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_page/title.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/title.tsx
@@ -51,7 +51,9 @@ const TitleComponent: React.FC = ({ draggableArguments, title, badgeOptio
tooltipPosition="bottom"
/>
) : (
- {badgeOptions.text}
+
+ {badgeOptions.text}
+
)}
>
)}
diff --git a/x-pack/legacy/plugins/siem/public/components/header_section/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_section/__snapshots__/index.test.tsx.snap
index d4c3763f5146..53b41e2240de 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_section/__snapshots__/index.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/header_section/__snapshots__/index.test.tsx.snap
@@ -11,13 +11,18 @@ exports[`HeaderSection it renders 1`] = `
responsive={false}
>
-
+
Test title
+
diff --git a/x-pack/legacy/plugins/siem/public/components/header_section/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_section/index.test.tsx
index bc4692b6fe0c..e61b39691203 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_section/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/header_section/index.test.tsx
@@ -48,7 +48,7 @@ describe('HeaderSection', () => {
).toBe(true);
});
- test('it DOES NOT render the subtitle when not provided', () => {
+ test('renders the subtitle when not provided (to prevent layout thrash)', () => {
const wrapper = mount(
@@ -60,7 +60,7 @@ describe('HeaderSection', () => {
.find('[data-test-subj="header-section-subtitle"]')
.first()
.exists()
- ).toBe(false);
+ ).toBe(true);
});
test('it renders supplements when children provided', () => {
diff --git a/x-pack/legacy/plugins/siem/public/components/header_section/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_section/index.tsx
index 3153e785a8a3..43245121dd39 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_section/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/header_section/index.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiTitle } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiTitle, EuiTitleSize } from '@elastic/eui';
import React from 'react';
import styled, { css } from 'styled-components';
@@ -36,6 +36,7 @@ export interface HeaderSectionProps extends HeaderProps {
split?: boolean;
subtitle?: string | React.ReactNode;
title: string | React.ReactNode;
+ titleSize?: EuiTitleSize;
tooltip?: string;
}
@@ -46,6 +47,7 @@ const HeaderSectionComponent: React.FC = ({
split,
subtitle,
title,
+ titleSize = 'm',
tooltip,
}) => (
@@ -53,7 +55,7 @@ const HeaderSectionComponent: React.FC = ({
-
+
{title}
{tooltip && (
@@ -65,7 +67,7 @@ const HeaderSectionComponent: React.FC = ({
- {subtitle && }
+
{id && (
diff --git a/x-pack/legacy/plugins/siem/public/components/links/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/links/index.test.tsx
index d2d1d6569854..214c0294f2cf 100644
--- a/x-pack/legacy/plugins/siem/public/components/links/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/links/index.test.tsx
@@ -24,6 +24,8 @@ import {
ExternalLink,
} from '.';
+jest.mock('../../pages/overview/events_by_dataset');
+
jest.mock('../../lib/kibana', () => {
return {
useUiSetting$: jest.fn(),
diff --git a/x-pack/legacy/plugins/siem/public/components/links/index.tsx b/x-pack/legacy/plugins/siem/public/components/links/index.tsx
index 62a67af6e08b..45225e31e9ac 100644
--- a/x-pack/legacy/plugins/siem/public/components/links/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/links/index.tsx
@@ -6,9 +6,10 @@
import { EuiLink, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React, { useMemo } from 'react';
-
import { isNil } from 'lodash/fp';
import styled from 'styled-components';
+
+import { IP_REPUTATION_LINKS_SETTING } from '../../../../../../plugins/siem/common/constants';
import {
DefaultFieldRendererOverflow,
DEFAULT_MORE_MAX_HEIGHT,
@@ -22,7 +23,6 @@ import {
} from '../link_to';
import { FlowTarget, FlowTargetSourceDest } from '../../graphql/types';
import { useUiSetting$ } from '../../lib/kibana';
-import { IP_REPUTATION_LINKS_SETTING } from '../../../common/constants';
import { isUrlInvalid } from '../../pages/detection_engine/rules/components/step_about_rule/helpers';
import { ExternalLinkIcon } from '../external_link_icon';
import { navTabs } from '../../pages/home/home_navigations';
diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap
index 0e518e48e2e8..5aa846d15b68 100644
--- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap
@@ -1,5 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`Matrix Histogram Component not initial load it renders no MatrixLoader 1`] = `"
"`;
+exports[`Matrix Histogram Component not initial load it renders no MatrixLoader 1`] = `""`;
-exports[`Matrix Histogram Component on initial load it renders MatrixLoader 1`] = `"
"`;
+exports[`Matrix Histogram Component on initial load it renders MatrixLoader 1`] = `"
"`;
diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.test.tsx
index db5b1f7f03ee..3b8a43a0f395 100644
--- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.test.tsx
@@ -87,6 +87,17 @@ describe('Matrix Histogram Component', () => {
});
});
+ describe('spacer', () => {
+ test('it renders a spacer by default', () => {
+ expect(wrapper.find('[data-test-subj="spacer"]').exists()).toBe(true);
+ });
+
+ test('it does NOT render a spacer when showSpacer is false', () => {
+ wrapper = mount( );
+ expect(wrapper.find('[data-test-subj="spacer"]').exists()).toBe(false);
+ });
+ });
+
describe('not initial load', () => {
beforeAll(() => {
(useQuery as jest.Mock).mockReturnValue({
diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx
index 12a474009dc5..3d4eebd68319 100644
--- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx
@@ -37,6 +37,7 @@ import {
import { SetQuery } from '../../pages/hosts/navigation/types';
import { QueryTemplateProps } from '../../containers/query_template';
import { setAbsoluteRangeDatePicker } from '../../store/inputs/actions';
+import { InputsModelId } from '../../store/inputs/constants';
import { HistogramType } from '../../graphql/types';
export interface OwnProps extends QueryTemplateProps {
@@ -46,9 +47,12 @@ export interface OwnProps extends QueryTemplateProps {
hideHistogramIfEmpty?: boolean;
histogramType: HistogramType;
id: string;
+ indexToAdd?: string[] | null;
legendPosition?: Position;
mapping?: MatrixHistogramMappingTypes;
+ showSpacer?: boolean;
setQuery: SetQuery;
+ setAbsoluteRangeDatePickerTarget?: InputsModelId;
showLegend?: boolean;
stackByOptions: MatrixHistogramOption[];
subtitle?: string | GetSubTitle;
@@ -62,6 +66,7 @@ const HeaderChildrenFlexItem = styled(EuiFlexItem)`
margin-left: 24px;
`;
+// @ts-ignore - the EUI type definitions for Panel do no play nice with styled-components
const HistogramPanel = styled(Panel)<{ height?: number }>`
display: flex;
flex-direction: column;
@@ -79,16 +84,20 @@ export const MatrixHistogramComponent: React.FC {
@@ -100,7 +109,11 @@ export const MatrixHistogramComponent: React.FC {
- dispatchSetAbsoluteRangeDatePicker({ id: 'global', from: min, to: max });
+ dispatchSetAbsoluteRangeDatePicker({
+ id: setAbsoluteRangeDatePickerTarget,
+ from: min,
+ to: max,
+ });
},
yTickFormatter,
showLegend,
@@ -122,7 +135,7 @@ export const MatrixHistogramComponent: React.FC) => {
setSelectedStackByOption(
- stackByOptions?.find(co => co.value === event.target.value) ?? defaultStackByOption
+ stackByOptions.find(co => co.value === event.target.value) ?? defaultStackByOption
);
},
[]
@@ -134,6 +147,7 @@ export const MatrixHistogramComponent: React.FC (title != null && typeof title === 'function' ? title(selectedStackByOption) : title),
[title, selectedStackByOption]
);
- const subtitleWithCounts = useMemo(
- () => (subtitle != null && typeof subtitle === 'function' ? subtitle(totalCount) : subtitle),
- [subtitle, totalCount]
- );
+ const subtitleWithCounts = useMemo(() => {
+ if (isInitialLoading) {
+ return null;
+ }
+
+ if (typeof subtitle === 'function') {
+ return totalCount >= 0 ? subtitle(totalCount) : null;
+ }
+
+ return subtitle;
+ }, [isInitialLoading, subtitle, totalCount]);
const hideHistogram = useMemo(() => (totalCount <= 0 && hideHistogramIfEmpty ? true : false), [
totalCount,
hideHistogramIfEmpty,
@@ -155,7 +176,9 @@ export const MatrixHistogramComponent: React.FC getCustomChartData(data, mapping), [data, mapping]);
useEffect(() => {
- setQuery({ id, inspect, loading, refetch });
+ if (!loading && !isInitialLoading) {
+ setQuery({ id, inspect, loading, refetch });
+ }
if (isInitialLoading && !!barChartData && data) {
setIsInitialLoading(false);
@@ -189,59 +212,39 @@ export const MatrixHistogramComponent: React.FC
)}
+
+
+
+ {stackByOptions.length > 1 && (
+
+ )}
+
+ {headerChildren}
+
+
+
{isInitialLoading ? (
- <>
- = 0 ? subtitleWithCounts : null)}
- >
-
-
- {stackByOptions?.length > 1 && (
-
- )}
-
- {headerChildren}
-
-
-
- >
+
) : (
- <>
- = 0 ? subtitleWithCounts : null)
- }
- >
-
-
- {stackByOptions?.length > 1 && (
-
- )}
-
- {headerChildren}
-
-
-
- >
+
)}
-
+ {showSpacer && }
>
);
};
diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/types.ts b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/types.ts
index a435c7be6c89..98437845a3ab 100644
--- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/types.ts
+++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/types.ts
@@ -4,9 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { EuiTitleSize } from '@elastic/eui';
import { ScaleType, Position, TickFormatter } from '@elastic/charts';
import { ActionCreator } from 'redux';
-import { ESQuery } from '../../../common/typed_json';
+import { ESQuery } from '../../../../../../plugins/siem/common/typed_json';
import { SetQuery } from '../../pages/hosts/navigation/types';
import { InputsModelId } from '../../store/inputs/constants';
import { HistogramType } from '../../graphql/types';
@@ -34,6 +35,7 @@ export interface MatrixHisrogramConfigs {
stackByOptions: MatrixHistogramOption[];
subtitle?: string | GetSubTitle;
title: string | GetTitle;
+ titleSize?: EuiTitleSize;
}
interface MatrixHistogramBasicProps {
@@ -57,14 +59,22 @@ interface MatrixHistogramBasicProps {
stackByOptions: MatrixHistogramOption[];
subtitle?: string | GetSubTitle;
title?: string | GetTitle;
+ titleSize?: EuiTitleSize;
}
export interface MatrixHistogramQueryProps {
endDate: number;
errorMessage: string;
filterQuery?: ESQuery | string | undefined;
+ setAbsoluteRangeDatePicker?: ActionCreator<{
+ id: InputsModelId;
+ from: number;
+ to: number;
+ }>;
+ setAbsoluteRangeDatePickerTarget?: InputsModelId;
stackByField: string;
startDate: number;
+ indexToAdd?: string[] | null;
isInspected: boolean;
histogramType: HistogramType;
}
@@ -73,6 +83,7 @@ export interface MatrixHistogramProps extends MatrixHistogramBasicProps {
scaleType?: ScaleType;
yTickFormatter?: (value: number) => string;
showLegend?: boolean;
+ showSpacer?: boolean;
legendPosition?: Position;
}
diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts
index ddac615cef50..d31eb1da15ea 100644
--- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts
+++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/utils.ts
@@ -69,6 +69,19 @@ export const getBarchartConfigs = ({
customHeight: chartHeight ?? DEFAULT_CHART_HEIGHT,
});
+export const defaultLegendColors = [
+ '#1EA593',
+ '#2B70F7',
+ '#CE0060',
+ '#38007E',
+ '#FCA5D3',
+ '#F37020',
+ '#E49E29',
+ '#B0916F',
+ '#7B000B',
+ '#34130C',
+];
+
export const formatToChartDataItem = ([key, value]: [
string,
MatrixOverTimeHistogramData[]
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/anomaly/use_anomalies_table_data.ts b/x-pack/legacy/plugins/siem/public/components/ml/anomaly/use_anomalies_table_data.ts
index c4ca7dc20361..cebfc172ee6f 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/anomaly/use_anomalies_table_data.ts
+++ b/x-pack/legacy/plugins/siem/public/components/ml/anomaly/use_anomalies_table_data.ts
@@ -5,6 +5,8 @@
*/
import { useState, useEffect } from 'react';
+
+import { DEFAULT_ANOMALY_SCORE } from '../../../../../../../plugins/siem/common/constants';
import { anomaliesTableData } from '../api/anomalies_table_data';
import { InfluencerInput, Anomalies, CriteriaFields } from '../types';
import { hasMlUserPermissions } from '../permissions/has_ml_user_permissions';
@@ -14,7 +16,6 @@ import { useStateToaster, errorToToaster } from '../../toasters';
import * as i18n from './translations';
import { useTimeZone, useUiSetting$ } from '../../../lib/kibana';
-import { DEFAULT_ANOMALY_SCORE } from '../../../../common/constants';
interface Args {
influencers?: InfluencerInput[];
diff --git a/x-pack/legacy/plugins/siem/public/components/ml/score/create_description_list.tsx b/x-pack/legacy/plugins/siem/public/components/ml/score/create_description_list.tsx
index e0f3ea162ee7..24f203a3682d 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml/score/create_description_list.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml/score/create_description_list.tsx
@@ -7,11 +7,12 @@
import { EuiText, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
+
+import { DescriptionList } from '../../../../../../../plugins/siem/common/utility_types';
import { Anomaly, NarrowDateRange } from '../types';
import { getScoreString } from './score_health';
import { PreferenceFormattedDate } from '../../formatted_date';
import { createInfluencers } from './../influencers/create_influencers';
-import { DescriptionList } from '../../../../common/utility_types';
import * as i18n from './translations';
import { createExplorerLink } from '../links/create_explorer_link';
diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/hooks/use_siem_jobs.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/hooks/use_siem_jobs.tsx
index 9a82859066f5..bc488ee00988 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml_popover/hooks/use_siem_jobs.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/hooks/use_siem_jobs.tsx
@@ -6,12 +6,12 @@
import { useEffect, useState } from 'react';
+import { DEFAULT_INDEX_KEY } from '../../../../../../../plugins/siem/common/constants';
import { checkRecognizer, getJobsSummary, getModules } from '../api';
import { SiemJob } from '../types';
import { hasMlUserPermissions } from '../../ml/permissions/has_ml_user_permissions';
import { errorToToaster, useStateToaster } from '../../toasters';
import { useUiSetting$ } from '../../../lib/kibana';
-import { DEFAULT_INDEX_KEY } from '../../../../common/constants';
import * as i18n from './translations';
import { createSiemJobs } from './use_siem_jobs_helpers';
diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx
index a0e0c70d2f20..a0343608dc67 100644
--- a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx
@@ -7,12 +7,12 @@
import styled from 'styled-components';
import React, { useState, useCallback } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSwitch } from '@elastic/eui';
-import { SiemJob } from '../types';
import {
isJobLoading,
isJobFailed,
isJobStarted,
-} from '../../../../common/detection_engine/ml_helpers';
+} from '../../../../../../../plugins/siem/common/detection_engine/ml_helpers';
+import { SiemJob } from '../types';
const StaticSwitch = styled(EuiSwitch)`
.euiSwitch__thumb,
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts
index 155f63145ca9..5407eba8b5b2 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts
@@ -7,7 +7,7 @@
import { getOr, omit } from 'lodash/fp';
import { ChromeBreadcrumb } from '../../../../../../../../src/core/public';
-import { APP_NAME } from '../../../../common/constants';
+import { APP_NAME } from '../../../../../../../plugins/siem/common/constants';
import { StartServices } from '../../../plugin';
import { getBreadcrumbs as getHostDetailsBreadcrumbs } from '../../../pages/hosts/details/utils';
import { getBreadcrumbs as getIPDetailsBreadcrumbs } from '../../../pages/network/ip_details';
diff --git a/x-pack/legacy/plugins/siem/public/components/news_feed/helpers.test.ts b/x-pack/legacy/plugins/siem/public/components/news_feed/helpers.test.ts
index e7cd03d098da..686ec4e86e78 100644
--- a/x-pack/legacy/plugins/siem/public/components/news_feed/helpers.test.ts
+++ b/x-pack/legacy/plugins/siem/public/components/news_feed/helpers.test.ts
@@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { NEWS_FEED_URL_SETTING_DEFAULT } from '../../../../../../plugins/siem/common/constants';
import { KibanaServices } from '../../lib/kibana';
-import { NEWS_FEED_URL_SETTING_DEFAULT } from '../../../common/constants';
import { rawNewsApiResponse } from '../../mock/news';
import { rawNewsJSON } from '../../mock/raw_news';
diff --git a/x-pack/legacy/plugins/siem/public/components/notes/note_card/note_card_body.tsx b/x-pack/legacy/plugins/siem/public/components/notes/note_card/note_card_body.tsx
index 11761c8fd39b..4463f8d4ff60 100644
--- a/x-pack/legacy/plugins/siem/public/components/notes/note_card/note_card_body.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/notes/note_card/note_card_body.tsx
@@ -5,7 +5,7 @@
*/
import { EuiPanel, EuiToolTip } from '@elastic/eui';
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import { WithCopyToClipboard } from '../../../lib/clipboard/with_copy_to_clipboard';
@@ -19,33 +19,23 @@ const BodyContainer = styled(EuiPanel)`
BodyContainer.displayName = 'BodyContainer';
-const HoverActionsContainer = styled(EuiPanel)`
- align-items: center;
- display: flex;
- flex-direction: row;
- height: 25px;
- justify-content: center;
- left: 5px;
- position: absolute;
- top: -5px;
- width: 30px;
-`;
-
-HoverActionsContainer.displayName = 'HoverActionsContainer';
-
-export const NoteCardBody = React.memo<{ rawNote: string }>(({ rawNote }) => (
-
-
-
-
-
-
- }
- render={() => }
- />
-
-));
+export const NoteCardBody = React.memo<{ rawNote: string }>(({ rawNote }) => {
+ const hoverContent = useMemo(
+ () => (
+
+
+
+ ),
+ [rawNote]
+ );
+
+ const render = useCallback(() => , [rawNote]);
+
+ return (
+
+
+
+ );
+});
NoteCardBody.displayName = 'NoteCardBody';
diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/__snapshots__/index.test.tsx.snap
index 42ef4e5404fa..ef02311c0629 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/__snapshots__/index.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/__snapshots__/index.test.tsx.snap
@@ -3,22 +3,36 @@
exports[`AddFilterToGlobalSearchBar Component Rendering 1`] = `
-
-
+
+
+
+
}
render={[Function]}
/>
diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/helpers.test.tsx
index 7e5e53f575be..5c920d923d9a 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/helpers.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/helpers.test.tsx
@@ -24,6 +24,22 @@ describe('helpers', () => {
});
});
+ test('returns a negated filter when `negate` is true', () => {
+ const filter = createFilter('host.name', 'siem-xavier', true);
+ expect(filter).toEqual({
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'host.name',
+ negate: true, // <-- filter is negated
+ params: { query: 'siem-xavier' },
+ type: 'phrase',
+ value: 'siem-xavier',
+ },
+ query: { match: { 'host.name': { query: 'siem-xavier', type: 'phrase' } } },
+ });
+ });
+
test('return valid exists filter when valid key and null value are provided', () => {
const filter = createFilter('host.name', null);
expect(filter).toEqual({
diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/helpers.ts b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/helpers.ts
index bafe033368c8..d88bc2bf3b7e 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/helpers.ts
@@ -6,13 +6,17 @@
import { Filter } from '../../../../../../../../src/plugins/data/public';
-export const createFilter = (key: string, value: string[] | string | null | undefined): Filter => {
+export const createFilter = (
+ key: string,
+ value: string[] | string | null | undefined,
+ negate: boolean = false
+): Filter => {
const queryValue = value != null ? (Array.isArray(value) ? value[0] : value) : null;
return queryValue != null
? {
meta: {
alias: null,
- negate: false,
+ negate,
disabled: false,
type: 'phrase',
key,
diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx
index 160cd020796d..127eb3bae028 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx
@@ -4,9 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiIcon, EuiPanel, EuiToolTip } from '@elastic/eui';
+import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import React, { useCallback } from 'react';
-import styled from 'styled-components';
import { Filter } from '../../../../../../../../src/plugins/data/public';
import { WithHoverActions } from '../../with_hover_actions';
@@ -26,21 +25,52 @@ export const AddFilterToGlobalSearchBar = React.memo
(
({ children, filter, onFilterAdded }) => {
const { filterManager } = useKibana().services.data.query;
- const addToKql = useCallback(() => {
+ const filterForValue = useCallback(() => {
filterManager.addFilters(filter);
+
if (onFilterAdded != null) {
onFilterAdded();
}
- }, [filter, filterManager, onFilterAdded]);
+ }, [filterManager, filter, onFilterAdded]);
+
+ const filterOutValue = useCallback(() => {
+ filterManager.addFilters({
+ ...filter,
+ meta: {
+ ...filter.meta,
+ negate: true,
+ },
+ });
+
+ if (onFilterAdded != null) {
+ onFilterAdded();
+ }
+ }, [filterManager, filter, onFilterAdded]);
return (
+
-
+
+
+
+
+
-
+
}
render={() => children}
/>
@@ -49,16 +79,3 @@ export const AddFilterToGlobalSearchBar = React.memo(
);
AddFilterToGlobalSearchBar.displayName = 'AddFilterToGlobalSearchBar';
-
-export const HoverActionsContainer = styled(EuiPanel)`
- align-items: center;
- display: flex;
- flex-direction: row;
- height: 34px;
- justify-content: center;
- left: 5px;
- position: absolute;
- top: -10px;
- width: 34px;
- cursor: pointer;
-`;
diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/translations.ts b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/translations.ts
index 81772527e59d..f192c5c26fa4 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/translations.ts
+++ b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/translations.ts
@@ -12,3 +12,10 @@ export const FILTER_FOR_VALUE = i18n.translate(
defaultMessage: 'Filter for value',
}
);
+
+export const FILTER_OUT_VALUE = i18n.translate(
+ 'xpack.siem.add_filter_to_global_search_bar.filterOutValueHoverAction',
+ {
+ defaultMessage: 'Filter out value',
+ }
+);
diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx
index 4d0e6a737d30..a0ca5f855237 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx
@@ -10,8 +10,8 @@ import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
import { getOr } from 'lodash/fp';
import React from 'react';
-import { DEFAULT_DARK_MODE } from '../../../../../common/constants';
-import { DescriptionList } from '../../../../../common/utility_types';
+import { DEFAULT_DARK_MODE } from '../../../../../../../../plugins/siem/common/constants';
+import { DescriptionList } from '../../../../../../../../plugins/siem/common/utility_types';
import { useUiSetting$ } from '../../../../lib/kibana';
import { getEmptyTagValue } from '../../../empty_value';
import { DefaultFieldRenderer, hostIdRenderer } from '../../../field_renderers/field_renderers';
diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/columns.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/columns.tsx
index 8d490d2c152d..6bd82f3192f9 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/columns.tsx
@@ -46,9 +46,7 @@ export const getHostsColumns = (): HostsTableColumns => [
) : (
-
-
-
+
)
}
/>
diff --git a/x-pack/legacy/plugins/siem/public/components/page/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/index.tsx
index ef6a19f4b744..3a36a2dce476 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/index.tsx
@@ -17,7 +17,7 @@ export const AppGlobalStyle = createGlobalStyle`
position: static;
}
/* end of dirty hack to fix draggables with tooltip on FF */
-
+
div.app-wrapper {
background-color: rgba(0,0,0,0);
}
@@ -28,12 +28,13 @@ export const AppGlobalStyle = createGlobalStyle`
.euiPopover__panel.euiPopover__panel-isOpen {
z-index: 9900 !important;
+ min-width: 24px;
}
.euiToolTip {
z-index: 9950 !important;
}
- /*
+ /*
overrides the default styling of euiComboBoxOptionsList because it's implemented
as a popover, so it's not selectable as a child of the styled component
*/
@@ -45,6 +46,17 @@ export const AppGlobalStyle = createGlobalStyle`
.euiPanel-loading-hide-border {
border: none;
}
+
+ /* hide open popovers when a modal is being displayed to prevent them from covering the modal */
+ body.euiBody-hasOverlayMask .euiPopover__panel-isOpen {
+ visibility: hidden !important;
+ }
+
+ /* ensure elastic charts tooltips appear above open euiPopovers */
+ .echTooltip {
+ z-index: 9950;
+ }
+
`;
export const DescriptionListStyled = styled(EuiDescriptionList)`
diff --git a/x-pack/legacy/plugins/siem/public/components/page/manage_query.tsx b/x-pack/legacy/plugins/siem/public/components/page/manage_query.tsx
index 138c38c02065..3b723c66f5af 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/manage_query.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/manage_query.tsx
@@ -42,6 +42,6 @@ export function manageQuery(WrappedComponent: React.ComponentClass | React
return ;
}
}
- ManageQuery.displayName = `ManageQuery (${WrappedComponent.displayName || 'Unknown'})`;
+ ManageQuery.displayName = `ManageQuery (${WrappedComponent?.displayName ?? 'Unknown'})`;
return ManageQuery;
}
diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx
index 56b59ca97156..a652fef5508f 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx
@@ -9,8 +9,8 @@ import darkTheme from '@elastic/eui/dist/eui_theme_dark.json';
import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
import React from 'react';
-import { DEFAULT_DARK_MODE } from '../../../../../common/constants';
-import { DescriptionList } from '../../../../../common/utility_types';
+import { DEFAULT_DARK_MODE } from '../../../../../../../../plugins/siem/common/constants';
+import { DescriptionList } from '../../../../../../../../plugins/siem/common/utility_types';
import { useUiSetting$ } from '../../../../lib/kibana';
import { FlowTarget, IpOverviewData, Overview } from '../../../../graphql/types';
import { networkModel } from '../../../../store';
diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx
index 52c142ceff48..b43efbbde51b 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx
@@ -10,8 +10,8 @@ import numeral from '@elastic/numeral';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useMemo } from 'react';
-import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants';
-import { ESQuery } from '../../../../../common/typed_json';
+import { DEFAULT_NUMBER_FORMAT } from '../../../../../../../../plugins/siem/common/constants';
+import { ESQuery } from '../../../../../../../../plugins/siem/common/typed_json';
import {
ID as OverviewHostQueryId,
OverviewHostQuery,
diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx
index d649a0dd9e92..af50fa88e5fe 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx
@@ -10,8 +10,8 @@ import numeral from '@elastic/numeral';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useMemo } from 'react';
-import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants';
-import { ESQuery } from '../../../../../common/typed_json';
+import { DEFAULT_NUMBER_FORMAT } from '../../../../../../../../plugins/siem/common/constants';
+import { ESQuery } from '../../../../../../../../plugins/siem/common/typed_json';
import { HeaderSection } from '../../../header_section';
import { useUiSetting$ } from '../../../../lib/kibana';
import { manageQuery } from '../../../page/manage_query';
diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/stat_value.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/stat_value.tsx
index 7615001eec9d..cada0a9aff93 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/overview/stat_value.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/overview/stat_value.tsx
@@ -9,7 +9,7 @@ import numeral from '@elastic/numeral';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
-import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants';
+import { DEFAULT_NUMBER_FORMAT } from '../../../../../../../plugins/siem/common/constants';
import { useUiSetting$ } from '../../../lib/kibana';
const ProgressContainer = styled.div`
diff --git a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx
index 947bdee6a5cd..2f743c338720 100644
--- a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx
@@ -7,13 +7,13 @@
import { mount, shallow } from 'enzyme';
import React from 'react';
+import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../plugins/siem/common/constants';
import { Direction } from '../../graphql/types';
import { BasicTableProps, PaginatedTable } from './index';
import { getHostsColumns, mockData, rowItems, sortedHosts } from './index.mock';
import { ThemeProvider } from 'styled-components';
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
-import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants';
jest.mock('react', () => {
const r = jest.requireActual('react');
diff --git a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx
index 5cd200cbb41b..e481fe724520 100644
--- a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx
@@ -3,6 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
+
import {
EuiBasicTable,
EuiBasicTableProps,
@@ -21,6 +22,7 @@ import { noop } from 'lodash/fp';
import React, { FC, memo, useState, useEffect, ComponentType } from 'react';
import styled from 'styled-components';
+import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../plugins/siem/common/constants';
import { AuthTableColumns } from '../page/hosts/authentications_table';
import { HostsTableColumns } from '../page/hosts/hosts_table';
import { NetworkDnsColumns } from '../page/network/network_dns_table/columns';
@@ -39,7 +41,6 @@ import { UsersColumns } from '../page/network/users_table/columns';
import { HeaderSection } from '../header_section';
import { Loader } from '../loader';
import { useStateToaster } from '../toasters';
-import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants';
import * as i18n from './translations';
import { Panel } from '../panel';
@@ -246,21 +247,16 @@ const PaginatedTableComponent: FC = ({
) : (
<>
- {
- // @ts-ignore avoid some type mismatches
- }
= ({
export const PaginatedTable = memo(PaginatedTableComponent);
type BasicTableType = ComponentType>; // eslint-disable-line @typescript-eslint/no-explicit-any
-const BasicTable: typeof EuiBasicTable & { displayName: string } = styled(
- EuiBasicTable as BasicTableType
-)`
+const BasicTable = styled(EuiBasicTable as BasicTableType)`
tbody {
th,
td {
diff --git a/x-pack/legacy/plugins/siem/public/components/query_bar/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/query_bar/index.test.tsx
index 870d0b40d8cd..49afc8d5ef68 100644
--- a/x-pack/legacy/plugins/siem/public/components/query_bar/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/query_bar/index.test.tsx
@@ -7,9 +7,9 @@
import { mount } from 'enzyme';
import React from 'react';
+import { DEFAULT_FROM, DEFAULT_TO } from '../../../../../../plugins/siem/common/constants';
import { TestProviders, mockIndexPattern } from '../../mock';
import { createKibanaCoreStartMock } from '../../mock/kibana_core';
-import { DEFAULT_FROM, DEFAULT_TO } from '../../../common/constants';
import { FilterManager, SearchBar } from '../../../../../../../src/plugins/data/public';
import { QueryBar, QueryBarComponentProps } from '.';
import { createKibanaContextProviderMock } from '../../mock/kibana_react';
diff --git a/x-pack/legacy/plugins/siem/public/components/source_destination/source_destination_ip.tsx b/x-pack/legacy/plugins/siem/public/components/source_destination/source_destination_ip.tsx
index b8192cce11e5..62f01dfc020f 100644
--- a/x-pack/legacy/plugins/siem/public/components/source_destination/source_destination_ip.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/source_destination/source_destination_ip.tsx
@@ -171,7 +171,7 @@ export const SourceDestinationIp = React.memo(
return isIpFieldPopulated({ destinationIp, sourceIp, type }) ||
hasPorts({ destinationPort, sourcePort, type }) ? (
-
+
(({ header, onColumnRemoved, sort }) => {
- const isLoading = useTimelineContext();
+ const { isLoading } = useTimelineContext();
return (
<>
{sort.columnId === header.id && isLoading ? (
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/header_content.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/header_content.tsx
index 84781e6a2430..0a69cef61857 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/header_content.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/header_content.tsx
@@ -32,7 +32,7 @@ const HeaderContentComponent: React.FC = ({
onClick,
sort,
}) => {
- const isLoading = useTimelineContext();
+ const { isLoading } = useTimelineContext();
return (
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx
index 5f59915eac41..417a078a0815 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx
@@ -19,6 +19,8 @@ import {
createGenericFileRowRenderer,
} from './generic_row_renderer';
+jest.mock('../../../../../pages/overview/events_by_dataset');
+
describe('GenericRowRenderer', () => {
const mount = useMountAppended();
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx
index 9ccd1fb7a051..24c52f3372d6 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx
@@ -44,7 +44,7 @@ export const Tokens = React.memo<{ tokens: string[] }>(({ tokens }) => (
<>
{tokens.map(token => (
-
+
{token}
@@ -81,7 +81,7 @@ export const DraggableSignatureId = React.memo<{ id: string; signatureId: number
data-test-subj="signature-id-tooltip"
content={SURICATA_SIGNATURE_ID_FIELD_NAME}
>
-
+
{signatureId}
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_details.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_details.tsx
index e1524c8e5aec..2ad3eb468145 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_details.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_details.tsx
@@ -119,7 +119,7 @@ export const SystemGenericLine = React.memo(
-
+
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx
index c47d9603cbea..ef7c3b3ccf85 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx
@@ -193,7 +193,7 @@ export const SystemGenericFileLine = React.memo(
-
+
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx
index abe77a63f4a2..2f5fa76855f2 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx
@@ -48,6 +48,8 @@ import {
} from './generic_row_renderer';
import * as i18n from './translations';
+jest.mock('../../../../../pages/overview/events_by_dataset');
+
describe('GenericRowRenderer', () => {
const mount = useMountAppended();
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx
index f13a236e8ec3..39c21c4ffa33 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx
@@ -92,7 +92,7 @@ export const DraggableZeekElement = React.memo<{
) : (
-
+
{stringRenderer(value)}
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/index.tsx
index 525cc8e301d1..f369b961807a 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/index.tsx
@@ -109,7 +109,7 @@ export const DataProviders = React.memo(
data-test-subj="dataProviders"
>
- {isLoading => (
+ {({ isLoading }) => (
{dataProviders != null && dataProviders.length ? (
(
({ deleteProvider, field, isEnabled, isExcluded, operator, providerId, togglePopover, val }) => {
- const deleteFilter: React.MouseEventHandler = (
- event: React.MouseEvent
- ) => {
- // Make sure it doesn't also trigger the onclick for the whole badge
- if (event.stopPropagation) {
- event.stopPropagation();
- }
- deleteProvider();
- };
- const classes = classNames('globalFilterItem', {
- 'globalFilterItem-isDisabled': !isEnabled,
- 'globalFilterItem-isExcluded': isExcluded,
- });
- const formattedValue = isString(val) && val === '' ? getEmptyString() : val;
- const prefix = isExcluded ? {i18n.NOT} : null;
- const title = `${field}: "${formattedValue}"`;
-
- return (
-
- {prefix}
- {operator !== EXISTS_OPERATOR ? (
- <>
- {`${field}: `}
- {`"${formattedValue}"`}
- >
- ) : (
-
- {field} {i18n.EXISTS_LABEL}
-
- )}
-
+ const deleteFilter: React.MouseEventHandler = useCallback(
+ (event: React.MouseEvent) => {
+ // Make sure it doesn't also trigger the onclick for the whole badge
+ if (event.stopPropagation) {
+ event.stopPropagation();
+ }
+ deleteProvider();
+ },
+ [deleteProvider]
+ );
+
+ const classes = useMemo(
+ () =>
+ classNames('globalFilterItem', {
+ 'globalFilterItem-isDisabled': !isEnabled,
+ 'globalFilterItem-isExcluded': isExcluded,
+ }),
+ [isEnabled, isExcluded]
+ );
+
+ const formattedValue = useMemo(() => (isString(val) && val === '' ? getEmptyString() : val), [
+ val,
+ ]);
+
+ const prefix = useMemo(() => (isExcluded ? {i18n.NOT} : null), [isExcluded]);
+
+ const title = useMemo(() => `${field}: "${formattedValue}"`, [field, formattedValue]);
+
+ const hoverContent = useMemo(
+ () => (
+
+ ),
+ [field, val]
);
+
+ const badge = useCallback(
+ () => (
+
+ {prefix}
+ {operator !== EXISTS_OPERATOR ? (
+ <>
+ {`${field}: `}
+ {`"${formattedValue}"`}
+ >
+ ) : (
+
+ {field} {i18n.EXISTS_LABEL}
+
+ )}
+
+ ),
+ [
+ providerId,
+ field,
+ val,
+ classes,
+ title,
+ deleteFilter,
+ togglePopover,
+ formattedValue,
+ closeButtonProps,
+ prefix,
+ operator,
+ ]
+ );
+
+ return ;
}
);
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_badge.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_badge.tsx
index 79f9c32a176f..2cc19537d6a6 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_badge.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_badge.tsx
@@ -71,7 +71,7 @@ export const ProviderItemBadge = React.memo(
return (
- {isLoading => (
+ {({ isLoading }) => (
{
- const mockTimelineContext: boolean = true;
+ const isLoading: boolean = true;
const mount = useMountAppended();
+ const filterManager = new FilterManager(mockUiSettingsForFilterManager);
describe('rendering', () => {
test('renders correctly against snapshot', () => {
@@ -96,7 +101,7 @@ describe('Providers', () => {
const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
{
const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
{
const mockOnToggleDataProviderEnabled = jest.fn();
const wrapper = mount(
-
+
{
const wrapper = mount(
-
+
{
const wrapper = mount(