diff --git a/.backportrc.json b/.backportrc.json
index 0894909d2aac4..87bc3a1be583b 100644
--- a/.backportrc.json
+++ b/.backportrc.json
@@ -25,7 +25,7 @@
],
"targetPRLabels": ["backport"],
"branchLabelMapping": {
- "^v7.8.0$": "7.x",
+ "^v7.9.0$": "7.x",
"^v(\\d+).(\\d+).\\d+$": "$1.$2"
}
}
diff --git a/.eslintrc.js b/.eslintrc.js
index dde0ce010d4d4..f1e0b7d9353e8 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -112,7 +112,6 @@ module.exports = {
files: ['x-pack/plugins/lens/**/*.{js,ts,tsx}'],
rules: {
'react-hooks/exhaustive-deps': 'off',
- 'react-hooks/rules-of-hooks': 'off',
},
},
{
@@ -238,6 +237,7 @@ module.exports = {
],
from: [
'(src|x-pack)/plugins/**/(public|server)/**/*',
+ '!(src|x-pack)/plugins/**/(public|server)/mocks/index.{js,ts}',
'!(src|x-pack)/plugins/**/(public|server)/(index|mocks).{js,ts,tsx}',
],
allowSameFolder: true,
diff --git a/.i18nrc.json b/.i18nrc.json
index be3c043b6e52f..034b9da799d3e 100644
--- a/.i18nrc.json
+++ b/.i18nrc.json
@@ -34,7 +34,7 @@
"kibana_utils": "src/plugins/kibana_utils",
"navigation": "src/plugins/navigation",
"newsfeed": "src/plugins/newsfeed",
- "regionMap": "src/legacy/core_plugins/region_map",
+ "regionMap": "src/plugins/region_map",
"savedObjects": "src/plugins/saved_objects",
"savedObjectsManagement": "src/plugins/saved_objects_management",
"server": "src/legacy/server",
@@ -43,7 +43,7 @@
"src/plugins/telemetry",
"src/plugins/telemetry_management_section"
],
- "tileMap": "src/legacy/core_plugins/tile_map",
+ "tileMap": "src/plugins/tile_map",
"timelion": ["src/legacy/core_plugins/timelion", "src/plugins/vis_type_timelion"],
"uiActions": "src/plugins/ui_actions",
"visDefaultEditor": "src/plugins/vis_default_editor",
diff --git a/docs/apm/images/apm-service-map-anomaly.png b/docs/apm/images/apm-service-map-anomaly.png
new file mode 100644
index 0000000000000..b661e8f09d1a1
Binary files /dev/null and b/docs/apm/images/apm-service-map-anomaly.png differ
diff --git a/docs/apm/images/green-service.png b/docs/apm/images/green-service.png
new file mode 100644
index 0000000000000..bbc00a3543b08
Binary files /dev/null and b/docs/apm/images/green-service.png differ
diff --git a/docs/apm/images/red-service.png b/docs/apm/images/red-service.png
new file mode 100644
index 0000000000000..be7a62b1774ab
Binary files /dev/null and b/docs/apm/images/red-service.png differ
diff --git a/docs/apm/images/service-maps.png b/docs/apm/images/service-maps.png
index 454ae9bb720fb..d4272e8999991 100644
Binary files a/docs/apm/images/service-maps.png and b/docs/apm/images/service-maps.png differ
diff --git a/docs/apm/images/yellow-service.png b/docs/apm/images/yellow-service.png
new file mode 100644
index 0000000000000..43afd6250be72
Binary files /dev/null and b/docs/apm/images/yellow-service.png differ
diff --git a/docs/apm/machine-learning.asciidoc b/docs/apm/machine-learning.asciidoc
index 9d347fc4f1111..03f7e13c98579 100644
--- a/docs/apm/machine-learning.asciidoc
+++ b/docs/apm/machine-learning.asciidoc
@@ -6,13 +6,20 @@
Integrate with machine learning
++++
-The Machine Learning integration will initiate a new job predefined to calculate anomaly scores on transaction response times.
-The response time graph will show the expected bounds and add an annotation when the anomaly score is 75 or above.
-Jobs can be created per transaction type, and based on the average response time.
-Manage jobs in the *Machine Learning jobs management*.
+The Machine Learning integration initiates a new job predefined to calculate anomaly scores on APM transaction durations.
+Jobs can be created per transaction type, and are based on the service's average response time.
+
+After a machine learning job is created, results are shown in two places:
+
+The transaction duration graph will show the expected bounds and add an annotation when the anomaly score is 75 or above.
+
+[role="screenshot"]
+image::apm/images/apm-ml-integration.png[Example view of anomaly scores on response times in the APM app]
+
+Service maps will display a color-coded anomaly indicator based on the detected anomaly score.
[role="screenshot"]
-image::apm/images/apm-ml-integration.png[Example view of anomaly scores on response times in APM app in Kibana]
+image::apm/images/apm-service-map-anomaly.png[Example view of anomaly scores on service maps in the APM app]
[float]
[[create-ml-integration]]
@@ -20,8 +27,10 @@ image::apm/images/apm-ml-integration.png[Example view of anomaly scores on respo
To enable machine learning anomaly detection, first choose a service to monitor.
Then, select **Integrations** > **Enable ML anomaly detection** and click **Create job**.
+
That's it! After a few minutes, the job will begin calculating results;
it might take additional time for results to appear on your graph.
+Jobs can be managed in *Machine Learning jobs management*.
APM specific anomaly detection wizards are also available for certain Agents.
See the machine learning {ml-docs}/ootb-ml-jobs-apm.html[APM anomaly detection configurations] for more information.
diff --git a/docs/apm/service-maps.asciidoc b/docs/apm/service-maps.asciidoc
index be86b9d522ac5..3a6a96fca9d09 100644
--- a/docs/apm/service-maps.asciidoc
+++ b/docs/apm/service-maps.asciidoc
@@ -9,7 +9,9 @@ Please use Chrome or Firefox if available.
A service map is a real-time visual representation of the instrumented services in your application's architecture.
It shows you how these services are connected, along with high-level metrics like average transaction duration,
-requests per minute, and errors per minute, that allow you to quickly assess the status of your services.
+requests per minute, and errors per minute.
+If enabled, service maps also integrate with machine learning--for real time health indicators based on anomaly detection scores.
+All of these features can help you to quickly and visually assess the status and health of your services.
We currently surface two types of service maps:
@@ -52,6 +54,26 @@ Additional filters are not currently available for service maps.
[role="screenshot"]
image::apm/images/service-maps-java.png[Example view of service maps with Java highlighted in the APM app in Kibana]
+[float]
+[[service-map-anomaly-detection]]
+=== Anomaly detection with machine learning
+
+Machine learning jobs can be created to calculate anomaly scores on APM transaction durations within the selected service.
+When these jobs are active, service maps will display a color-coded anomaly indicator based on the detected anomaly score:
+
+[horizontal]
+image:apm/images/green-service.png[APM green service]:: Max anomaly score **<=25**. Service is healthy.
+image:apm/images/yellow-service.png[APM yellow service]:: Max anomaly score **26-74**. Anomalous activity detected. Service may be degraded.
+image:apm/images/red-service.png[APM red service]:: Max anomaly score **>=75**. Anomalous activity detected. Service is unhealthy.
+
+[role="screenshot"]
+image::apm/images/apm-service-map-anomaly.png[Example view of anomaly scores on service maps in the APM app]
+
+If an anomaly has been detected, click *view anomalies* to view the anomaly detection metric viewier in the Machine learning app.
+This time series analysis will display additional details on the severity and time of the detected anomalies.
+
+To learn how to create a machine learning job, see <>.
+
[float]
[[service-maps-legend]]
=== Legend
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.error.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.error.md
new file mode 100644
index 0000000000000..7536cd2b07ae6
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.error.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsMigrationLogger](./kibana-plugin-core-server.savedobjectsmigrationlogger.md) > [error](./kibana-plugin-core-server.savedobjectsmigrationlogger.error.md)
+
+## SavedObjectsMigrationLogger.error property
+
+Signature:
+
+```typescript
+error: (msg: string, meta: LogMeta) => void;
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md
index 066643516b213..1b691ee8cb16d 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsmigrationlogger.md
@@ -16,6 +16,7 @@ export interface SavedObjectsMigrationLogger
| Property | Type | Description |
| --- | --- | --- |
| [debug](./kibana-plugin-core-server.savedobjectsmigrationlogger.debug.md) | (msg: string) => void | |
+| [error](./kibana-plugin-core-server.savedobjectsmigrationlogger.error.md) | (msg: string, meta: LogMeta) => void | |
| [info](./kibana-plugin-core-server.savedobjectsmigrationlogger.info.md) | (msg: string) => void | |
| [warn](./kibana-plugin-core-server.savedobjectsmigrationlogger.warn.md) | (msg: string) => void | |
| [warning](./kibana-plugin-core-server.savedobjectsmigrationlogger.warning.md) | (msg: string) => void | |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md
index 21a155ba977c9..60cbfd30e667d 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md
@@ -28,7 +28,6 @@ export declare class IndexPattern implements IIndexPattern
| [formatHit](./kibana-plugin-plugins-data-public.indexpattern.formathit.md) | | any | |
| [id](./kibana-plugin-plugins-data-public.indexpattern.id.md) | | string | |
| [metaFields](./kibana-plugin-plugins-data-public.indexpattern.metafields.md) | | string[] | |
-| [routes](./kibana-plugin-plugins-data-public.indexpattern.routes.md) | | { edit: string; addField: string; indexedFields: string; scriptedFields: string; sourceFilters: string; } | |
| [timeFieldName](./kibana-plugin-plugins-data-public.indexpattern.timefieldname.md) | | string | undefined | |
| [title](./kibana-plugin-plugins-data-public.indexpattern.title.md) | | string | |
| [type](./kibana-plugin-plugins-data-public.indexpattern.type.md) | | string | |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.routes.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.routes.md
deleted file mode 100644
index 81e7abd4f9609..0000000000000
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.routes.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [routes](./kibana-plugin-plugins-data-public.indexpattern.routes.md)
-
-## IndexPattern.routes property
-
-Signature:
-
-```typescript
-get routes(): {
- edit: string;
- addField: string;
- indexedFields: string;
- scriptedFields: string;
- sourceFilters: string;
- };
-```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterns.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterns.md
index fa97666a61b93..39c8b0a700c8a 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterns.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterns.md
@@ -18,7 +18,6 @@ indexPatterns: {
validate: typeof validateIndexPattern;
getFromSavedObject: typeof getFromSavedObject;
flattenHitWrapper: typeof flattenHitWrapper;
- getRoutes: typeof getRoutes;
formatHitProvider: typeof formatHitProvider;
}
```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md
new file mode 100644
index 0000000000000..a4d6abcf86a94
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) > [getTimeField](./kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md)
+
+## IIndexPattern.getTimeField() method
+
+Signature:
+
+```typescript
+getTimeField?(): IFieldType | undefined;
+```
+Returns:
+
+`IFieldType | undefined`
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md
index 24b56a9b98621..a79244a24acf5 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md
@@ -21,3 +21,9 @@ export interface IIndexPattern
| [title](./kibana-plugin-plugins-data-server.iindexpattern.title.md) | string | |
| [type](./kibana-plugin-plugins-data-server.iindexpattern.type.md) | string | |
+## Methods
+
+| Method | Description |
+| --- | --- |
+| [getTimeField()](./kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md) | |
+
diff --git a/docs/management/alerting/images/alerts-and-actions-ui.png b/docs/management/alerting/images/alerts-and-actions-ui.png
index acf3f3b1f0be9..d46df21e6f6b0 100644
Binary files a/docs/management/alerting/images/alerts-and-actions-ui.png and b/docs/management/alerting/images/alerts-and-actions-ui.png differ
diff --git a/docs/management/alerting/images/alerts-details-instance-muting.png b/docs/management/alerting/images/alerts-details-instance-muting.png
index 9d26fad419e4f..fd59e79d07279 100644
Binary files a/docs/management/alerting/images/alerts-details-instance-muting.png and b/docs/management/alerting/images/alerts-details-instance-muting.png differ
diff --git a/docs/management/alerting/images/alerts-details-instances-active.png b/docs/management/alerting/images/alerts-details-instances-active.png
index d6895bd4952b8..7506d1cb8c65e 100644
Binary files a/docs/management/alerting/images/alerts-details-instances-active.png and b/docs/management/alerting/images/alerts-details-instances-active.png differ
diff --git a/docs/management/alerting/images/alerts-details-instances-inactive.png b/docs/management/alerting/images/alerts-details-instances-inactive.png
index b049b4ba082f6..a757d59e12360 100644
Binary files a/docs/management/alerting/images/alerts-details-instances-inactive.png and b/docs/management/alerting/images/alerts-details-instances-inactive.png differ
diff --git a/docs/management/alerting/images/alerts-details-muting.png b/docs/management/alerting/images/alerts-details-muting.png
index 9b47d82a74639..29cdf707b4912 100644
Binary files a/docs/management/alerting/images/alerts-details-muting.png and b/docs/management/alerting/images/alerts-details-muting.png differ
diff --git a/docs/management/alerting/images/alerts-filter-by-action-type.png b/docs/management/alerting/images/alerts-filter-by-action-type.png
index 94336a20e1d6c..c0e495a87ecd3 100644
Binary files a/docs/management/alerting/images/alerts-filter-by-action-type.png and b/docs/management/alerting/images/alerts-filter-by-action-type.png differ
diff --git a/docs/management/alerting/images/alerts-filter-by-type.png b/docs/management/alerting/images/alerts-filter-by-type.png
index 75ffb3ff69bab..859274e9b6613 100644
Binary files a/docs/management/alerting/images/alerts-filter-by-type.png and b/docs/management/alerting/images/alerts-filter-by-type.png differ
diff --git a/docs/management/alerting/images/individual-mute-disable.png b/docs/management/alerting/images/individual-mute-disable.png
index ca00240a4af61..dc187c97de309 100644
Binary files a/docs/management/alerting/images/individual-mute-disable.png and b/docs/management/alerting/images/individual-mute-disable.png differ
diff --git a/docs/user/alerting/action-types.asciidoc b/docs/user/alerting/action-types.asciidoc
index 8794c389d72bc..09878b3059ac8 100644
--- a/docs/user/alerting/action-types.asciidoc
+++ b/docs/user/alerting/action-types.asciidoc
@@ -43,11 +43,10 @@ see https://www.elastic.co/subscriptions[the subscription page].
[[create-connectors]]
=== Preconfigured connectors and action types
-You can create connectors for actions in <> or via the action API.
-For out-of-the-box and standardized connectors, you can <>
+For out-of-the-box and standardized connectors, you can <>
before {kib} starts.
-Action type with only preconfigured connectors could be specified as a <>.
+If you preconfigure a connector, you can also <>.
include::action-types/email.asciidoc[]
include::action-types/index.asciidoc[]
@@ -56,4 +55,3 @@ include::action-types/server-log.asciidoc[]
include::action-types/slack.asciidoc[]
include::action-types/webhook.asciidoc[]
include::pre-configured-connectors.asciidoc[]
-include::pre-configured-action-types.asciidoc[]
diff --git a/docs/user/alerting/action-types/email.asciidoc b/docs/user/alerting/action-types/email.asciidoc
index 794fc14005f2f..81b4e210961f6 100644
--- a/docs/user/alerting/action-types/email.asciidoc
+++ b/docs/user/alerting/action-types/email.asciidoc
@@ -19,6 +19,37 @@ Username:: username for 'login' type authentication.
Password:: password for 'login' type authentication.
[float]
+[[Preconfigured-email-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ my-email:
+ name: preconfigured-email-action-type
+ actionTypeId: .email
+ config:
+ from: testsender@test.com <1.1>
+ host: validhostname <1.2>
+ port: 8080 <1.3>
+ secure: false <1.4>
+ secrets:
+ user: testuser <2.1>
+ password: passwordkeystorevalue <2.2>
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+<1.1> `from:` is an email address and correspond to *Sender*.
+<1.2> `host:` is a string and correspond to *Host*.
+<1.3> `port:` is a number and correspond to *Port*.
+<1.4> `secure:` is a boolean and correspond to *Secure*.
+
+`secrets` defines action type sensitive configuration:
+
+<2.1> `user:` is a string and correspond to *User*.
+<2.2> `password:` is a string and correspond to *Password*. Should be stored in the <>.
+
+
[[email-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/action-types/index.asciidoc b/docs/user/alerting/action-types/index.asciidoc
index 625b8f704b7c6..c71412210c535 100644
--- a/docs/user/alerting/action-types/index.asciidoc
+++ b/docs/user/alerting/action-types/index.asciidoc
@@ -15,6 +15,28 @@ Index:: The {es} index to be written to.
Refresh:: Setting for the {ref}/docs-refresh.html[refresh] policy for the write request.
Execution time field:: This field will be automatically set to the time the alert condition was detected.
+[float]
+[[Preconfigured-index-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ my-index:
+ name: action-type-index
+ actionTypeId: .index
+ config:
+ index: .kibana <1>
+ refresh: true <2>
+ executionTimeField: somedate <3>
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+<1> `index:` is a string and correspond to *Index*.
+<2> `refresh:` is a boolean and correspond to *Refresh*.
+<3> `executionTimeField:` is a string and correspond to *Execution time field*.
+
+
[float]
[[index-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/action-types/pagerduty.asciidoc b/docs/user/alerting/action-types/pagerduty.asciidoc
index 673b4f6263e18..cd51ec2e3301e 100644
--- a/docs/user/alerting/action-types/pagerduty.asciidoc
+++ b/docs/user/alerting/action-types/pagerduty.asciidoc
@@ -135,6 +135,29 @@ Name:: The name of the connector. The name is used to identify a connector
API URL:: An optional PagerDuty event URL. Defaults to `https://events.pagerduty.com/v2/enqueue`. If you are using the <> setting, make sure the hostname is whitelisted.
Integration Key:: A 32 character PagerDuty Integration Key for an integration on a service, also referred to as the routing key.
+[float]
+[[Preconfigured-pagerduty-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ my-pagerduty:
+ name: preconfigured-pagerduty-action-type
+ actionTypeId: .pagerduty
+ config:
+ apiUrl: https://test.host <1.1>
+ secrets:
+ routingKey: testroutingkey <2.1>
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+<1.1> `apiUrl:` is URL string and correspond to *API URL*.
+
+`secrets` defines action type sensitive configuration:
+
+<2.1> `routingKey:` is a string and correspond to *Integration Key*.
+
[float]
[[pagerduty-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/action-types/server-log.asciidoc b/docs/user/alerting/action-types/server-log.asciidoc
index 8f888785626c9..eadca229bc19c 100644
--- a/docs/user/alerting/action-types/server-log.asciidoc
+++ b/docs/user/alerting/action-types/server-log.asciidoc
@@ -12,6 +12,17 @@ Server log connectors have the following configuration properties:
Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action.
+[float]
+[[Preconfigured-server-log-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ my-server-log:
+ name: test
+ actionTypeId: .server-log
+--
+
[float]
[[server-log-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/action-types/slack.asciidoc b/docs/user/alerting/action-types/slack.asciidoc
index c0965d65bfdbe..afa616ba77b3a 100644
--- a/docs/user/alerting/action-types/slack.asciidoc
+++ b/docs/user/alerting/action-types/slack.asciidoc
@@ -13,6 +13,24 @@ Slack connectors have the following configuration properties:
Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action.
Webhook URL:: The URL of the incoming webhook. See https://api.slack.com/messaging/webhooks#getting_started[Slack Incoming Webhooks] for instructions on generating this URL. If you are using the <> setting, make sure the hostname is whitelisted.
+[float]
+[[Preconfigured-slack-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ my-slack:
+ name: preconfigured-slack-action-type
+ actionTypeId: .slack
+ config:
+ webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz' <1>
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+<1> `webhookUrl:` is URL string and correspond to *Webhook URL*.
+
+
[float]
[[slack-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/action-types/webhook.asciidoc b/docs/user/alerting/action-types/webhook.asciidoc
index 64bfa6a1d6364..27609652288b5 100644
--- a/docs/user/alerting/action-types/webhook.asciidoc
+++ b/docs/user/alerting/action-types/webhook.asciidoc
@@ -17,6 +17,36 @@ Headers:: A set of key-value pairs sent as headers with the request
User:: An optional username. If set, HTTP basic authentication is used. Currently only basic authentication is supported.
Password:: An optional password. If set, HTTP basic authentication is used. Currently only basic authentication is supported.
+[float]
+[[Preconfigured-webhook-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ my-webhook:
+ name: preconfigured-webhook-action-type
+ actionTypeId: .webhook
+ config:
+ url: https://test.host <1.1>
+ method: POST <1.2>
+ headers: <1.3>
+ testheader: testvalue
+ secrets:
+ user: testuser <2.1>
+ password: passwordkeystorevalue <2.2>
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+<1.1> `url:` is URL string and correspond to *URL*.
+<1.2> `method:` is a string and correspond to *Method*.
+<1.3> `headers:` is Record and correspond to *Headers*.
+
+`secrets` defines action type sensitive configuration:
+
+<2.1> `user:` is a string and correspond to *User*.
+<2.2> `password:` is a string and correspond to *Password*. Should be stored in the <>.
+
[float]
[[webhook-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/defining-alerts.asciidoc b/docs/user/alerting/defining-alerts.asciidoc
index f05afac34e595..d05a727016455 100644
--- a/docs/user/alerting/defining-alerts.asciidoc
+++ b/docs/user/alerting/defining-alerts.asciidoc
@@ -22,12 +22,12 @@ image::images/alert-flyout-sections.png[The three sections of an alert definitio
All alert share the following four properties in common:
[role="screenshot"]
-image::images/alert-flyout-general-details.png[All alerts have name, tags, check every, and re-notify every properties in common]
+image::images/alert-flyout-general-details.png[alt='All alerts have name, tags, check every, and notify every properties in common']
Name:: The name of the alert. While this name does not have to be unique, the name can be referenced in actions and also appears in the searchable alert listing in the management UI. A distinctive name can help identify and find an alert.
Tags:: A list of tag names that can be applied to an alert. Tags can help you organize and find alerts, because tags appear in the alert listing in the management UI which is searchable by tag.
Check every:: This value determines how frequently the alert conditions below are checked. Note that the timing of background alert checks are not guaranteed, particularly for intervals of less than 10 seconds. See <> for more information.
-Re-notify every:: This value limits how often actions are repeated when an alert instance remains active across alert checks. See <> for more information.
+Notify every:: This value limits how often actions are repeated when an alert instance remains active across alert checks. See <> for more information.
[float]
[[defining-alerts-type-conditions]]
diff --git a/docs/user/alerting/images/alert-flyout-action-type-selection.png b/docs/user/alerting/images/alert-flyout-action-type-selection.png
index e4448ca5f3fcd..2df2a031c6661 100644
Binary files a/docs/user/alerting/images/alert-flyout-action-type-selection.png and b/docs/user/alerting/images/alert-flyout-action-type-selection.png differ
diff --git a/docs/user/alerting/images/alert-flyout-alert-conditions.png b/docs/user/alerting/images/alert-flyout-alert-conditions.png
index f3e8f42ff0f37..8e0eff0224363 100644
Binary files a/docs/user/alerting/images/alert-flyout-alert-conditions.png and b/docs/user/alerting/images/alert-flyout-alert-conditions.png differ
diff --git a/docs/user/alerting/images/alert-flyout-alert-type-selection.png b/docs/user/alerting/images/alert-flyout-alert-type-selection.png
index a0a25dc5f1bbc..ccd3f07f07c94 100644
Binary files a/docs/user/alerting/images/alert-flyout-alert-type-selection.png and b/docs/user/alerting/images/alert-flyout-alert-type-selection.png differ
diff --git a/docs/user/alerting/images/alert-flyout-general-details.png b/docs/user/alerting/images/alert-flyout-general-details.png
index db56c16c1c308..883c2348ecc8a 100644
Binary files a/docs/user/alerting/images/alert-flyout-general-details.png and b/docs/user/alerting/images/alert-flyout-general-details.png differ
diff --git a/docs/user/alerting/images/alert-types-index-threshold-conditions.png b/docs/user/alerting/images/alert-types-index-threshold-conditions.png
index 356732dfb9777..5d66123ac733e 100644
Binary files a/docs/user/alerting/images/alert-types-index-threshold-conditions.png and b/docs/user/alerting/images/alert-types-index-threshold-conditions.png differ
diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-aggregation.png b/docs/user/alerting/images/alert-types-index-threshold-example-aggregation.png
index fc40da7436547..055b643ec3458 100644
Binary files a/docs/user/alerting/images/alert-types-index-threshold-example-aggregation.png and b/docs/user/alerting/images/alert-types-index-threshold-example-aggregation.png differ
diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-grouping.png b/docs/user/alerting/images/alert-types-index-threshold-example-grouping.png
index ea3a3849c8927..5be81b45612bc 100644
Binary files a/docs/user/alerting/images/alert-types-index-threshold-example-grouping.png and b/docs/user/alerting/images/alert-types-index-threshold-example-grouping.png differ
diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-index.png b/docs/user/alerting/images/alert-types-index-threshold-example-index.png
index 8f818f7001278..b13201ce5d38a 100644
Binary files a/docs/user/alerting/images/alert-types-index-threshold-example-index.png and b/docs/user/alerting/images/alert-types-index-threshold-example-index.png differ
diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-preview.png b/docs/user/alerting/images/alert-types-index-threshold-example-preview.png
index b5d9c38d99810..70e1355004c47 100644
Binary files a/docs/user/alerting/images/alert-types-index-threshold-example-preview.png and b/docs/user/alerting/images/alert-types-index-threshold-example-preview.png differ
diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-threshold.png b/docs/user/alerting/images/alert-types-index-threshold-example-threshold.png
index 9c51807b8d219..7e9432d8c8678 100644
Binary files a/docs/user/alerting/images/alert-types-index-threshold-example-threshold.png and b/docs/user/alerting/images/alert-types-index-threshold-example-threshold.png differ
diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-timefield.png b/docs/user/alerting/images/alert-types-index-threshold-example-timefield.png
index 24e4e03f829ce..4b1eaa631dc98 100644
Binary files a/docs/user/alerting/images/alert-types-index-threshold-example-timefield.png and b/docs/user/alerting/images/alert-types-index-threshold-example-timefield.png differ
diff --git a/docs/user/alerting/images/alert-types-index-threshold-example-window.png b/docs/user/alerting/images/alert-types-index-threshold-example-window.png
index 5405415958485..b4b272d2a241a 100644
Binary files a/docs/user/alerting/images/alert-types-index-threshold-example-window.png and b/docs/user/alerting/images/alert-types-index-threshold-example-window.png differ
diff --git a/docs/user/alerting/images/alert-types-index-threshold-preview.png b/docs/user/alerting/images/alert-types-index-threshold-preview.png
index 3709f162b612b..b3b868dbc41e8 100644
Binary files a/docs/user/alerting/images/alert-types-index-threshold-preview.png and b/docs/user/alerting/images/alert-types-index-threshold-preview.png differ
diff --git a/docs/user/alerting/images/alert-types-index-threshold-select.png b/docs/user/alerting/images/alert-types-index-threshold-select.png
index 0c2776e01b962..18c28a703e966 100644
Binary files a/docs/user/alerting/images/alert-types-index-threshold-select.png and b/docs/user/alerting/images/alert-types-index-threshold-select.png differ
diff --git a/docs/user/alerting/images/alerting-overview.png b/docs/user/alerting/images/alerting-overview.png
index 383bc8c2ce015..b4ec6f3df6028 100644
Binary files a/docs/user/alerting/images/alerting-overview.png and b/docs/user/alerting/images/alerting-overview.png differ
diff --git a/docs/user/alerting/images/pre-configured-action-type-select-type.png b/docs/user/alerting/images/pre-configured-action-type-select-type.png
index 5f555f851cd81..29e5a29edc7c0 100644
Binary files a/docs/user/alerting/images/pre-configured-action-type-select-type.png and b/docs/user/alerting/images/pre-configured-action-type-select-type.png differ
diff --git a/docs/user/alerting/pre-configured-action-types.asciidoc b/docs/user/alerting/pre-configured-action-types.asciidoc
deleted file mode 100644
index 780a2119037b1..0000000000000
--- a/docs/user/alerting/pre-configured-action-types.asciidoc
+++ /dev/null
@@ -1,61 +0,0 @@
-[role="xpack"]
-[[pre-configured-action-types]]
-
-== Preconfigured action types
-
-A preconfigure an action type has all the information it needs prior to startup.
-A preconfigured action type offers the following capabilities:
-
-- Requires no setup. Configuration and credentials needed to execute an
-action are predefined.
-- Has only <>.
-- Connectors of the preconfigured action type cannot be edited or deleted.
-
-[float]
-[[preconfigured-action-type-example]]
-=== Creating a preconfigured action
-
-In the `kibana.yml` file:
-
-. Exclude the action type from `xpack.actions.enabledActionTypes`.
-. Add all its connectors.
-
-The following example shows a valid configuration of preconfigured action type with one out-of-the box connector.
-
-```js
- xpack.actions.enabledActionTypes: ['.slack', '.email', '.index'] <1>
- xpack.actions.preconfigured: <2>
- - id: 'my-server-log'
- actionTypeId: .server-log
- name: 'Server log #xyz'
-```
-
-<1> `enabledActionTypes` should exclude preconfigured action type to prevent creating and deleting connectors.
-<2> `preconfigured` is the setting for defining the list of available connectors for the preconfigured action type.
-
-[float]
-[[pre-configured-action-type-alert-form]]
-=== Attaching a preconfigured action to an alert
-
-To attach an action to an alert,
-select from a list of available action types, and
-then select the *Server log* type. This action type was configured previously.
-
-[role="screenshot"]
-image::images/pre-configured-action-type-alert-form.png[Create alert with selected Server log action type]
-
-[float]
-[[managing-pre-configured-action-types]]
-=== Managing preconfigured actions
-
-Connectors with preconfigured actions 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-action-type-managing.png[Connectors managing tab with pre-cofigured]
-
-Clicking *Create connector* shows the list of available action types.
-Preconfigured action types are not included because you can't create a connector with a preconfigured action type.
-
-[role="screenshot"]
-image::images/pre-configured-action-type-select-type.png[Pre-configured connector create menu]
diff --git a/docs/user/alerting/pre-configured-connectors.asciidoc b/docs/user/alerting/pre-configured-connectors.asciidoc
index 4c408da92f579..d5c20d1853d42 100644
--- a/docs/user/alerting/pre-configured-connectors.asciidoc
+++ b/docs/user/alerting/pre-configured-connectors.asciidoc
@@ -1,11 +1,10 @@
[role="xpack"]
-[[pre-configured-connectors]]
+[[pre-configured-action-types-and-connectors]]
-== Preconfigured connectors
+== Preconfigured connectors and action types
-You can preconfigure an action connector to have all the information it needs prior to startup
+You can preconfigure an action type or a 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:
@@ -14,20 +13,24 @@ action are predefined, including the connector name and ID.
- Appear in all spaces because they are not saved objects.
- Cannot be edited or deleted.
+Sensitive configuration information, such as credentials, can use the <>.
+
+A preconfigured action types has only preconfigured connectors. Preconfigured connectors can belong to either the preconfigured action type or to the regular action type.
+
[float]
[[preconfigured-connector-example]]
-=== Example of a preconfigured connector
+=== Creating a preconfigured connector
-The following example shows a valid configuration 2 out-of-the box connector.
+The following example shows a valid configuration of two out-of-the box connectors: <> and <>.
```js
xpack.actions.preconfigured:
- - id: 'my-slack1' <1>
+ my-slack1: <1>
actionTypeId: .slack <2>
name: 'Slack #xyz' <3>
config: <4>
webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz'
- - id: 'webhook-service'
+ webhook-service:
actionTypeId: .webhook
name: 'Email service'
config:
@@ -41,7 +44,7 @@ The following example shows a valid configuration 2 out-of-the box connector.
password: changeme
```
-<1> `id` is the action connector identifier.
+<1> the key is the action connector identifier, eg `my-slack1` in this example.
<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.
@@ -49,26 +52,30 @@ The following example shows a valid configuration 2 out-of-the box connector.
[NOTE]
==============================================
-Sensitive properties, such as passwords, can also be stored in the {kib} keystore.
+Sensitive properties, such as passwords, can also be stored in the <>.
==============================================
[float]
-[[pre-configured-connector-alert-form]]
-=== Creating an alert with a preconfigured connector
+[[preconfigured-action-type-example]]
+=== Creating a preconfigured action type
-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.
+In the `kibana.yml` file:
-[role="screenshot"]
-image::images/alert-pre-configured-slack-connector.png[Create alert with selected Slack action type]
+. Exclude the action type from `xpack.actions.enabledActionTypes`.
+. Add all its preconfigured connectors.
-The dropdown is populated with additional preconfigured Slack connectors.
-The `preconfigured` label distinguishes them from space-aware connectors that use saved objects.
+The following example shows a valid configuration of preconfigured action type with one out-of-the box connector.
-[role="screenshot"]
-image::images/alert-pre-configured-connectors-dropdown.png[Dropdown list with pre-cofigured connectors]
+```js
+ xpack.actions.enabledActionTypes: ['.slack', '.email', '.index'] <1>
+ xpack.actions.preconfigured: <2>
+ my-server-log:
+ actionTypeId: .server-log
+ name: 'Server log #xyz'
+```
+
+<1> `enabledActionTypes` should exclude preconfigured action type to prevent creating and deleting connectors.
+<2> `preconfigured` is the setting for defining the list of available connectors for the preconfigured action type.
[float]
[[managing-pre-configured-connectors]]
@@ -85,3 +92,37 @@ A message indicates that this is a preconfigured connector.
[role="screenshot"]
image::images/pre-configured-connectors-view-screen.png[Pre-configured connector view details]
+
+The connector details preview is disabled for preconfigured connectors.
+
+[role="screenshot"]
+image::images/pre-configured-action-type-managing.png[Connectors managing tab with pre-cofigured]
+
+
+[float]
+[[managing-pre-configured-action-types]]
+=== Managing preconfigured action types
+
+Clicking *Create connector* shows the list of available action types.
+Disabled action types are not included.
+
+[role="screenshot"]
+image::images/pre-configured-action-type-select-type.png[Pre-configured connector create menu]
+
+[float]
+[[pre-configured-connector-alert-form]]
+=== 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]
diff --git a/docs/visualize/timelion.asciidoc b/docs/visualize/timelion.asciidoc
index 852c3e1ecdeca..9e41cce561454 100644
--- a/docs/visualize/timelion.asciidoc
+++ b/docs/visualize/timelion.asciidoc
@@ -32,7 +32,9 @@ To start tracking the real-time percentage of CPU, enter the following in the *T
[source,text]
----------------------------------
-.es(index=metricbeat-*, timefield='@timestamp', metric='avg:system.cpu.user.pct')
+.es(index=metricbeat-*,
+ timefield='@timestamp',
+ metric='avg:system.cpu.user.pct')
----------------------------------
[role="screenshot"]
@@ -70,7 +72,12 @@ To easily distinguish between the two data sets, add the label names:
[source,text]
----------------------------------
-.es(offset=-1h,index=metricbeat-*, timefield='@timestamp', metric='avg:system.cpu.user.pct').label('last hour'), .es(index=metricbeat-*, timefield='@timestamp', metric='avg:system.cpu.user.pct').label('current hour') <1>
+.es(offset=-1h,index=metricbeat-*,
+ timefield='@timestamp',
+ metric='avg:system.cpu.user.pct').label('last hour'),
+.es(index=metricbeat-*,
+ timefield='@timestamp',
+ metric='avg:system.cpu.user.pct').label('current hour') <1>
----------------------------------
<1> `.label()` adds custom labels to the visualization.
diff --git a/docs/visualize/tsvb.asciidoc b/docs/visualize/tsvb.asciidoc
index 36709c2cc6437..9a1e81670b654 100644
--- a/docs/visualize/tsvb.asciidoc
+++ b/docs/visualize/tsvb.asciidoc
@@ -122,3 +122,17 @@ Edit the source for the Markdown visualization.
. To insert the mustache template variable into the editor, click the variable name.
+
The http://mustache.github.io/mustache.5.html[mustache syntax] uses the Handlebar.js processor, which is an extended version of the Mustache template language.
+
+[float]
+[[tsvb-style-markdown]]
+==== Style Markdown text
+
+Style your Markdown visualization using http://lesscss.org/features/[less syntax].
+
+. Select *Markdown*.
+
+. Select *Panel options*.
+
+. Enter styling rules in *Custom CSS* section
++
+Less in TSVB does not support custom plugins or inline JavaScript.
diff --git a/package.json b/package.json
index 8a92b46489308..0c83cb429b651 100644
--- a/package.json
+++ b/package.json
@@ -210,7 +210,7 @@
"leaflet-responsive-popup": "0.6.4",
"leaflet-vega": "^0.8.6",
"leaflet.heat": "0.2.0",
- "less": "^2.7.3",
+ "less": "npm:@elastic/less@2.7.3-kibana",
"less-loader": "5.0.0",
"lodash": "npm:@elastic/lodash@3.10.1-kibana4",
"lodash.clonedeep": "^4.5.0",
diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/README.md b/packages/kbn-dev-utils/src/ci_stats_reporter/README.md
index 6133f9871699f..c7b98224c4e57 100644
--- a/packages/kbn-dev-utils/src/ci_stats_reporter/README.md
+++ b/packages/kbn-dev-utils/src/ci_stats_reporter/README.md
@@ -8,7 +8,7 @@ This class integrates with the `ciStats.trackBuild {}` Jenkins Pipeline function
To create an instance of the reporter, import the class and call `CiStatsReporter.fromEnv(log)` (passing it a tooling log).
-#### `CiStatsReporter#metric(name: string, subName: string, value: number)`
+#### `CiStatsReporter#metrics(metrics: Array<{ group: string, id: string, value: number }>)`
Use this method to record metrics in the Kibana CI Stats service.
@@ -19,5 +19,11 @@ import { CiStatsReporter, ToolingLog } from '@kbn/dev-utils';
const log = new ToolingLog(...);
const reporter = CiStatsReporter.fromEnv(log)
-reporter.metric('Build speed', specificBuildName, timeToRunBuild)
+reporter.metrics([
+ {
+ group: 'Build size',
+ id: specificBuildName,
+ value: sizeOfBuild
+ }
+])
```
\ No newline at end of file
diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts
index 5fe1844a85563..4e91289610432 100644
--- a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts
+++ b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts
@@ -84,13 +84,16 @@ export class CiStatsReporter {
return !!this.config;
}
- async metric(name: string, subName: string, value: number) {
+ async metrics(metrics: Array<{ group: string; id: string; value: number }>) {
if (!this.config) {
return;
}
let attempt = 0;
const maxAttempts = 5;
+ const bodySummary = metrics
+ .map(({ group, id, value }) => `[${group}/${id}=${value}]`)
+ .join(' ');
while (true) {
attempt += 1;
@@ -98,18 +101,14 @@ export class CiStatsReporter {
try {
await Axios.request({
method: 'POST',
- url: '/metric',
+ url: '/v1/metrics',
baseURL: this.config.apiUrl,
- params: {
- buildId: this.config.buildId,
- },
headers: {
Authorization: `token ${this.config.apiToken}`,
},
data: {
- name,
- subName,
- value,
+ buildId: this.config.buildId,
+ metrics,
},
});
@@ -125,14 +124,14 @@ export class CiStatsReporter {
this.log.warning(
`error recording metric [status=${error.response.status}] [resp=${inspect(
error.response.data
- )}] [${name}/${subName}=${value}]`
+ )}] ${bodySummary}`
);
return;
}
if (attempt === maxAttempts) {
this.log.warning(
- `failed to reach kibana-ci-stats service too many times, unable to record metric [${name}/${subName}=${value}]`
+ `failed to reach kibana-ci-stats service too many times, unable to record metric ${bodySummary}`
);
return;
}
diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts
index e46075eff63a7..a2fbe969e34d8 100644
--- a/packages/kbn-optimizer/src/cli.ts
+++ b/packages/kbn-optimizer/src/cli.ts
@@ -21,7 +21,7 @@ import 'source-map-support/register';
import Path from 'path';
-import { run, REPO_ROOT, createFlagError, createFailError, CiStatsReporter } from '@kbn/dev-utils';
+import { run, REPO_ROOT, createFlagError, CiStatsReporter } from '@kbn/dev-utils';
import { logOptimizerState } from './log_optimizer_state';
import { OptimizerConfig } from './optimizer';
@@ -82,9 +82,9 @@ run(
throw createFlagError('expected --scan-dir to be a string');
}
- const reportStatsName = flags['report-stats'];
- if (reportStatsName !== undefined && typeof reportStatsName !== 'string') {
- throw createFlagError('expected --report-stats to be a string');
+ const reportStats = flags['report-stats'] ?? false;
+ if (typeof reportStats !== 'boolean') {
+ throw createFlagError('expected --report-stats to have no value');
}
const config = OptimizerConfig.create({
@@ -103,22 +103,32 @@ run(
let update$ = runOptimizer(config);
- if (reportStatsName) {
+ if (reportStats) {
const reporter = CiStatsReporter.fromEnv(log);
if (!reporter.isEnabled()) {
- throw createFailError('Unable to initialize CiStatsReporter from env');
+ log.warning('Unable to initialize CiStatsReporter from env');
}
- update$ = update$.pipe(reportOptimizerStats(reporter, reportStatsName));
+ update$ = update$.pipe(reportOptimizerStats(reporter, config));
}
await update$.pipe(logOptimizerState(log, config)).toPromise();
},
{
flags: {
- boolean: ['core', 'watch', 'oss', 'examples', 'dist', 'cache', 'profile', 'inspect-workers'],
- string: ['workers', 'scan-dir', 'report-stats'],
+ boolean: [
+ 'core',
+ 'watch',
+ 'oss',
+ 'examples',
+ 'dist',
+ 'cache',
+ 'profile',
+ 'inspect-workers',
+ 'report-stats',
+ ],
+ string: ['workers', 'scan-dir'],
default: {
core: true,
examples: true,
@@ -136,7 +146,7 @@ run(
--dist create bundles that are suitable for inclusion in the Kibana distributable
--scan-dir add a directory to the list of directories scanned for plugins (specify as many times as necessary)
--no-inspect-workers when inspecting the parent process, don't inspect the workers
- --report-stats=[name] attempt to report stats about this execution of the build to the kibana-ci-stats service using this name
+ --report-stats attempt to report stats about this execution of the build to the kibana-ci-stats service using this name
`,
},
}
diff --git a/packages/kbn-optimizer/src/report_optimizer_stats.ts b/packages/kbn-optimizer/src/report_optimizer_stats.ts
index 375978b9b7944..06161fb2567b9 100644
--- a/packages/kbn-optimizer/src/report_optimizer_stats.ts
+++ b/packages/kbn-optimizer/src/report_optimizer_stats.ts
@@ -21,10 +21,10 @@ import { materialize, mergeMap, dematerialize } from 'rxjs/operators';
import { CiStatsReporter } from '@kbn/dev-utils';
import { OptimizerUpdate$ } from './run_optimizer';
-import { OptimizerState } from './optimizer';
+import { OptimizerState, OptimizerConfig } from './optimizer';
import { pipeClosure } from './common';
-export function reportOptimizerStats(reporter: CiStatsReporter, name: string) {
+export function reportOptimizerStats(reporter: CiStatsReporter, config: OptimizerConfig) {
return pipeClosure((update$: OptimizerUpdate$) => {
let lastState: OptimizerState | undefined;
return update$.pipe(
@@ -35,7 +35,18 @@ export function reportOptimizerStats(reporter: CiStatsReporter, name: string) {
}
if (n.kind === 'C' && lastState) {
- await reporter.metric('@kbn/optimizer build time', name, lastState.durSec);
+ await reporter.metrics(
+ config.bundles.map(bundle => {
+ // make the cache read from the cache file since it was likely updated by the worker
+ bundle.cache.refresh();
+
+ return {
+ group: `@kbn/optimizer bundle module count`,
+ id: bundle.id,
+ value: bundle.cache.getModuleCount() || 0,
+ };
+ })
+ );
}
return n;
diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts
index 95e826e7620aa..49bcc6e7e704c 100644
--- a/packages/kbn-optimizer/src/worker/webpack.config.ts
+++ b/packages/kbn-optimizer/src/worker/webpack.config.ts
@@ -137,9 +137,9 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) {
// or which have require() statements that should be ignored because the file is
// already bundled with all its necessary depedencies
noParse: [
- /[\///]node_modules[\///]elasticsearch-browser[\///]/,
- /[\///]node_modules[\///]lodash[\///]index\.js$/,
- /[\///]node_modules[\///]vega-lib[\///]build[\///]vega\.js$/,
+ /[\/\\]node_modules[\/\\]elasticsearch-browser[\/\\]/,
+ /[\/\\]node_modules[\/\\]lodash[\/\\]index\.js$/,
+ /[\/\\]node_modules[\/\\]vega-lib[\/\\]build[\/\\]vega\.js$/,
],
rules: [
diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js
index 28cf36dedba3f..1b70cced4a5c9 100644
--- a/packages/kbn-pm/dist/index.js
+++ b/packages/kbn-pm/dist/index.js
@@ -43933,30 +43933,29 @@ class CiStatsReporter {
isEnabled() {
return !!this.config;
}
- async metric(name, subName, value) {
+ async metrics(metrics) {
var _a, _b, _c, _d;
if (!this.config) {
return;
}
let attempt = 0;
const maxAttempts = 5;
+ const bodySummary = metrics
+ .map(({ group, id, value }) => `[${group}/${id}=${value}]`)
+ .join(' ');
while (true) {
attempt += 1;
try {
await axios_1.default.request({
method: 'POST',
- url: '/metric',
+ url: '/v1/metrics',
baseURL: this.config.apiUrl,
- params: {
- buildId: this.config.buildId,
- },
headers: {
Authorization: `token ${this.config.apiToken}`,
},
data: {
- name,
- subName,
- value,
+ buildId: this.config.buildId,
+ metrics,
},
});
return;
@@ -43968,11 +43967,11 @@ class CiStatsReporter {
}
if (((_b = error) === null || _b === void 0 ? void 0 : _b.response) && error.response.status !== 502) {
// error response from service was received so warn the user and move on
- this.log.warning(`error recording metric [status=${error.response.status}] [resp=${util_1.inspect(error.response.data)}] [${name}/${subName}=${value}]`);
+ this.log.warning(`error recording metric [status=${error.response.status}] [resp=${util_1.inspect(error.response.data)}] ${bodySummary}`);
return;
}
if (attempt === maxAttempts) {
- this.log.warning(`failed to reach kibana-ci-stats service too many times, unable to record metric [${name}/${subName}=${value}]`);
+ this.log.warning(`failed to reach kibana-ci-stats service too many times, unable to record metric ${bodySummary}`);
return;
}
// we failed to reach the backend and we have remaining attempts, lets retry after a short delay
diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx
index 8442f1ecc6411..fd496da26283c 100644
--- a/src/core/public/application/application_service.tsx
+++ b/src/core/public/application/application_service.tsx
@@ -114,7 +114,9 @@ export class ApplicationService {
context,
http: { basePath },
injectedMetadata,
- redirectTo = (path: string) => (window.location.href = path),
+ redirectTo = (path: string) => {
+ window.location.assign(path);
+ },
history,
}: SetupDeps): InternalApplicationSetup {
const basename = basePath.get();
@@ -210,7 +212,10 @@ export class ApplicationService {
}
const appBasePath = basePath.prepend(appRoute);
- const mount: LegacyAppMounter = () => redirectTo(appBasePath);
+ const mount: LegacyAppMounter = ({ history: appHistory }) => {
+ redirectTo(appHistory.createHref(appHistory.location));
+ window.location.reload();
+ };
const { updater$, ...appProps } = app;
this.apps.set(app.id, {
diff --git a/src/core/public/application/integration_tests/application_service.test.tsx b/src/core/public/application/integration_tests/application_service.test.tsx
index 60c36d3e330e0..e399fbc726977 100644
--- a/src/core/public/application/integration_tests/application_service.test.tsx
+++ b/src/core/public/application/integration_tests/application_service.test.tsx
@@ -18,8 +18,10 @@
*/
import { take } from 'rxjs/operators';
-import { createRenderer } from './utils';
+import { act } from 'react-dom/test-utils';
import { createMemoryHistory, MemoryHistory } from 'history';
+
+import { createRenderer } from './utils';
import { ApplicationService } from '../application_service';
import { httpServiceMock } from '../../http/http_service.mock';
import { contextServiceMock } from '../../context/context_service.mock';
@@ -27,6 +29,9 @@ import { injectedMetadataServiceMock } from '../../injected_metadata/injected_me
import { MockLifecycle } from '../test_types';
import { overlayServiceMock } from '../../overlays/overlay_service.mock';
import { AppMountParameters } from '../types';
+import { ScopedHistory } from '../scoped_history';
+
+const flushPromises = () => new Promise(resolve => setImmediate(resolve));
describe('ApplicationService', () => {
let setupDeps: MockLifecycle<'setup'>;
@@ -83,7 +88,10 @@ describe('ApplicationService', () => {
expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1');
- resolveMount!();
+ await act(async () => {
+ resolveMount!();
+ await flushPromises();
+ });
expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1');
});
@@ -109,7 +117,7 @@ describe('ApplicationService', () => {
const { navigateToApp, currentAppId$ } = await service.start(startDeps);
- await navigateToApp('app1');
+ await act(() => navigateToApp('app1'));
expect(await currentAppId$.pipe(take(1)).toPromise()).toEqual('app1');
@@ -120,6 +128,46 @@ describe('ApplicationService', () => {
});
});
+ it('redirects to full path when navigating to legacy app', async () => {
+ const redirectTo = jest.fn();
+ const reloadSpy = jest.spyOn(window.location, 'reload').mockImplementation(() => {});
+
+ // In the real application, we use a BrowserHistory instance configured with `basename`. However, in tests we must
+ // use MemoryHistory which does not support `basename`. In order to emulate this behavior, we will wrap this
+ // instance with a ScopedHistory configured with a basepath.
+ history.push(setupDeps.http.basePath.get()); // ScopedHistory constructor will fail if underlying history is not currently at basePath.
+ const { register, registerLegacyApp } = service.setup({
+ ...setupDeps,
+ redirectTo,
+ history: new ScopedHistory(history, setupDeps.http.basePath.get()),
+ });
+
+ register(Symbol(), {
+ id: 'app1',
+ title: 'App1',
+ mount: ({ onAppLeave }: AppMountParameters) => {
+ onAppLeave(actions => actions.default());
+ return () => undefined;
+ },
+ });
+ registerLegacyApp({
+ id: 'myLegacyTestApp',
+ appUrl: '/app/myLegacyTestApp',
+ title: 'My Legacy Test App',
+ });
+
+ const { navigateToApp, getComponent } = await service.start(startDeps);
+
+ update = createRenderer(getComponent());
+
+ await navigate('/test/app/app1');
+ await act(() => navigateToApp('myLegacyTestApp', { path: '#/some-path' }));
+
+ expect(redirectTo).toHaveBeenCalledWith('/test/app/myLegacyTestApp#/some-path');
+ expect(reloadSpy).toHaveBeenCalled();
+ reloadSpy.mockRestore();
+ });
+
describe('leaving an application that registered an app leave handler', () => {
it('navigates to the new app if action is default', async () => {
startDeps.overlays.openConfirm.mockResolvedValue(true);
@@ -146,8 +194,10 @@ describe('ApplicationService', () => {
update = createRenderer(getComponent());
- await navigate('/app/app1');
- await navigateToApp('app2');
+ await act(async () => {
+ await navigate('/app/app1');
+ await navigateToApp('app2');
+ });
expect(startDeps.overlays.openConfirm).not.toHaveBeenCalled();
expect(history.entries.length).toEqual(3);
@@ -179,8 +229,10 @@ describe('ApplicationService', () => {
update = createRenderer(getComponent());
- await navigate('/app/app1');
- await navigateToApp('app2');
+ await act(async () => {
+ await navigate('/app/app1');
+ await navigateToApp('app2');
+ });
expect(startDeps.overlays.openConfirm).toHaveBeenCalledTimes(1);
expect(startDeps.overlays.openConfirm).toHaveBeenCalledWith(
@@ -216,8 +268,10 @@ describe('ApplicationService', () => {
update = createRenderer(getComponent());
- await navigate('/app/app1');
- await navigateToApp('app2');
+ await act(async () => {
+ await navigate('/app/app1');
+ await navigateToApp('app2');
+ });
expect(startDeps.overlays.openConfirm).toHaveBeenCalledTimes(1);
expect(startDeps.overlays.openConfirm).toHaveBeenCalledWith(
diff --git a/src/core/public/chrome/ui/_loading_indicator.scss b/src/core/public/chrome/ui/_loading_indicator.scss
index 026c23b93b040..ad934717b4b76 100644
--- a/src/core/public/chrome/ui/_loading_indicator.scss
+++ b/src/core/public/chrome/ui/_loading_indicator.scss
@@ -11,7 +11,7 @@ $kbnLoadingIndicatorColor2: tint($euiColorAccent, 60%);
top: 0; // 1
left: 0; // 1
right: 0; // 1
- z-index: $euiZLevel1; // 1
+ z-index: $euiZLevel2; // 1
overflow: hidden; // 2
height: $euiSizeXS / 2;
@@ -28,7 +28,7 @@ $kbnLoadingIndicatorColor2: tint($euiColorAccent, 60%);
right: 0;
bottom: 0;
position: absolute;
- z-index: $euiZLevel1 + 1;
+ z-index: $euiZLevel2 + 1;
visibility: visible;
display: block;
animation: kbn-animate-loading-indicator 2s linear infinite;
diff --git a/src/core/server/logging/README.md b/src/core/server/logging/README.md
index ed64e7c4ce0b1..553dc7c36e824 100644
--- a/src/core/server/logging/README.md
+++ b/src/core/server/logging/README.md
@@ -167,7 +167,7 @@ logging:
- context: plugins
appenders: [custom]
level: warn
- - context: plugins.pid
+ - context: plugins.myPlugin
level: info
- context: server
level: fatal
@@ -180,14 +180,14 @@ logging:
Here is what we get with the config above:
-| Context | Appenders | Level |
-| ------------- |:------------------------:| -----:|
-| root | console, file | error |
-| plugins | custom | warn |
-| plugins.pid | custom | info |
-| server | console, file | fatal |
-| optimize | console | error |
-| telemetry | json-file-appender | all |
+| Context | Appenders | Level |
+| ---------------- |:------------------------:| -----:|
+| root | console, file | error |
+| plugins | custom | warn |
+| plugins.myPlugin | custom | info |
+| server | console, file | fatal |
+| optimize | console | error |
+| telemetry | json-file-appender | all |
The `root` logger has a dedicated configuration node since this context is special and should always exist. By
@@ -259,7 +259,7 @@ define a custom one.
```yaml
logging:
loggers:
- - context: your-plugin
+ - context: plugins.myPlugin
appenders: [console]
```
Logs in a *file* if given file path. You should define a custom appender with `kind: file`
@@ -273,7 +273,7 @@ logging:
layout:
kind: pattern
loggers:
- - context: your-plugin
+ - context: plugins.myPlugin
appenders: [file]
```
#### logging.json
@@ -282,10 +282,10 @@ the output format with [layouts](#layouts).
#### logging.quiet
Suppresses all logging output other than error messages. With new logging, config can be achieved
-with adjusting minimum required [logging level](#log-level)
+with adjusting minimum required [logging level](#log-level).
```yaml
loggers:
- - context: my-plugin
+ - context: plugins.myPlugin
appenders: [console]
level: error
# or for all output
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 3ec478e3ca28d..bd10520ca1c57 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
@@ -293,7 +293,7 @@ describe('DocumentMigrator', () => {
migrationVersion: { dog: '10.2.0' },
})
).toThrow(
- /Document "smelly" has property "dog" which belongs to a more recent version of Kibana \(10\.2\.0\)/i
+ /Document "smelly" has property "dog" which belongs to a more recent version of Kibana \[10\.2\.0\]\. The last known version is \[undefined\]/i
);
});
@@ -315,7 +315,7 @@ describe('DocumentMigrator', () => {
migrationVersion: { dawg: '1.2.4' },
})
).toThrow(
- /Document "fleabag" has property "dawg" which belongs to a more recent version of Kibana \(1\.2\.4\)/i
+ /Document "fleabag" has property "dawg" which belongs to a more recent version of Kibana \[1\.2\.4\]\. The last known version is \[1\.2\.3\]/i
);
});
diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts
index 4ddb2b070d3ac..07c1da5586107 100644
--- a/src/core/server/saved_objects/migrations/core/document_migrator.ts
+++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts
@@ -350,7 +350,7 @@ function nextUnmigratedProp(doc: SavedObjectUnsanitizedDoc, migrations: ActiveMi
if (docVersion && (!latestVersion || Semver.gt(docVersion, latestVersion))) {
throw Boom.badData(
`Document "${doc.id}" has property "${p}" which belongs to a more recent` +
- ` version of Kibana (${docVersion}).`,
+ ` version of Kibana [${docVersion}]. The last known version is [${latestVersion}]`,
doc
);
}
diff --git a/src/core/server/saved_objects/migrations/core/index_migrator.ts b/src/core/server/saved_objects/migrations/core/index_migrator.ts
index c75fa68572c71..ef2a8870d78d0 100644
--- a/src/core/server/saved_objects/migrations/core/index_migrator.ts
+++ b/src/core/server/saved_objects/migrations/core/index_migrator.ts
@@ -195,7 +195,7 @@ async function migrateSourceToDest(context: Context) {
await Index.write(
callCluster,
dest.indexName,
- migrateRawDocs(serializer, documentMigrator.migrate, docs)
+ migrateRawDocs(serializer, documentMigrator.migrate, docs, log)
);
}
}
diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts
index 89f3fde384848..e55b72be2436d 100644
--- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts
+++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts
@@ -21,6 +21,7 @@ import _ from 'lodash';
import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry';
import { SavedObjectsSerializer } from '../../serialization';
import { migrateRawDocs } from './migrate_raw_docs';
+import { createSavedObjectsMigrationLoggerMock } from '../../migrations/mocks';
describe('migrateRawDocs', () => {
test('converts raw docs to saved objects', async () => {
@@ -31,7 +32,8 @@ describe('migrateRawDocs', () => {
[
{ _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } },
{ _id: 'c:d', _source: { type: 'c', c: { name: 'DDD' } } },
- ]
+ ],
+ createSavedObjectsMigrationLoggerMock()
);
expect(result).toEqual([
@@ -48,7 +50,8 @@ describe('migrateRawDocs', () => {
expect(transform).toHaveBeenCalled();
});
- test('passes invalid docs through untouched', async () => {
+ test('passes invalid docs through untouched and logs error', async () => {
+ const logger = createSavedObjectsMigrationLoggerMock();
const transform = jest.fn((doc: any) =>
_.set(_.cloneDeep(doc), 'attributes.name', 'TADA')
);
@@ -58,7 +61,8 @@ describe('migrateRawDocs', () => {
[
{ _id: 'foo:b', _source: { type: 'a', a: { name: 'AAA' } } },
{ _id: 'c:d', _source: { type: 'c', c: { name: 'DDD' } } },
- ]
+ ],
+ logger
);
expect(result).toEqual([
@@ -82,5 +86,7 @@ describe('migrateRawDocs', () => {
},
],
]);
+
+ expect(logger.error).toBeCalledTimes(1);
});
});
diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts
index 5fe15f40db8ec..49acea82e1c8a 100644
--- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts
+++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts
@@ -23,6 +23,7 @@
import { SavedObjectsRawDoc, SavedObjectsSerializer } from '../../serialization';
import { TransformFn } from './document_migrator';
+import { SavedObjectsMigrationLogger } from '.';
/**
* Applies the specified migration function to every saved object document in the list
@@ -35,7 +36,8 @@ import { TransformFn } from './document_migrator';
export function migrateRawDocs(
serializer: SavedObjectsSerializer,
migrateDoc: TransformFn,
- rawDocs: SavedObjectsRawDoc[]
+ rawDocs: SavedObjectsRawDoc[],
+ log: SavedObjectsMigrationLogger
): SavedObjectsRawDoc[] {
return rawDocs.map(raw => {
if (serializer.isRawSavedObject(raw)) {
@@ -47,6 +49,10 @@ export function migrateRawDocs(
});
}
+ log.error(
+ `Error: Unable to migrate the corrupt Saved Object document ${raw._id}. To prevent Kibana from performing a migration on every restart, please delete or fix this document by ensuring that the namespace and type in the document's id matches the values in the namespace and type fields.`,
+ { rawDocument: raw }
+ );
return raw;
});
}
diff --git a/src/core/server/saved_objects/migrations/core/migration_coordinator.test.ts b/src/core/server/saved_objects/migrations/core/migration_coordinator.test.ts
index 800edaeaa5885..3f2c31a7c0e5c 100644
--- a/src/core/server/saved_objects/migrations/core/migration_coordinator.test.ts
+++ b/src/core/server/saved_objects/migrations/core/migration_coordinator.test.ts
@@ -19,14 +19,10 @@
import _ from 'lodash';
import { coordinateMigration } from './migration_coordinator';
+import { createSavedObjectsMigrationLoggerMock } from '../mocks';
describe('coordinateMigration', () => {
- const log = {
- debug: jest.fn(),
- warning: jest.fn(),
- warn: jest.fn(),
- info: jest.fn(),
- };
+ const log = createSavedObjectsMigrationLoggerMock();
test('waits for isMigrated, if there is an index conflict', async () => {
const pollInterval = 1;
diff --git a/src/core/server/saved_objects/migrations/core/migration_logger.ts b/src/core/server/saved_objects/migrations/core/migration_logger.ts
index 9dfb3abc8e72d..00ed8bf0b73fc 100644
--- a/src/core/server/saved_objects/migrations/core/migration_logger.ts
+++ b/src/core/server/saved_objects/migrations/core/migration_logger.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { Logger } from 'src/core/server/logging';
+import { Logger, LogMeta } from '../../../logging';
/*
* This file provides a helper class for ensuring that all logging
@@ -35,6 +35,7 @@ export interface SavedObjectsMigrationLogger {
*/
warning: (msg: string) => void;
warn: (msg: string) => void;
+ error: (msg: string, meta: LogMeta) => void;
}
export class MigrationLogger implements SavedObjectsMigrationLogger {
@@ -48,4 +49,5 @@ export class MigrationLogger implements SavedObjectsMigrationLogger {
public debug = (msg: string) => this.logger.debug(msg);
public warning = (msg: string) => this.logger.warn(msg);
public warn = (msg: string) => this.logger.warn(msg);
+ public error = (msg: string, meta: LogMeta) => this.logger.error(msg, meta);
}
diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts
index dafd6c5341196..7d9ff9bed6d72 100644
--- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts
+++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts
@@ -22,9 +22,9 @@
* (the shape of the mappings and documents in the index).
*/
-import { Logger } from 'src/core/server/logging';
import { KibanaConfigType } from 'src/core/server/kibana_config';
import { BehaviorSubject } from 'rxjs';
+import { Logger } from '../../../logging';
import { IndexMapping, SavedObjectsTypeMappingDefinitions } from '../../mappings';
import { SavedObjectUnsanitizedDoc, SavedObjectsSerializer } from '../../serialization';
import { docValidator, PropertyValidators } from '../../validation';
diff --git a/src/core/server/saved_objects/migrations/mocks.ts b/src/core/server/saved_objects/migrations/mocks.ts
index 76a890d26bfa0..50a7191393472 100644
--- a/src/core/server/saved_objects/migrations/mocks.ts
+++ b/src/core/server/saved_objects/migrations/mocks.ts
@@ -20,12 +20,13 @@
import { SavedObjectMigrationContext } from './types';
import { SavedObjectsMigrationLogger } from './core';
-const createLoggerMock = (): jest.Mocked => {
+export const createSavedObjectsMigrationLoggerMock = (): jest.Mocked => {
const mock = {
debug: jest.fn(),
info: jest.fn(),
warning: jest.fn(),
warn: jest.fn(),
+ error: jest.fn(),
};
return mock;
@@ -33,7 +34,7 @@ const createLoggerMock = (): jest.Mocked => {
const createContextMock = (): jest.Mocked => {
const mock = {
- log: createLoggerMock(),
+ log: createSavedObjectsMigrationLoggerMock(),
};
return mock;
};
diff --git a/src/core/server/saved_objects/migrations/types.ts b/src/core/server/saved_objects/migrations/types.ts
index 85f15b4c18b66..5e55a34193a96 100644
--- a/src/core/server/saved_objects/migrations/types.ts
+++ b/src/core/server/saved_objects/migrations/types.ts
@@ -88,5 +88,5 @@ export interface SavedObjectMigrationContext {
* @public
*/
export interface SavedObjectMigrationMap {
- [version: string]: SavedObjectMigrationFn;
+ [version: string]: SavedObjectMigrationFn;
}
diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js
index 927171438ae99..c46fcfbc6dbd7 100644
--- a/src/core/server/saved_objects/service/lib/repository.test.js
+++ b/src/core/server/saved_objects/service/lib/repository.test.js
@@ -23,6 +23,7 @@ import { SavedObjectsErrorHelpers } from './errors';
import { SavedObjectsSerializer } from '../../serialization';
import { encodeHitVersion } from '../../version';
import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry';
+import { DocumentMigrator } from '../../migrations/core/document_migrator';
jest.mock('./search_dsl/search_dsl', () => ({ getSearchDsl: jest.fn() }));
@@ -115,6 +116,7 @@ describe('SavedObjectsRepository', () => {
const createType = type => ({
name: type,
mappings: { properties: mappings.properties[type].properties },
+ migrations: { '1.1.1': doc => doc },
});
const registry = new SavedObjectTypeRegistry();
@@ -144,6 +146,13 @@ describe('SavedObjectsRepository', () => {
namespaceType: 'agnostic',
});
+ const documentMigrator = new DocumentMigrator({
+ typeRegistry: registry,
+ kibanaVersion: '2.0.0',
+ log: {},
+ validateDoc: jest.fn(),
+ });
+
const getMockGetResponse = ({ type, id, references, namespace }) => ({
// NOTE: Elasticsearch returns more fields (_index, _type) but the SavedObjectsRepository method ignores these
found: true,
@@ -207,7 +216,7 @@ describe('SavedObjectsRepository', () => {
beforeEach(() => {
callAdminCluster = jest.fn();
migrator = {
- migrateDocument: jest.fn(doc => doc),
+ migrateDocument: jest.fn().mockImplementation(documentMigrator.migrate),
runMigrations: async () => ({ status: 'skipped' }),
};
@@ -424,9 +433,17 @@ describe('SavedObjectsRepository', () => {
const getMockBulkCreateResponse = (objects, namespace) => {
return {
- items: objects.map(({ type, id }) => ({
+ items: objects.map(({ type, id, attributes, references, migrationVersion }) => ({
create: {
_id: `${namespace ? `${namespace}:` : ''}${type}:${id}`,
+ _source: {
+ [type]: attributes,
+ type,
+ namespace,
+ references,
+ ...mockTimestampFields,
+ migrationVersion: migrationVersion || { [type]: '1.1.1' },
+ },
...mockVersionProps,
},
})),
@@ -474,7 +491,7 @@ describe('SavedObjectsRepository', () => {
const expectSuccessResult = obj => ({
...obj,
- migrationVersion: undefined,
+ migrationVersion: { [obj.type]: '1.1.1' },
version: mockVersion,
...mockTimestampFields,
});
@@ -619,13 +636,16 @@ describe('SavedObjectsRepository', () => {
};
const bulkCreateError = async (obj, esError, expectedError) => {
- const objects = [obj1, obj, obj2];
- const response = getMockBulkCreateResponse(objects);
+ let response;
if (esError) {
+ response = getMockBulkCreateResponse([obj1, obj, obj2]);
response.items[1].create = { error: esError };
+ } else {
+ response = getMockBulkCreateResponse([obj1, obj2]);
}
callAdminCluster.mockResolvedValue(response); // this._writeToCluster('bulk', ...)
+ const objects = [obj1, obj, obj2];
const result = await savedObjectsRepository.bulkCreate(objects);
expectClusterCalls('bulk');
const objCall = esError ? expectObjArgs(obj) : [];
@@ -781,7 +801,7 @@ describe('SavedObjectsRepository', () => {
id: 'three',
};
const objects = [obj1, obj, obj2];
- const response = getMockBulkCreateResponse(objects);
+ const response = getMockBulkCreateResponse([obj1, obj2]);
callAdminCluster.mockResolvedValue(response); // this._writeToCluster('bulk', ...)
const result = await savedObjectsRepository.bulkCreate(objects);
expect(callAdminCluster).toHaveBeenCalledTimes(1);
@@ -789,6 +809,32 @@ describe('SavedObjectsRepository', () => {
saved_objects: [expectSuccessResult(obj1), expectError(obj), expectSuccessResult(obj2)],
});
});
+
+ it(`a deserialized saved object`, async () => {
+ // Test for fix to https://github.com/elastic/kibana/issues/65088 where
+ // we returned raw ID's when an object without an id was created.
+ const namespace = 'myspace';
+ const response = getMockBulkCreateResponse([obj1, obj2], namespace);
+ callAdminCluster.mockResolvedValueOnce(response); // this._writeToCluster('bulk', ...)
+
+ // Bulk create one object with id unspecified, and one with id specified
+ const result = await savedObjectsRepository.bulkCreate([{ ...obj1, id: undefined }, obj2], {
+ namespace,
+ });
+
+ // Assert that both raw docs from the ES response are deserialized
+ expect(serializer.rawToSavedObject).toHaveBeenNthCalledWith(1, {
+ ...response.items[0].create,
+ _id: expect.stringMatching(/^myspace:config:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/),
+ });
+ expect(serializer.rawToSavedObject).toHaveBeenNthCalledWith(2, response.items[1].create);
+
+ // Assert that ID's are deserialized to remove the type and namespace
+ expect(result.saved_objects[0].id).toEqual(
+ expect.stringMatching(/^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/)
+ );
+ expect(result.saved_objects[1].id).toEqual(obj2.id);
+ });
});
});
@@ -1604,6 +1650,7 @@ describe('SavedObjectsRepository', () => {
version: mockVersion,
attributes,
references,
+ migrationVersion: { [type]: '1.1.1' },
});
});
});
diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts
index bc8ad2cdb0058..61027130e0eb7 100644
--- a/src/core/server/saved_objects/service/lib/repository.ts
+++ b/src/core/server/saved_objects/service/lib/repository.ts
@@ -18,6 +18,7 @@
*/
import { omit } from 'lodash';
+import uuid from 'uuid';
import { retryCallCluster } from '../../../elasticsearch/retry_call_cluster';
import { APICaller } from '../../../elasticsearch/';
@@ -299,6 +300,8 @@ export class SavedObjectsRepository {
const requiresNamespacesCheck =
method === 'index' && this._registry.isMultiNamespace(object.type);
+ if (object.id == null) object.id = uuid.v1();
+
return {
tag: 'Right' as 'Right',
value: {
@@ -404,35 +407,25 @@ export class SavedObjectsRepository {
}
const { requestedId, rawMigratedDoc, esRequestIndex } = expectedResult.value;
- const response = bulkResponse.items[esRequestIndex];
- const {
- error,
- _id: responseId,
- _seq_no: seqNo,
- _primary_term: primaryTerm,
- } = Object.values(response)[0] as any;
-
- const {
- _source: { type, [type]: attributes, references = [], namespaces },
- } = rawMigratedDoc;
-
- const id = requestedId || responseId;
+ const { error, ...rawResponse } = Object.values(
+ bulkResponse.items[esRequestIndex]
+ )[0] as any;
+
if (error) {
return {
- id,
- type,
- error: getBulkOperationError(error, type, id),
+ id: requestedId,
+ type: rawMigratedDoc._source.type,
+ error: getBulkOperationError(error, rawMigratedDoc._source.type, requestedId),
};
}
- return {
- id,
- type,
- ...(namespaces && { namespaces }),
- updated_at: time,
- version: encodeVersion(seqNo, primaryTerm),
- attributes,
- references,
- };
+
+ // When method == 'index' the bulkResponse doesn't include the indexed
+ // _source so we return rawMigratedDoc but have to spread the latest
+ // _seq_no and _primary_term values from the rawResponse.
+ return this._serializer.rawToSavedObject({
+ ...rawMigratedDoc,
+ ...{ _seq_no: rawResponse._seq_no, _primary_term: rawResponse._primary_term },
+ });
}),
};
}
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index 62d11ee7cf9a7..e4234689c25e8 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -91,7 +91,6 @@ import { IngestGetPipelineParams } from 'elasticsearch';
import { IngestPutPipelineParams } from 'elasticsearch';
import { IngestSimulateParams } from 'elasticsearch';
import { KibanaConfigType } from 'src/core/server/kibana_config';
-import { Logger as Logger_2 } from 'src/core/server/logging';
import { MGetParams } from 'elasticsearch';
import { MGetResponse } from 'elasticsearch';
import { MSearchParams } from 'elasticsearch';
@@ -1735,7 +1734,7 @@ export type SavedObjectMigrationFn;
}
// @public
@@ -2169,6 +2168,8 @@ export interface SavedObjectsMigrationLogger {
// (undocumented)
debug: (msg: string) => void;
// (undocumented)
+ error: (msg: string, meta: LogMeta) => void;
+ // (undocumented)
info: (msg: string) => void;
// (undocumented)
warn: (msg: string) => void;
diff --git a/src/dev/build/tasks/build_kibana_platform_plugins.js b/src/dev/build/tasks/build_kibana_platform_plugins.js
index 28d6b49f9e89a..153a3120f896f 100644
--- a/src/dev/build/tasks/build_kibana_platform_plugins.js
+++ b/src/dev/build/tasks/build_kibana_platform_plugins.js
@@ -39,11 +39,10 @@ export const BuildKibanaPlatformPluginsTask = {
});
const reporter = CiStatsReporter.fromEnv(log);
- const reportStatsName = build.isOss() ? 'oss distributable' : 'default distributable';
await runOptimizer(optimizerConfig)
.pipe(
- reportOptimizerStats(reporter, reportStatsName),
+ reportOptimizerStats(reporter, optimizerConfig),
logOptimizerState(log, optimizerConfig)
)
.toPromise();
diff --git a/src/dev/build/tasks/create_archives_task.js b/src/dev/build/tasks/create_archives_task.js
index 06be1bd0bd14f..541b9551dbc9b 100644
--- a/src/dev/build/tasks/create_archives_task.js
+++ b/src/dev/build/tasks/create_archives_task.js
@@ -17,13 +17,22 @@
* under the License.
*/
-import path from 'path';
+import Path from 'path';
+import Fs from 'fs';
+import { promisify } from 'util';
+
+import { CiStatsReporter } from '@kbn/dev-utils';
+
import { mkdirp, compress } from '../lib';
+const asyncStat = promisify(Fs.stat);
+
export const CreateArchivesTask = {
description: 'Creating the archives for each platform',
async run(config, log, build) {
+ const archives = [];
+
// archive one at a time, parallel causes OOM sometimes
for (const platform of config.getTargetPlatforms()) {
const source = build.resolvePathForPlatform(platform, '.');
@@ -31,10 +40,15 @@ export const CreateArchivesTask = {
log.info('archiving', source, 'to', destination);
- await mkdirp(path.dirname(destination));
+ await mkdirp(Path.dirname(destination));
- switch (path.extname(destination)) {
+ switch (Path.extname(destination)) {
case '.zip':
+ archives.push({
+ format: 'zip',
+ path: destination,
+ });
+
await compress(
'zip',
{
@@ -51,6 +65,11 @@ export const CreateArchivesTask = {
break;
case '.gz':
+ archives.push({
+ format: 'tar',
+ path: destination,
+ });
+
await compress(
'tar',
{
@@ -71,5 +90,20 @@ export const CreateArchivesTask = {
throw new Error(`Unexpected extension for archive destination: ${destination}`);
}
}
+
+ const reporter = CiStatsReporter.fromEnv(log);
+ if (reporter.isEnabled()) {
+ await reporter.metrics(
+ await Promise.all(
+ archives.map(async ({ format, path }) => {
+ return {
+ group: `${build.isOss() ? 'oss ' : ''}distributable size`,
+ id: format,
+ value: (await asyncStat(path)).size,
+ };
+ })
+ )
+ );
+ }
},
};
diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts
index a13f61af60173..5019c8bd22341 100644
--- a/src/dev/typescript/projects.ts
+++ b/src/dev/typescript/projects.ts
@@ -50,6 +50,9 @@ export const PROJECTS = [
...glob
.sync('test/plugin_functional/plugins/*/tsconfig.json', { cwd: REPO_ROOT })
.map(path => new Project(resolve(REPO_ROOT, path))),
+ ...glob
+ .sync('test/interpreter_functional/plugins/*/tsconfig.json', { cwd: REPO_ROOT })
+ .map(path => new Project(resolve(REPO_ROOT, path))),
];
export function filterProjectsByFlag(projectFlag?: string) {
diff --git a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js
index 9f5f4b764f9b0..691318e32245b 100644
--- a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js
+++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js
@@ -21,6 +21,9 @@ import Bluebird from 'bluebird';
import expect from '@kbn/expect';
import ngMock from 'ng_mock';
import $ from 'jquery';
+
+import 'leaflet/dist/leaflet.js';
+import 'leaflet-vega';
// Will be replaced with new path when tests are moved
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { createVegaVisualization } from '../../../../../../plugins/vis_type_vega/public/vega_visualization';
@@ -100,6 +103,39 @@ describe('VegaVisualizations', () => {
setSavedObjects(npStart.core.savedObjects);
setNotifications(npStart.core.notifications);
+ const mockMapConfig = {
+ includeElasticMapsService: true,
+ proxyElasticMapsServiceInMaps: false,
+ tilemap: {
+ deprecated: {
+ config: {
+ options: {
+ attribution: '',
+ },
+ },
+ },
+ options: {
+ attribution: '',
+ minZoom: 0,
+ maxZoom: 10,
+ },
+ },
+ regionmap: {
+ includeElasticMapsService: true,
+ layers: [],
+ },
+ manifestServiceUrl: '',
+ emsFileApiUrl: 'https://vector.maps.elastic.co',
+ emsTileApiUrl: 'https://tiles.maps.elastic.co',
+ emsLandingPageUrl: 'https://maps.elastic.co/v7.7',
+ emsFontLibraryUrl: 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf',
+ emsTileLayerId: {
+ bright: 'road_map',
+ desaturated: 'road_map_desaturated',
+ dark: 'dark_map',
+ },
+ };
+
beforeEach(ngMock.module('kibana'));
beforeEach(
ngMock.inject(() => {
@@ -127,7 +163,7 @@ describe('VegaVisualizations', () => {
return 'not found';
}
});
- const serviceSettings = new ServiceSettings();
+ const serviceSettings = new ServiceSettings(mockMapConfig, mockMapConfig.tilemap);
vegaVisualizationDependencies = {
serviceSettings,
core: {
diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js
index ad67a74121cc9..4e97d46ab1773 100644
--- a/src/legacy/core_plugins/kibana/public/kibana.js
+++ b/src/legacy/core_plugins/kibana/public/kibana.js
@@ -45,7 +45,6 @@ import 'ui/autoload/all';
import './management';
import './dev_tools';
import { showAppRedirectNotification } from '../../../../plugins/kibana_legacy/public';
-import 'leaflet';
import { localApplicationService } from './local_application_service';
npSetup.plugins.kibanaLegacy.registerLegacyAppAlias('doc', 'discover', { keepPrefix: true });
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/utils.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/utils.ts
index bdb1436c37efb..83335a6fabfeb 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/utils.ts
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/tabs/utils.ts
@@ -96,18 +96,21 @@ export function getTabs(
tabs.push({
name: getTitle('indexed', filteredCount, totalCount),
id: TAB_INDEXED_FIELDS,
+ 'data-test-subj': 'tab-indexedFields',
});
if (indexPatternListProvider.areScriptedFieldsEnabled(indexPattern)) {
tabs.push({
name: getTitle('scripted', filteredCount, totalCount),
id: TAB_SCRIPTED_FIELDS,
+ 'data-test-subj': 'tab-scriptedFields',
});
}
tabs.push({
name: getTitle('sourceFilters', filteredCount, totalCount),
id: TAB_SOURCE_FILTERS,
+ 'data-test-subj': 'tab-sourceFilters',
});
return tabs;
diff --git a/src/legacy/core_plugins/region_map/index.ts b/src/legacy/core_plugins/region_map/index.ts
deleted file mode 100644
index 8c059314786bc..0000000000000
--- a/src/legacy/core_plugins/region_map/index.ts
+++ /dev/null
@@ -1,49 +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 regionMapPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) =>
- new Plugin({
- id: 'region_map',
- require: ['kibana', 'elasticsearch'],
- publicDir: resolve(__dirname, 'public'),
- uiExports: {
- hacks: [resolve(__dirname, 'public/legacy')],
- injectDefaultVars(server) {
- const { regionmap } = server.config().get('map');
-
- return {
- regionmap,
- };
- },
- },
- 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 regionMapPluginInitializer;
diff --git a/src/legacy/core_plugins/region_map/public/legacy.ts b/src/legacy/core_plugins/region_map/public/legacy.ts
deleted file mode 100644
index 4bbd839331e56..0000000000000
--- a/src/legacy/core_plugins/region_map/public/legacy.ts
+++ /dev/null
@@ -1,35 +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 { RegionMapPluginSetupDependencies } from './plugin';
-import { plugin } from '.';
-
-const plugins: Readonly = {
- expressions: npSetup.plugins.expressions,
- visualizations: npSetup.plugins.visualizations,
- mapsLegacy: npSetup.plugins.mapsLegacy,
-};
-
-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/tile_map/index.ts b/src/legacy/core_plugins/tile_map/index.ts
deleted file mode 100644
index 27f019318a82b..0000000000000
--- a/src/legacy/core_plugins/tile_map/index.ts
+++ /dev/null
@@ -1,50 +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 tileMapPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) =>
- new Plugin({
- id: 'tile_map',
- require: ['kibana', 'elasticsearch'],
- publicDir: resolve(__dirname, 'public'),
- uiExports: {
- styleSheetPaths: resolve(__dirname, 'public/index.scss'),
- hacks: [resolve(__dirname, 'public/legacy')],
- injectDefaultVars: server => {
- const serverConfig = server.config();
- const mapConfig: Record = serverConfig.get('map');
-
- return {
- emsTileLayerId: mapConfig.emsTileLayerId,
- };
- },
- },
- 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 tileMapPluginInitializer;
diff --git a/src/legacy/ui/public/new_platform/new_platform.test.ts b/src/legacy/ui/public/new_platform/new_platform.test.ts
index 1629aac588a61..21e7b559f71f5 100644
--- a/src/legacy/ui/public/new_platform/new_platform.test.ts
+++ b/src/legacy/ui/public/new_platform/new_platform.test.ts
@@ -22,6 +22,7 @@ jest.mock('history');
import { setRootControllerMock, historyMock } from './new_platform.test.mocks';
import { legacyAppRegister, __reset__, __setup__, __start__ } from './new_platform';
import { coreMock } from '../../../../core/public/mocks';
+import { AppMount } from '../../../../core/public';
describe('ui/new_platform', () => {
describe('legacyAppRegister', () => {
@@ -33,7 +34,7 @@ describe('ui/new_platform', () => {
const registerApp = () => {
const unmountMock = jest.fn();
- const mountMock = jest.fn(() => unmountMock);
+ const mountMock = jest.fn, Parameters>(() => unmountMock);
legacyAppRegister({
id: 'test',
title: 'Test',
@@ -62,13 +63,25 @@ describe('ui/new_platform', () => {
controller(scopeMock, elementMock);
expect(mountMock).toHaveBeenCalledWith({
- element: elementMock[0],
+ element: expect.any(HTMLElement),
appBasePath: '/test/base/path/app/test',
onAppLeave: expect.any(Function),
history: historyMock,
});
});
+ test('app is mounted in new div inside containing element', () => {
+ const { mountMock } = registerApp();
+ const controller = setRootControllerMock.mock.calls[0][1];
+ const scopeMock = { $on: jest.fn() };
+ const elementMock = [document.createElement('div')];
+
+ controller(scopeMock, elementMock);
+
+ const { element } = mountMock.mock.calls[0][0];
+ expect(element.parentElement).toEqual(elementMock[0]);
+ });
+
test('controller calls deprecated context app.mount when invoked', () => {
const unmountMock = jest.fn();
// Two arguments changes how this is called.
@@ -84,7 +97,7 @@ describe('ui/new_platform', () => {
controller(scopeMock, elementMock);
expect(mountMock).toHaveBeenCalledWith(expect.any(Object), {
- element: elementMock[0],
+ element: expect.any(HTMLElement),
appBasePath: '/test/base/path/app/test',
onAppLeave: expect.any(Function),
history: historyMock,
diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts
index a15c7cce5511d..1eb46e1a43895 100644
--- a/src/legacy/ui/public/new_platform/new_platform.ts
+++ b/src/legacy/ui/public/new_platform/new_platform.ts
@@ -176,7 +176,8 @@ export const legacyAppRegister = (app: App) => {
legacyAppRegistered = true;
require('ui/chrome').setRootController(app.id, ($scope: IScope, $element: JQLite) => {
- const element = $element[0];
+ const element = document.createElement('div');
+ $element[0].appendChild(element);
// Root controller cannot return a Promise so use an internal async function and call it immediately
(async () => {
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 1bc85fa110ca0..698c124d2d805 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
@@ -301,7 +301,7 @@ exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = `
>
@@ -995,7 +995,7 @@ exports[`DashboardEmptyScreen renders correctly without visualize paragraph 1`]
>
diff --git a/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx b/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx
index 8bf205b8cb507..955d5244ce190 100644
--- a/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx
+++ b/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx
@@ -50,8 +50,8 @@ export function DashboardEmptyScreen({
}: DashboardEmptyScreenProps) {
const IS_DARK_THEME = uiSettings.get('theme:darkMode');
const emptyStateGraphicURL = IS_DARK_THEME
- ? '/plugins/kibana/home/assets/welcome_graphic_dark_2x.png'
- : '/plugins/kibana/home/assets/welcome_graphic_light_2x.png';
+ ? '/plugins/home/assets/welcome_graphic_dark_2x.png'
+ : '/plugins/home/assets/welcome_graphic_light_2x.png';
const linkToVisualizeParagraph = (
;
+
interface SetupDependencies {
data: DataPublicPluginSetup;
embeddable: EmbeddableSetup;
@@ -111,8 +117,10 @@ interface StartDependencies {
}
export type Setup = void;
+
export interface DashboardStart {
getSavedDashboardLoader: () => SavedObjectLoader;
+ dashboardUrlGenerator?: DashboardUrlGenerator;
}
declare module '../../../plugins/ui_actions/public' {
@@ -130,6 +138,8 @@ export class DashboardPlugin
private appStateUpdater = new BehaviorSubject(() => ({}));
private stopUrlTracking: (() => void) | undefined = undefined;
+ private dashboardUrlGenerator?: DashboardUrlGenerator;
+
public setup(
core: CoreSetup,
{ share, uiActions, embeddable, home, kibanaLegacy, data, usageCollection }: SetupDependencies
@@ -140,8 +150,8 @@ export class DashboardPlugin
const startServices = core.getStartServices();
if (share) {
- share.urlGenerators.registerUrlGenerator(
- createDirectAccessDashboardLinkGenerator(async () => {
+ this.dashboardUrlGenerator = share.urlGenerators.registerUrlGenerator(
+ createDashboardUrlGenerator(async () => {
const [coreStart, , selfStart] = await startServices;
return {
appBasePath: coreStart.application.getUrlForApp('dashboard'),
@@ -325,6 +335,7 @@ export class DashboardPlugin
});
return {
getSavedDashboardLoader: () => savedDashboardLoader,
+ dashboardUrlGenerator: this.dashboardUrlGenerator,
};
}
diff --git a/src/plugins/dashboard/public/url_generator.test.ts b/src/plugins/dashboard/public/url_generator.test.ts
index 248a3f991d6cb..68d447c4a1336 100644
--- a/src/plugins/dashboard/public/url_generator.test.ts
+++ b/src/plugins/dashboard/public/url_generator.test.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { createDirectAccessDashboardLinkGenerator } from './url_generator';
+import { createDashboardUrlGenerator } from './url_generator';
import { hashedItemStore } from '../../kibana_utils/public';
// eslint-disable-next-line
import { mockStorage } from '../../kibana_utils/public/storage/hashed_item_store/mock';
@@ -55,7 +55,7 @@ describe('dashboard url generator', () => {
});
test('creates a link to a saved dashboard', async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: false,
@@ -67,7 +67,7 @@ describe('dashboard url generator', () => {
});
test('creates a link with global time range set up', async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: false,
@@ -83,7 +83,7 @@ describe('dashboard url generator', () => {
});
test('creates a link with filters, time range, refresh interval and query to a saved object', async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: false,
@@ -123,7 +123,7 @@ describe('dashboard url generator', () => {
});
test('if no useHash setting is given, uses the one was start services', async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: true,
@@ -137,7 +137,7 @@ describe('dashboard url generator', () => {
});
test('can override a false useHash ui setting', async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: false,
@@ -152,7 +152,7 @@ describe('dashboard url generator', () => {
});
test('can override a true useHash ui setting', async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: true,
@@ -195,7 +195,7 @@ describe('dashboard url generator', () => {
};
test('attaches filters from destination dashboard', async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: false,
@@ -224,7 +224,7 @@ describe('dashboard url generator', () => {
});
test("doesn't fail if can't retrieve filters from destination dashboard", async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: false,
@@ -246,7 +246,7 @@ describe('dashboard url generator', () => {
});
test('can enforce empty filters', async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: false,
@@ -270,7 +270,7 @@ describe('dashboard url generator', () => {
});
test('no filters in result url if no filters applied', async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: false,
@@ -288,7 +288,7 @@ describe('dashboard url generator', () => {
});
test('can turn off preserving filters', async () => {
- const generator = createDirectAccessDashboardLinkGenerator(() =>
+ const generator = createDashboardUrlGenerator(() =>
Promise.resolve({
appBasePath: APP_BASE_PATH,
useHashedUrl: false,
diff --git a/src/plugins/dashboard/public/url_generator.ts b/src/plugins/dashboard/public/url_generator.ts
index 6f121ceb2d373..9d66f2df65777 100644
--- a/src/plugins/dashboard/public/url_generator.ts
+++ b/src/plugins/dashboard/public/url_generator.ts
@@ -75,7 +75,7 @@ export type DashboardAppLinkGeneratorState = UrlGeneratorState<{
preserveSavedFilters?: boolean;
}>;
-export const createDirectAccessDashboardLinkGenerator = (
+export const createDashboardUrlGenerator = (
getStartServices: () => Promise<{
appBasePath: string;
useHashedUrl: boolean;
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index d4433f3825fea..69dd97a881797 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -230,7 +230,6 @@ import {
validateIndexPattern,
getFromSavedObject,
flattenHitWrapper,
- getRoutes,
formatHitProvider,
} from './index_patterns';
@@ -246,8 +245,6 @@ export const indexPatterns = {
validate: validateIndexPattern,
getFromSavedObject,
flattenHitWrapper,
- // TODO: exported only in stub_index_pattern test. Move into data plugin and remove export.
- getRoutes,
formatHitProvider,
};
diff --git a/src/plugins/data/public/index_patterns/index.ts b/src/plugins/data/public/index_patterns/index.ts
index e05db0e4d4cec..58c2cae1de0f3 100644
--- a/src/plugins/data/public/index_patterns/index.ts
+++ b/src/plugins/data/public/index_patterns/index.ts
@@ -26,7 +26,6 @@ export {
getFromSavedObject,
isDefault,
} from './lib';
-export { getRoutes } from './utils';
export { flattenHitWrapper, formatHitProvider } from './index_patterns';
export { getIndexPatternFieldListCreator, Field, IIndexPatternFieldList } from './fields';
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts
index f39be78433710..98ec4495cef29 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts
+++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts
@@ -30,7 +30,7 @@ import {
import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../../../common';
-import { findByTitle, getRoutes } from '../utils';
+import { findByTitle } from '../utils';
import { IndexPatternMissingIndices } from '../lib';
import { Field, IIndexPatternFieldList, getIndexPatternFieldListCreator } from '../fields';
import { createFieldsFetcher } from './_fields_fetcher';
@@ -190,10 +190,6 @@ export class IndexPattern implements IIndexPattern {
return this.indexFields(forceFieldRefresh);
}
- public get routes() {
- return getRoutes();
- }
-
getComputedFields() {
const scriptFields: any = {};
if (!this.fields) {
diff --git a/src/plugins/data/public/index_patterns/utils.ts b/src/plugins/data/public/index_patterns/utils.ts
index 0ecc87f3080fd..c3f9af62f8c0e 100644
--- a/src/plugins/data/public/index_patterns/utils.ts
+++ b/src/plugins/data/public/index_patterns/utils.ts
@@ -48,13 +48,3 @@ export async function findByTitle(
(obj: SimpleSavedObject) => obj.get('title').toLowerCase() === title.toLowerCase()
);
}
-
-export function getRoutes() {
- return {
- edit: '/management/kibana/index_patterns/{{id}}',
- addField: '/management/kibana/index_patterns/{{id}}/create-field',
- indexedFields: '/management/kibana/index_patterns/{{id}}?_a=(tab:indexedFields)',
- scriptedFields: '/management/kibana/index_patterns/{{id}}?_a=(tab:scriptedFields)',
- sourceFilters: '/management/kibana/index_patterns/{{id}}?_a=(tab:sourceFilters)',
- };
-}
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index cb1e1d2bd0efe..ee56ad60441f4 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -912,14 +912,6 @@ export class IndexPattern implements IIndexPattern {
// (undocumented)
removeScriptedField(field: IFieldType): Promise;
// (undocumented)
- get routes(): {
- edit: string;
- addField: string;
- indexedFields: string;
- scriptedFields: string;
- sourceFilters: string;
- };
- // (undocumented)
save(saveAttempts?: number): Promise;
// (undocumented)
timeFieldName: string | undefined;
@@ -1021,7 +1013,6 @@ export const indexPatterns: {
validate: typeof validateIndexPattern;
getFromSavedObject: typeof getFromSavedObject;
flattenHitWrapper: typeof flattenHitWrapper;
- getRoutes: typeof getRoutes;
formatHitProvider: typeof formatHitProvider;
};
@@ -1812,27 +1803,26 @@ export type TSearchStrategyProvider = (context: ISearc
// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:179:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "getRoutes" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:379:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:380:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:237:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:237:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:237:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:237:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:237:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:237:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:374:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:374:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:374:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:374:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:376:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:377:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:400: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 "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts
diff --git a/src/plugins/data/public/search/legacy/fetch_soon.test.ts b/src/plugins/data/public/search/legacy/fetch_soon.test.ts
index b2e17798ccc9f..6c0467e3297e8 100644
--- a/src/plugins/data/public/search/legacy/fetch_soon.test.ts
+++ b/src/plugins/data/public/search/legacy/fetch_soon.test.ts
@@ -58,7 +58,7 @@ describe('fetchSoon', () => {
(callClient as jest.Mock).mockClear();
});
- test('should delay by 0ms if config is set to not batch searches', () => {
+ test('should execute asap if config is set to not batch searches', () => {
const config = getConfigStub({
'courier:batchSearches': false,
});
@@ -67,8 +67,6 @@ describe('fetchSoon', () => {
fetchSoon(request, options, { config } as FetchHandlers);
- expect(callClient).not.toBeCalled();
- jest.advanceTimersByTime(0);
expect(callClient).toBeCalled();
});
diff --git a/src/plugins/data/public/search/legacy/fetch_soon.ts b/src/plugins/data/public/search/legacy/fetch_soon.ts
index 18fa410a5bef0..83617d394fe95 100644
--- a/src/plugins/data/public/search/legacy/fetch_soon.ts
+++ b/src/plugins/data/public/search/legacy/fetch_soon.ts
@@ -67,6 +67,10 @@ async function delayedFetch(
fetchHandlers: FetchHandlers,
ms: number
) {
+ if (ms === 0) {
+ return callClient([request], [options], fetchHandlers)[0];
+ }
+
const i = requestsToFetch.length;
requestsToFetch = [...requestsToFetch, request];
requestOptions = [...requestOptions, options];
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index df4ba23244b4d..1f4076aa12bde 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -93,8 +93,7 @@ import { IngestGetPipelineParams } from 'elasticsearch';
import { IngestPutPipelineParams } from 'elasticsearch';
import { IngestSimulateParams } from 'elasticsearch';
import { KibanaConfigType as KibanaConfigType_2 } from 'src/core/server/kibana_config';
-import { Logger as Logger_2 } from 'src/core/server/logging';
-import { Logger as Logger_3 } from 'kibana/server';
+import { Logger as Logger_2 } from 'kibana/server';
import { MGetParams } from 'elasticsearch';
import { MGetResponse } from 'elasticsearch';
import moment from 'moment';
diff --git a/src/plugins/discover/public/application/_discover.scss b/src/plugins/discover/public/application/_discover.scss
index 8eaa66cf58624..590dbebdf4cfe 100644
--- a/src/plugins/discover/public/application/_discover.scss
+++ b/src/plugins/discover/public/application/_discover.scss
@@ -38,17 +38,7 @@ discover-app {
}
.dscResultCount {
- text-align: center;
padding-top: $euiSizeXS;
- padding-left: $euiSizeM;
-
- .dscResultHits {
- padding-left: $euiSizeXS;
- }
-
- > .kuiLink {
- padding-left: $euiSizeM;
- }
}
.dscTimechart__header {
diff --git a/src/plugins/discover/public/application/angular/discover.html b/src/plugins/discover/public/application/angular/discover.html
index b4db89b9275b4..a0f98ea38ef78 100644
--- a/src/plugins/discover/public/application/angular/discover.html
+++ b/src/plugins/discover/public/application/angular/discover.html
@@ -89,24 +89,12 @@
{{screenTitle}}
-
- {{(hits || 0) | number:0}}
-
-
-
+
+ ;
+
+ beforeAll(() => {
+ props = {
+ onResetQuery: jest.fn(),
+ showResetButton: true,
+ hits: 2,
+ };
+ });
+
+ it('HitsCounter renders a button by providing the showResetButton property', () => {
+ component = mountWithIntl();
+ expect(findTestSubject(component, 'resetSavedSearch').length).toBe(1);
+ });
+
+ it('HitsCounter not renders a button when the showResetButton property is false', () => {
+ component = mountWithIntl(
+
+ );
+ expect(findTestSubject(component, 'resetSavedSearch').length).toBe(0);
+ });
+
+ it('expect to render the number of hits', function() {
+ component = mountWithIntl();
+ const hits = findTestSubject(component, 'discoverQueryHits');
+ expect(hits.text()).toBe('2');
+ });
+
+ it('expect to render 1,899 hits if 1899 hits given', function() {
+ component = mountWithIntl(
+
+ );
+ const hits = findTestSubject(component, 'discoverQueryHits');
+ expect(hits.text()).toBe('1,899');
+ });
+
+ it('should reset query', function() {
+ component = mountWithIntl();
+ findTestSubject(component, 'resetSavedSearch').simulate('click');
+ expect(props.onResetQuery).toHaveBeenCalled();
+ });
+});
diff --git a/src/plugins/discover/public/application/components/hits_counter/hits_counter.tsx b/src/plugins/discover/public/application/components/hits_counter/hits_counter.tsx
new file mode 100644
index 0000000000000..1d2cd12877b1c
--- /dev/null
+++ b/src/plugins/discover/public/application/components/hits_counter/hits_counter.tsx
@@ -0,0 +1,83 @@
+/*
+ * 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 { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
+import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
+import { i18n } from '@kbn/i18n';
+import { formatNumWithCommas } from '../../helpers';
+
+export interface HitsCounterProps {
+ /**
+ * the number of query hits
+ */
+ hits: number;
+ /**
+ * displays the reset button
+ */
+ showResetButton: boolean;
+ /**
+ * resets the query
+ */
+ onResetQuery: () => void;
+}
+
+export function HitsCounter({ hits, showResetButton, onResetQuery }: HitsCounterProps) {
+ return (
+
+
+
+
+ {formatNumWithCommas(hits)}{' '}
+
+
+
+ {showResetButton && (
+
+
+
+
+
+ )}
+
+
+ );
+}
diff --git a/src/plugins/discover/public/application/components/hits_counter/hits_counter_directive.ts b/src/plugins/discover/public/application/components/hits_counter/hits_counter_directive.ts
new file mode 100644
index 0000000000000..8d45e28370cad
--- /dev/null
+++ b/src/plugins/discover/public/application/components/hits_counter/hits_counter_directive.ts
@@ -0,0 +1,27 @@
+/*
+ * 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 { HitsCounter } from './hits_counter';
+
+export function createHitsCounterDirective(reactDirective: any) {
+ return reactDirective(HitsCounter, [
+ ['hits', { watchDepth: 'reference' }],
+ ['showResetButton', { watchDepth: 'reference' }],
+ ['onResetQuery', { watchDepth: 'reference' }],
+ ]);
+}
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/initialize.ts b/src/plugins/discover/public/application/components/hits_counter/index.ts
similarity index 87%
rename from test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/initialize.ts
rename to src/plugins/discover/public/application/components/hits_counter/index.ts
index a4bc3cf17026c..58e7a9eda7f51 100644
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/initialize.ts
+++ b/src/plugins/discover/public/application/components/hits_counter/index.ts
@@ -17,4 +17,5 @@
* under the License.
*/
-import './np_ready/public/legacy';
+export { HitsCounter } from './hits_counter';
+export { createHitsCounterDirective } from './hits_counter_directive';
diff --git a/src/plugins/discover/public/application/helpers/format_number_with_commas.ts b/src/plugins/discover/public/application/helpers/format_number_with_commas.ts
new file mode 100644
index 0000000000000..01a010d823d5f
--- /dev/null
+++ b/src/plugins/discover/public/application/helpers/format_number_with_commas.ts
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+const COMMA_SEPARATOR_RE = /(\d)(?=(\d{3})+(?!\d))/g;
+
+/**
+ * Converts a number to a string and adds commas
+ * as thousands separators
+ */
+export const formatNumWithCommas = (input: number) =>
+ String(input).replace(COMMA_SEPARATOR_RE, '$1,');
diff --git a/src/plugins/discover/public/application/helpers/index.ts b/src/plugins/discover/public/application/helpers/index.ts
index 7196c96989e97..3555d24924e80 100644
--- a/src/plugins/discover/public/application/helpers/index.ts
+++ b/src/plugins/discover/public/application/helpers/index.ts
@@ -18,3 +18,4 @@
*/
export { shortenDottedString } from './shorten_dotted_string';
+export { formatNumWithCommas } from './format_number_with_commas';
diff --git a/src/plugins/discover/public/get_inner_angular.ts b/src/plugins/discover/public/get_inner_angular.ts
index e7813c43383f9..8c3f4f030688c 100644
--- a/src/plugins/discover/public/get_inner_angular.ts
+++ b/src/plugins/discover/public/get_inner_angular.ts
@@ -57,6 +57,7 @@ import {
createTopNavHelper,
} from '../../kibana_legacy/public';
import { createDiscoverSidebarDirective } from './application/components/sidebar';
+import { createHitsCounterDirective } from '././application/components/hits_counter';
import { DiscoverStartPlugins } from './plugin';
/**
@@ -151,6 +152,7 @@ export function initializeInnerAngularModule(
.directive('fixedScroll', FixedScrollProvider)
.directive('renderComplete', createRenderCompleteDirective)
.directive('discoverSidebar', createDiscoverSidebarDirective)
+ .directive('hitsCounter', createHitsCounterDirective)
.service('debounce', ['$timeout', DebounceProviderTimeout]);
}
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx
index 282b0f05891e0..3894d6fbed382 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx
@@ -18,6 +18,7 @@
*/
import * as React from 'react';
+import { EuiFlyout } from '@elastic/eui';
import { AddPanelFlyout } from './add_panel_flyout';
import {
ContactCardEmbeddableFactory,
@@ -75,6 +76,9 @@ test('createNewEmbeddable() add embeddable to container', async () => {
/>
) as ReactWrapper;
+ // https://github.com/elastic/kibana/issues/64789
+ expect(component.exists(EuiFlyout)).toBe(false);
+
expect(Object.values(container.getInput().panels).length).toBe(0);
component.instance().createNewEmbeddable(CONTACT_CARD_EMBEDDABLE);
await new Promise(r => setTimeout(r, 1));
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx
index 5bf3f69a95c30..4c23916675e8f 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx
@@ -21,13 +21,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import React, { ReactElement } from 'react';
import { CoreSetup } from 'src/core/public';
-import {
- EuiContextMenuItem,
- EuiFlyout,
- EuiFlyoutBody,
- EuiFlyoutHeader,
- EuiTitle,
-} from '@elastic/eui';
+import { EuiContextMenuItem, EuiFlyoutBody, EuiFlyoutHeader, EuiTitle } from '@elastic/eui';
import { EmbeddableStart } from 'src/plugins/embeddable/public';
import { IContainer } from '../../../../containers';
@@ -152,7 +146,7 @@ export class AddPanelFlyout extends React.Component {
);
return (
-
+ <>
@@ -161,7 +155,7 @@ export class AddPanelFlyout extends React.Component {