diff --git a/.backportrc.json b/.backportrc.json
index 0894909d2aac40..87bc3a1be583ba 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 dde0ce010d4d44..f1e0b7d9353e8b 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 be3c043b6e52f7..034b9da799d3ec 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 00000000000000..b661e8f09d1a12
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 00000000000000..bbc00a3543b08f
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 00000000000000..be7a62b1774ab4
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 454ae9bb720fbd..d4272e89999916 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 00000000000000..43afd6250be724
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 9d347fc4f1111c..03f7e13c985791 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 be86b9d522ac5e..3a6a96fca9d097 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 00000000000000..7536cd2b07ae6c
--- /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 066643516b213a..1b691ee8cb16dc 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 21a155ba977c96..60cbfd30e667d7 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 81e7abd4f9609e..00000000000000
--- 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 fa97666a61b939..39c8b0a700c8a3 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 00000000000000..a4d6abcf86a940
--- /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 24b56a9b986216..a79244a24acf57 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 acf3f3b1f0be95..d46df21e6f6b04 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 9d26fad419e4f2..fd59e79d07279a 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 d6895bd4952b8c..7506d1cb8c65ea 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 b049b4ba082f66..a757d59e123600 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 9b47d82a746398..29cdf707b4912a 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 94336a20e1d6cc..c0e495a87ecd31 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 75ffb3ff69babc..859274e9b6613c 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 ca00240a4af618..dc187c97de3097 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 8794c389d72bcb..09878b3059ac87 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 794fc14005f2ff..81b4e210961f6e 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 625b8f704b7c6d..c71412210c535f 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 673b4f6263e18f..cd51ec2e3301e9 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 8f888785626c9b..eadca229bc19c6 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 c0965d65bfdbec..afa616ba77b3a7 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 64bfa6a1d6364b..27609652288b5a 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 f05afac34e5957..d05a727016455f 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 e4448ca5f3fcd3..2df2a031c66614 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 f3e8f42ff0f374..8e0eff0224363c 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 a0a25dc5f1bbc9..ccd3f07f07c943 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 db56c16c1c308f..883c2348ecc8ad 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 356732dfb97775..5d66123ac733ea 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 fc40da74365474..055b643ec34586 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 ea3a3849c8927f..5be81b45612bcc 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 8f818f70012784..b13201ce5d38a5 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 b5d9c38d998108..70e1355004c473 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 9c51807b8d2199..7e9432d8c8678e 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 24e4e03f829ce6..4b1eaa631dc988 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 54054159584855..b4b272d2a241ad 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 3709f162b612b2..b3b868dbc41e80 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 0c2776e01b962b..18c28a703e9669 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 383bc8c2ce015d..b4ec6f3df60281 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 5f555f851cd816..29e5a29edc7c06 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 780a2119037b1a..00000000000000
--- 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 4c408da92f5791..d5c20d1853d421 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 852c3e1ecdeca1..9e41cce561454b 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 36709c2cc64370..9a1e81670b6541 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 8a92b464893081..0c83cb429b651e 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 6133f9871699f9..c7b98224c4e570 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 5fe1844a85563c..4e912896104328 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 e46075eff63a77..a2fbe969e34d82 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 375978b9b79447..06161fb2567b94 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 95e826e7620aa6..49bcc6e7e704c2 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 28cf36dedba3f3..1b70cced4a5c9c 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 8442f1ecc6411a..fd496da26283c4 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 60c36d3e330e0b..e399fbc726977a 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 026c23b93b040f..ad934717b4b766 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 ed64e7c4ce0b1c..553dc7c36e824c 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 3ec478e3ca28db..bd10520ca1c571 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 4ddb2b070d3ac3..07c1da55861076 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 c75fa68572c710..ef2a8870d78d0f 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 89f3fde3848488..e55b72be2436d9 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 5fe15f40db8ec8..49acea82e1c8af 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 800edaeaa58858..3f2c31a7c0e5cf 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 9dfb3abc8e72da..00ed8bf0b73fc5 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 dafd6c53411966..7d9ff9bed6d728 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 76a890d26bfa0c..50a71913934720 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 85f15b4c18b66a..5e55a34193a962 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 927171438ae996..c46fcfbc6dbd74 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 bc8ad2cdb00582..61027130e0eb73 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 62d11ee7cf9a7a..e4234689c25e81 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 28d6b49f9e89a8..153a3120f896f8 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 06be1bd0bd14f3..541b9551dbc9ba 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 a13f61af601733..5019c8bd223411 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 9f5f4b764f9b05..691318e32245b4 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 ad67a74121cc94..4e97d46ab1773e 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 bdb1436c37efb3..83335a6fabfeb6 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 8c059314786bcc..00000000000000
--- 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 4bbd839331e56b..00000000000000
--- 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 27f019318a82b4..00000000000000
--- 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 1629aac588a617..21e7b559f71f5f 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 a15c7cce5511d9..1eb46e1a438955 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 1bc85fa110ca0b..698c124d2d8057 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 8bf205b8cb5070..955d5244ce1904 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 248a3f991d6cbf..68d447c4a13361 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 6f121ceb2d3731..9d66f2df65777b 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 d4433f3825feae..69dd97a8817976 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 e05db0e4d4cec0..58c2cae1de0f38 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 f39be78433710f..98ec4495cef29a 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 0ecc87f3080fd9..c3f9af62f8c0e7 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 cb1e1d2bd0efe8..ee56ad60441f40 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 b2e17798ccc9fa..6c0467e3297e8a 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 18fa410a5bef03..83617d394fe958 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 df4ba23244b4dc..1f4076aa12bded 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 8eaa66cf586244..590dbebdf4cfee 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/_index.scss b/src/plugins/discover/public/application/angular/_index.scss
index 9e00ade3d41f6d..b0e5b6e3edf7b3 100644
--- a/src/plugins/discover/public/application/angular/_index.scss
+++ b/src/plugins/discover/public/application/angular/_index.scss
@@ -1,3 +1,2 @@
@import 'directives/index';
-@import 'doc_table/index';
@import 'context/index';
diff --git a/src/plugins/discover/public/application/angular/discover.html b/src/plugins/discover/public/application/angular/discover.html
index b4db89b9275b46..a0f98ea38ef783 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 00000000000000..1d2cd12877b1c0
--- /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 00000000000000..8d45e28370cade
--- /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 a4bc3cf17026c3..58e7a9eda7f51a 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/components/sidebar/_index.scss b/src/plugins/discover/public/application/components/sidebar/_index.scss
deleted file mode 100644
index 17b0a6c9cfe4e6..00000000000000
--- a/src/plugins/discover/public/application/components/sidebar/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import './_sidebar';
diff --git a/src/plugins/discover/public/application/components/sidebar/_sidebar.scss b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss
similarity index 100%
rename from src/plugins/discover/public/application/components/sidebar/_sidebar.scss
rename to src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss
diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx
index 74d1347b1694cb..56597dd31e572c 100644
--- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx
+++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import './discover_sidebar.scss';
import React, { useCallback, useEffect, useState, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiButtonIcon, EuiTitle } from '@elastic/eui';
diff --git a/src/plugins/discover/public/application/embeddable/_index.scss b/src/plugins/discover/public/application/embeddable/_index.scss
deleted file mode 100644
index 6d64040e9e7a31..00000000000000
--- a/src/plugins/discover/public/application/embeddable/_index.scss
+++ /dev/null
@@ -1,2 +0,0 @@
-
-@import 'embeddables';
diff --git a/src/plugins/discover/public/application/embeddable/_embeddables.scss b/src/plugins/discover/public/application/embeddable/search_embeddable.scss
similarity index 100%
rename from src/plugins/discover/public/application/embeddable/_embeddables.scss
rename to src/plugins/discover/public/application/embeddable/search_embeddable.scss
diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable.ts b/src/plugins/discover/public/application/embeddable/search_embeddable.ts
index b650672ccaea71..2f8ac40bdf52c0 100644
--- a/src/plugins/discover/public/application/embeddable/search_embeddable.ts
+++ b/src/plugins/discover/public/application/embeddable/search_embeddable.ts
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+import './search_embeddable.scss';
import angular from 'angular';
import _ from 'lodash';
import * as Rx from 'rxjs';
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 00000000000000..01a010d823d5f3
--- /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 7196c96989e97d..3555d24924e806 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/application/index.scss b/src/plugins/discover/public/application/index.scss
index 0de036b1e17074..aaec7ab387e966 100644
--- a/src/plugins/discover/public/application/index.scss
+++ b/src/plugins/discover/public/application/index.scss
@@ -10,6 +10,4 @@
// monChart__legend--small
// monChart__legend-isLoading
-@import 'components/index';
@import 'angular/index';
-@import 'embeddable/index';
diff --git a/src/plugins/discover/public/get_inner_angular.ts b/src/plugins/discover/public/get_inner_angular.ts
index e7813c43383f93..8c3f4f030688ce 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 282b0f05891e02..3894d6fbed382c 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 5bf3f69a95c303..4c23916675e8ff 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 {