diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8f2c27ac7c3cf..5a9e8bc585119 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -119,18 +119,18 @@ #CC# /x-pack/plugins/beats_management/ @elastic/beats # Canvas -/src/plugins/dashboard/ @elastic/kibana-app -/src/plugins/input_control_vis/ @elastic/kibana-app -/src/plugins/vis_type_markdown/ @elastic/kibana-app +/src/plugins/dashboard/ @elastic/kibana-canvas +/src/plugins/input_control_vis/ @elastic/kibana-canvas +/src/plugins/vis_type_markdown/ @elastic/kibana-canvas /x-pack/plugins/canvas/ @elastic/kibana-canvas -/x-pack/plugins/dashboard_enhanced/ @elastic/kibana-app +/x-pack/plugins/dashboard_enhanced/ @elastic/kibana-canvas /x-pack/test/functional/apps/canvas/ @elastic/kibana-canvas -#CC# /src/legacy/core_plugins/kibana/public/dashboard/ @elastic/kibana-app -#CC# /src/legacy/core_plugins/input_control_vis @elastic/kibana-app +#CC# /src/legacy/core_plugins/kibana/public/dashboard/ @elastic/kibana-canvas +#CC# /src/legacy/core_plugins/input_control_vis @elastic/kibana-canvas #CC# /src/plugins/kibana_react/public/code_editor/ @elastic/kibana-canvas #CC# /x-pack/legacy/plugins/canvas/ @elastic/kibana-canvas -#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-app -#CC# /x-pack/legacy/plugins/dashboard_mode/ @elastic/kibana-app +#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-canvas +#CC# /x-pack/legacy/plugins/dashboard_mode/ @elastic/kibana-canvas # Core UI # Exclude tutorials folder for now because they are not owned by Kibana app and most will move out soon diff --git a/.github/ISSUE_TEMPLATE/security_solution_bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report_security_solution.md similarity index 75% rename from .github/ISSUE_TEMPLATE/security_solution_bug_report.md rename to .github/ISSUE_TEMPLATE/Bug_report_security_solution.md index 86d2b1405d4eb..059e1d267c286 100644 --- a/.github/ISSUE_TEMPLATE/security_solution_bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report_security_solution.md @@ -1,7 +1,8 @@ --- -name: Security Solution Bug Report -about: Things break. Help us identify those things so we can fix them! +name: Bug report for Security Solution +about: Help us identify bugs in Elastic Security, SIEM, and Endpoint so we can fix them! title: '[Security Solution]' +labels: 'Team: SecuritySolution' --- **Describe the bug:** diff --git a/docs/api/dashboard/export-dashboard.asciidoc b/docs/api/dashboard/export-dashboard.asciidoc index 2099fb599ba67..d33b9603fae73 100644 --- a/docs/api/dashboard/export-dashboard.asciidoc +++ b/docs/api/dashboard/export-dashboard.asciidoc @@ -11,6 +11,8 @@ experimental[] Export dashboards and corresponding saved objects. `GET :/api/kibana/dashboards/export` +`GET :/s//api/kibana/dashboards/export` + [[dashboard-api-export-params]] ==== Query parameters diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc index 56bd4abbc8023..5d1fab41a2a14 100644 --- a/docs/api/dashboard/import-dashboard.asciidoc +++ b/docs/api/dashboard/import-dashboard.asciidoc @@ -11,6 +11,8 @@ experimental[] Import dashboards and corresponding saved objects. `POST :/api/kibana/dashboards/import` +`POST :/s//api/kibana/dashboards/import` + [[dashboard-api-import-params]] ==== Query parameters diff --git a/docs/api/features.asciidoc b/docs/api/features.asciidoc index 57a87ff6342f9..dad3ef75c8117 100644 --- a/docs/api/features.asciidoc +++ b/docs/api/features.asciidoc @@ -28,8 +28,6 @@ The API returns the following: { "id": "discover", "name": "Discover", - "icon": "discoverApp", - "navLinkId": "discover", "app": [ "kibana" ], @@ -73,8 +71,6 @@ The API returns the following: { "id": "visualize", "name": "Visualize", - "icon": "visualizeApp", - "navLinkId": "visualize", "app": [ "kibana" ], @@ -120,8 +116,6 @@ The API returns the following: { "id": "dashboard", "name": "Dashboard", - "icon": "dashboardApp", - "navLinkId": "dashboards", "app": [ "kibana" ], @@ -172,8 +166,6 @@ The API returns the following: { "id": "dev_tools", "name": "Dev Tools", - "icon": "devToolsApp", - "navLinkId": "dev_tools", "app": [ "kibana" ], diff --git a/docs/api/saved-objects.asciidoc b/docs/api/saved-objects.asciidoc index a4e9fa32f8a5c..0d8ceefb47e91 100644 --- a/docs/api/saved-objects.asciidoc +++ b/docs/api/saved-objects.asciidoc @@ -28,6 +28,8 @@ The following saved objects APIs are available: * <> to resolve errors from the import API +* <> to rotate the encryption key for encrypted saved objects + include::saved-objects/get.asciidoc[] include::saved-objects/bulk_get.asciidoc[] include::saved-objects/find.asciidoc[] @@ -38,3 +40,4 @@ include::saved-objects/delete.asciidoc[] include::saved-objects/export.asciidoc[] include::saved-objects/import.asciidoc[] include::saved-objects/resolve_import_errors.asciidoc[] +include::saved-objects/rotate_encryption_key.asciidoc[] diff --git a/docs/api/saved-objects/rotate_encryption_key.asciidoc b/docs/api/saved-objects/rotate_encryption_key.asciidoc new file mode 100644 index 0000000000000..0a66ed2b4b361 --- /dev/null +++ b/docs/api/saved-objects/rotate_encryption_key.asciidoc @@ -0,0 +1,110 @@ +[role="xpack"] +[[saved-objects-api-rotate-encryption-key]] +=== Rotate encryption key API +++++ +Rotate encryption key +++++ + +experimental[] Rotate the encryption key for encrypted saved objects. + +If a saved object cannot be decrypted using the primary encryption key, then {kib} will attempt to decrypt it using the specified <>. In most of the cases this overhead is negligible, but if you're dealing with a large number of saved objects and experiencing performance issues, you may want to rotate the encryption key. + +[IMPORTANT] +============================================================================ +Bulk key rotation can consume a considerable amount of resources and hence only user with a `superuser` role can trigger it. +============================================================================ + +[[saved-objects-api-rotate-encryption-key-request]] +==== Request + +`POST :/api/encrypted_saved_objects/_rotate_key` + +[[saved-objects-api-rotate-encryption-key-request-query-params]] +==== Query parameters + +`type`:: +(Optional, string) Limits encryption key rotation only to the saved objects with the specified type. By default, {kib} tries to rotate the encryption key for all saved object types that may contain encrypted attributes. + +`batchSize`:: +(Optional, number) Specifies a maximum number of saved objects that {kib} can process in a single batch. Bulk key rotation is an iterative process since {kib} may not be able to fetch and process all required saved objects in one go and splits processing into consequent batches. By default, the batch size is 10000, which is also a maximum allowed value. + +[[saved-objects-api-rotate-encryption-key-response-body]] +==== Response body + +`total`:: +(number) Indicates the total number of _all_ encrypted saved objects (optionally filtered by the requested `type`), regardless of the key {kib} used for encryption. + +`successful`:: +(number) Indicates the total number of _all_ encrypted saved objects (optionally filtered by the requested `type`), regardless of the key {kib} used for encryption. ++ +NOTE: In most cases, `total` will be greater than `successful` even if `failed` is zero. The reason is that {kib} may not need or may not be able to rotate encryption keys for all encrypted saved objects. + +`failed`:: +(number) Indicates the number of the saved objects that were still encrypted with one of the old encryption keys that {kib} failed to re-encrypt with the primary key. + +[[saved-objects-api-rotate-encryption-key-response-codes]] +==== Response code + +`200`:: +Indicates a successful call. + +`400`:: +Indicates that either query parameters are wrong or <> aren't configured. + +`429`:: +Indicates that key rotation is already in progress. + +[[saved-objects-api-rotate-encryption-key-example]] +==== Examples + +[[saved-objects-api-rotate-encryption-key-example-1]] +===== Encryption key rotation with default parameters + +[source,sh] +-------------------------------------------------- +$ curl -X POST /api/encrypted_saved_objects/_rotate_key +-------------------------------------------------- +// KIBANA + +The API returns the following: + +[source,sh] +-------------------------------------------------- +{ + "total": 1000, + "successful": 300, + "failed": 0 +} +-------------------------------------------------- + +The result indicates that the encryption key was successfully rotated for 300 out of 1000 saved objects with encrypted attributes, and 700 of the saved objects either didn't require key rotation, or were encrypted with an unknown encryption key. + +[[saved-objects-api-rotate-encryption-key-example-2]] +===== Encryption key rotation for the specific type with reduce batch size + +[IMPORTANT] +============================================================================ +Default parameters are optimized for speed. Change the parameters only when necessary. However, if you're experiencing any issues with this API, you may want to decrease a batch size or rotate the encryption keys for the specific types only. In this case, you may need to run key rotation multiple times in a row. +============================================================================ + +In this example, key rotation is performed for all saved objects with the `alert` type in batches of 5000. + +[source,sh] +-------------------------------------------------- +$ curl -X POST /api/encrypted_saved_objects/_rotate_key?type=alert&batchSize=5000 +-------------------------------------------------- +// KIBANA + +The API returns the following: + +[source,sh] +-------------------------------------------------- +{ + "total": 100, + "successful": 100, + "failed": 0 +} +-------------------------------------------------- + +The result indicates that the encryption key was successfully rotated for all 100 saved objects with the `alert` type. + diff --git a/docs/apm/apm-alerts.asciidoc b/docs/apm/apm-alerts.asciidoc index bc5e1ccc1dd55..7bdfe80b42177 100644 --- a/docs/apm/apm-alerts.asciidoc +++ b/docs/apm/apm-alerts.asciidoc @@ -18,12 +18,22 @@ image::apm/images/apm-alert.png[Create an alert in the APM app] For a walkthrough of the alert flyout panel, including detailed information on each configurable property, see Kibana's <>. -The APM app supports two different types of threshold alerts: transaction duration, and error rate. -Below, we'll create one of each. +The APM app supports four different types of alerts: + +* Transaction duration anomaly: +alerts when the service's transaction duration reaches a certain anomaly score +* Transaction duration threshold: +alerts when the service's transaction duration exceeds a given time limit over a given time frame +* Transaction error rate threshold: +alerts when the service's transaction error rate is above the selected rate over a given time frame +* Error count threshold: +alerts when service exceeds a selected number of errors over a given time frame + +Below, we'll walk through the creation of two of these alerts. [float] [[apm-create-transaction-alert]] -=== Create a transaction duration alert +=== Example: create a transaction duration alert Transaction duration alerts trigger when the duration of a specific transaction type in a service exceeds a defined threshold. This guide will create an alert for the `opbeans-java` service based on the following criteria: @@ -57,9 +67,9 @@ Enter a name for the connector, and paste the webhook URL. See Slack's webhook documentation if you need to create one. -Add a message body in markdown format. +A default message is provided as a starting point for your alert. You can use the https://mustache.github.io/[Mustache] template syntax, i.e., `{{variable}}` -to pass alert values at the time a condition is detected to an action. +to pass additional alert values at the time a condition is detected to an action. A list of available variables can be accessed by selecting the **add variable** button image:apm/images/add-variable.png[add variable button]. @@ -67,7 +77,7 @@ Select **Save**. The alert has been created and is now active! [float] [[apm-create-error-alert]] -=== Create an error rate alert +=== Example: create an error rate alert Error rate alerts trigger when the number of errors in a service exceeds a defined threshold. This guide creates an alert for the `opbeans-python` service based on the following criteria: @@ -94,9 +104,9 @@ Based on the alert criteria, define the following alert details: Select the **Email** action type and click **Create a connector**. Fill out the required details: sender, host, port, etc., and click **save**. -Add a message body in markdown format. +A default message is provided as a starting point for your alert. You can use the https://mustache.github.io/[Mustache] template syntax, i.e., `{{variable}}` -to pass alert values at the time a condition is detected to an action. +to pass additional alert values at the time a condition is detected to an action. A list of available variables can be accessed by selecting the **add variable** button image:apm/images/add-variable.png[add variable button]. diff --git a/docs/apm/filters.asciidoc b/docs/apm/filters.asciidoc index d53adb439f0c8..c405ea10ade3d 100644 --- a/docs/apm/filters.asciidoc +++ b/docs/apm/filters.asciidoc @@ -69,7 +69,7 @@ the host filter will still be applied. These filters are very useful for quickly and easily removing noise from your data. With just a click, you can filter your transactions by the transaction result, -host, container ID, and more. +host, container ID, Kubernetes pod, and more. [role="screenshot"] image::apm/images/local-filter.png[Local filters available in the APM app in Kibana] \ No newline at end of file diff --git a/docs/apm/images/apm-alert.png b/docs/apm/images/apm-alert.png index 350704d8969ae..c68b36f522bfc 100644 Binary files a/docs/apm/images/apm-alert.png and b/docs/apm/images/apm-alert.png differ diff --git a/docs/apm/images/apm-distributed-tracing.png b/docs/apm/images/apm-distributed-tracing.png index e9c6713361c73..0dbffa591d43a 100644 Binary files a/docs/apm/images/apm-distributed-tracing.png and b/docs/apm/images/apm-distributed-tracing.png differ diff --git a/docs/apm/images/apm-error-group.png b/docs/apm/images/apm-error-group.png index ecdf9c20cf4aa..359bdc6b704e9 100644 Binary files a/docs/apm/images/apm-error-group.png and b/docs/apm/images/apm-error-group.png differ diff --git a/docs/apm/images/apm-errors-overview.png b/docs/apm/images/apm-errors-overview.png index 90f16b81e9f50..969a1f19f9f43 100644 Binary files a/docs/apm/images/apm-errors-overview.png and b/docs/apm/images/apm-errors-overview.png differ diff --git a/docs/apm/images/apm-geo-ui.png b/docs/apm/images/apm-geo-ui.png index a767ed7e08e0c..3757127bad9c0 100644 Binary files a/docs/apm/images/apm-geo-ui.png and b/docs/apm/images/apm-geo-ui.png differ diff --git a/docs/apm/images/apm-metrics.png b/docs/apm/images/apm-metrics.png index 60383ef428f2a..ffe5ffc7e1d83 100644 Binary files a/docs/apm/images/apm-metrics.png and b/docs/apm/images/apm-metrics.png differ diff --git a/docs/apm/images/apm-query-bar.png b/docs/apm/images/apm-query-bar.png index 313ee7d4b8fc8..90955fb61016d 100644 Binary files a/docs/apm/images/apm-query-bar.png and b/docs/apm/images/apm-query-bar.png differ diff --git a/docs/apm/images/apm-service-map-anomaly.png b/docs/apm/images/apm-service-map-anomaly.png index b661e8f09d1a1..cd59f86690666 100644 Binary files a/docs/apm/images/apm-service-map-anomaly.png and b/docs/apm/images/apm-service-map-anomaly.png differ diff --git a/docs/apm/images/apm-services-overview.png b/docs/apm/images/apm-services-overview.png index 48236522ddfbb..85d14cc7dfc6e 100644 Binary files a/docs/apm/images/apm-services-overview.png and b/docs/apm/images/apm-services-overview.png differ diff --git a/docs/apm/images/apm-settings.png b/docs/apm/images/apm-settings.png index 4eaef9ec15ac5..14cf32877b720 100644 Binary files a/docs/apm/images/apm-settings.png and b/docs/apm/images/apm-settings.png differ diff --git a/docs/apm/images/apm-traces.png b/docs/apm/images/apm-traces.png index 6219be5b6d6e4..bf1f7e783bb11 100644 Binary files a/docs/apm/images/apm-traces.png and b/docs/apm/images/apm-traces.png differ diff --git a/docs/apm/images/apm-transaction-response-dist.png b/docs/apm/images/apm-transaction-response-dist.png index ecf5a4af2c25d..1d268bbaac465 100644 Binary files a/docs/apm/images/apm-transaction-response-dist.png and b/docs/apm/images/apm-transaction-response-dist.png differ diff --git a/docs/apm/images/apm-transaction-sample.png b/docs/apm/images/apm-transaction-sample.png index 73668b094f9cf..bfdb6a5abe65b 100644 Binary files a/docs/apm/images/apm-transaction-sample.png and b/docs/apm/images/apm-transaction-sample.png differ diff --git a/docs/apm/images/apm-transactions-overview.png b/docs/apm/images/apm-transactions-overview.png index b3b6ca22c4f63..53d7637b18647 100644 Binary files a/docs/apm/images/apm-transactions-overview.png and b/docs/apm/images/apm-transactions-overview.png differ diff --git a/docs/apm/images/example-metadata.png b/docs/apm/images/example-metadata.png index 0e35f90691723..2a5bda7f088f6 100644 Binary files a/docs/apm/images/example-metadata.png and b/docs/apm/images/example-metadata.png differ diff --git a/docs/apm/images/jvm-metrics-overview.png b/docs/apm/images/jvm-metrics-overview.png index 9c8ba4a12a262..586836c6cfe3e 100644 Binary files a/docs/apm/images/jvm-metrics-overview.png and b/docs/apm/images/jvm-metrics-overview.png differ diff --git a/docs/apm/images/jvm-metrics.png b/docs/apm/images/jvm-metrics.png index 1720e1370ff90..52a1ca5bea8d8 100644 Binary files a/docs/apm/images/jvm-metrics.png and b/docs/apm/images/jvm-metrics.png differ diff --git a/docs/apm/images/local-filter.png b/docs/apm/images/local-filter.png index faac5c143a7d8..8657e39f430aa 100644 Binary files a/docs/apm/images/local-filter.png and b/docs/apm/images/local-filter.png differ diff --git a/docs/apm/images/service-maps-java.png b/docs/apm/images/service-maps-java.png index e1a42f4c76e12..b3726bdc00ab6 100644 Binary files a/docs/apm/images/service-maps-java.png and b/docs/apm/images/service-maps-java.png differ diff --git a/docs/apm/images/service-maps.png b/docs/apm/images/service-maps.png index 078fabcfa2879..878a31adc69ca 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/service-quick-health.png b/docs/apm/images/service-quick-health.png new file mode 100644 index 0000000000000..aab1332513079 Binary files /dev/null and b/docs/apm/images/service-quick-health.png differ diff --git a/docs/apm/images/specific-transaction.png b/docs/apm/images/specific-transaction.png index 9911dbd879f41..52073bf76520a 100644 Binary files a/docs/apm/images/specific-transaction.png and b/docs/apm/images/specific-transaction.png differ diff --git a/docs/apm/machine-learning.asciidoc b/docs/apm/machine-learning.asciidoc index db2a1ef6e2da0..b31d717a6932e 100644 --- a/docs/apm/machine-learning.asciidoc +++ b/docs/apm/machine-learning.asciidoc @@ -14,7 +14,12 @@ Machine learning jobs are created per environment, and are based on a service's Because jobs are created at the environment level, you can add new services to your existing environments without the need for additional machine learning jobs. -After a machine learning job is created, results are shown in two places: +Results from machine learning jobs are shown in multiple places throughout the APM app: + +* The **Services overview** provides a quick-glance view of the general health of all of your services. ++ +[role="screenshot"] +image::apm/images/service-quick-health.png[Example view of anomaly scores on response times in the APM app] * The transaction duration chart will show the expected bounds and add an annotation when the anomaly score is 75 or above. + diff --git a/docs/apm/service-maps.asciidoc b/docs/apm/service-maps.asciidoc index d629a95073a74..d44c4ff6caa5c 100644 --- a/docs/apm/service-maps.asciidoc +++ b/docs/apm/service-maps.asciidoc @@ -33,7 +33,7 @@ distributed tracing will not work, and the connection will not be drawn on the m Select the **Service Map** tab to get started. By default, all instrumented services and connections are shown. Whether you're onboarding a new engineer, or just trying to grasp the big picture, -click around, zoom in and out, and begin to visualize how your services are connected. +drag things around, zoom in and out, and begin to visualize how your services are connected. If there's a specific service that interests you, select that service to highlight its connections. Clicking **Focus map** will refocus the map on that specific service and lock the connection highlighting. diff --git a/docs/apm/services.asciidoc b/docs/apm/services.asciidoc index 395e23c379306..2bf2e35c21cd8 100644 --- a/docs/apm/services.asciidoc +++ b/docs/apm/services.asciidoc @@ -2,8 +2,13 @@ [[services]] === Services overview -The *Services* overview gives you quick insights into the health and general performance of all of your instrumented services. -Services are sorted by the `service.name` configured in each of the {apm-agents-ref}[APM agents] you’ve installed. +The *Services* overview page provides a quick, high-level overview of the health and general +performance of all instrumented services. + +To help surface potential issues, services are sorted by their health status: +**critical** > **warning** > **healthy** > **unknown**. +Health status is powered by machine learning and requires anomaly detection to be enabled. +Learn more in <>. [role="screenshot"] -image::apm/images/apm-services-overview.png[Example view of services table the APM app in Kibana] \ No newline at end of file +image::apm/images/apm-services-overview.png[Example view of services table the APM app in Kibana] diff --git a/docs/apm/spans.asciidoc b/docs/apm/spans.asciidoc index c35fb115d2db4..7f29b1f003f1c 100644 --- a/docs/apm/spans.asciidoc +++ b/docs/apm/spans.asciidoc @@ -3,7 +3,7 @@ === Trace sample timeline The trace sample timeline visualization is a bird's-eye view of what your application was doing while it was trying to respond to a request. -This makes it useful for visualizing where the selected transaction spent most of its time. +This makes it useful for visualizing where a selected transaction spent most of its time. [role="screenshot"] image::apm/images/apm-transaction-sample.png[Example of distributed trace colors in the APM app in Kibana] @@ -43,9 +43,12 @@ this makes finding possible bottlenecks throughout your application much easier image::apm/images/apm-distributed-tracing.png[Example view of the distributed tracing in APM app in Kibana] Don't forget; by definition, a distributed trace includes more than one transaction. -When viewing these distributed traces in the timeline waterfall, you'll see this image:apm/images/transaction-icon.png[APM icon] icon, +When viewing distributed traces in the timeline waterfall, +you'll see this icon: image:apm/images/transaction-icon.png[APM icon], which indicates the next transaction in the trace. -These transactions can be expanded and viewed in detail by clicking on them. +For easier problem isolation, transactions can be collapsed in the waterfall by clicking +the icon to the left of the transactions. +Transactions can also be expanded and viewed in detail by clicking on them. After exploring these traces, you can return to the full trace by clicking *View full trace*. diff --git a/docs/apm/traces.asciidoc b/docs/apm/traces.asciidoc index 52b4b618de466..3bafebd733159 100644 --- a/docs/apm/traces.asciidoc +++ b/docs/apm/traces.asciidoc @@ -7,7 +7,8 @@ and which services were part of it. In addition to the Traces overview, you can view your application traces in the <>. The *Traces* overview displays the entry transaction for all traces in your application. -If you're using <>, this view is key to finding the critical paths within your application. +If you're using <>, +this view is key to finding the critical paths within your application. Transactions with the same name are grouped together and only shown once in this table. By default, transactions are sorted by _Impact_. diff --git a/docs/apm/transactions.asciidoc b/docs/apm/transactions.asciidoc index 84ab6b2a58579..fef98a86de1d0 100644 --- a/docs/apm/transactions.asciidoc +++ b/docs/apm/transactions.asciidoc @@ -10,17 +10,8 @@ Selecting a <> brings you to the *transactions* overview. [role="screenshot"] image::apm/images/apm-transactions-overview.png[Example view of transactions table in the APM app in Kibana] -The *time spent by span type*, *transaction duration*, and *requests per minute* chart display information on all transactions associated with the selected service: - -*Time spent by span type*:: -Visualize where your application is spending most of its time. -For example, is your app spending time in external calls, database processing, or application code execution? -+ -The time a transaction took to complete is also recorded and displayed on the chart under the "app" label. -"app" indicates that something was happening within the application, but we're not sure exactly what. -This could be a sign that the agent does not have auto-instrumentation for whatever was happening during that time. -+ -It's important to note that if you have asynchronous spans, the sum of all span times may exceed the duration of the transaction. +The *transaction duration*, *transactions per minute*, *transaction error rate*, and *time spent by span type* +charts display information on all transactions associated with the selected service: *Transaction duration*:: Response times for this service, broken down into average, 95th, and 99th percentile. @@ -28,11 +19,26 @@ If there's a weird spike that you'd like to investigate, you can simply zoom in on the graph - this will adjust the specific time range, and all of the data on the page will update accordingly. -*Requests per minute*:: +*Transactions per minute*:: Visualize response codes: `2xx`, `3xx`, `4xx`, etc., and is useful for determining if you're serving more of one code than you typically do. Like in the Transaction duration graph, you can zoom in on anomalies to further investigate them. +*Transaction error rate*:: +Visualize the total number of transactions with errors divided by the total number of transactions. +Any unexpected increases, decreases, or irregular patterns can be investigated further +with the <>. + +*Time spent by span type*:: +Visualize where your application is spending most of its time. +For example, is your app spending time in external calls, database processing, or application code execution? ++ +The time a transaction took to complete is also recorded and displayed on the chart under the "app" label. +"app" indicates that something was happening within the application, but we're not sure exactly what. +This could be a sign that the agent does not have auto-instrumentation for whatever was happening during that time. ++ +It's important to note that if you have asynchronous spans, the sum of all span times may exceed the duration of the transaction. + [[transactions-table]] ==== Transactions table @@ -61,42 +67,45 @@ refer to the documentation for each {apm-agents-ref}[APM Agent] you've implement ==== RUM Transaction overview The transaction overview page is customized for the JavaScript RUM Agent. -This page highlights things like *page load times*, *transactions per minute*, and even the *average page load duration distribution by country*. +Specifically, the page highlights *page load times* for your service: [role="screenshot"] image::apm/images/apm-geo-ui.png[average page load duration distribution] -This data is available due to the geo-ip and user agent pipelines being enabled by default, -which allows for the capture of geo-location and user agent data. -These visualizations make it easy for you to visualize performance information about your -end-users' experience based on their location. +Additional RUM goodies, like core vitals, and visitor breakdown by browser, location, and device, +are available in the Observability User Experience tab. +// To do +// Add link to the Observability UE docs when complete [[transaction-details]] ==== Transaction details Selecting a transaction group will bring you to the *transaction* details. -Transaction details include a high-level overview of the time spent by span type, -transaction group duration, requests per minute, and transaction group duration distribution. -It's important to note that all of these graphs show data from every transaction within the selected transaction group. +This page is visually similar to the transaction overview, but it shows data from all transactions within +the selected transaction group. [role="screenshot"] image::apm/images/apm-transaction-response-dist.png[Example view of response time distribution] Up to ten sampled transactions are also displayed. -These sampled transactions are based on your selection in the *Transactions duration distribution*. -You can update the sampled transactions by selecting a new _bucket_ in the transactions duration distribution graph. -The number of requests per bucket is displayed when hovering over the graph, and the selected bucket is highlighted to stand out. +These sampled transactions are based on the _bucket_ selection in the *Transactions duration distribution* chart. +You can update the sampled transactions by selecting a new _bucket_. +The number of requests per bucket is displayed when hovering over the graph, +and the selected bucket is highlighted to stand out. + +The screenshot below shows a typical distribution, and indicates most of our requests were served quickly--awesome! +It's the requests on the right, the ones taking longer than average, that we probably want to focus on. [role="screenshot"] image::apm/images/apm-transaction-duration-dist.png[Example view of transactions duration distribution graph] -This graph shows a typical distribution, and indicates most of our requests were served quickly--awesome! -It's the requests on the right, the ones taking longer than average, that we probably want to focus on. - -When you select one of these buckets, +When you select a bucket, you're presented with up to ten trace samples. -Each sample has a trace timeline waterfall that shows what a typical request in that bucket was doing. -By investigating this timeline waterfall, we can hopefully determine _why_ this request was slow and then implement a fix. +Each sample has a trace timeline waterfall that shows how a typical request in that bucket executed. +This waterfall is useful for understanding the parent/child hierarchy of transactions and spans, +and ultimately determining _why_ a request was slow. +For large waterfalls, expand problematic transactions and collapse well-performing ones +for easier problem isolation and troubleshooting. [role="screenshot"] image::apm/images/apm-transaction-sample.png[Example view of transactions sample] diff --git a/docs/apm/troubleshooting.asciidoc b/docs/apm/troubleshooting.asciidoc index b4c9c6a4ec39e..6c52c021fc0fc 100644 --- a/docs/apm/troubleshooting.asciidoc +++ b/docs/apm/troubleshooting.asciidoc @@ -14,6 +14,7 @@ Also, check out the https://discuss.elastic.co/c/apm[APM discussion forum]. * <> * <> * <> +* <> [float] [[no-apm-data-found]] @@ -180,3 +181,19 @@ setup.template.append_fields: type: object dynamic: true ---- + +[float] +[[service-map-rum-connections]] +=== Service maps: no connection between client and server + +If the service map is not showing an expected connection between the client and server, +it's likely because you haven't configured +{apm-agent-rum}/configuration.html#distributed-tracing-origins[`distributedTracingOrigins`]. + + +This setting is necessary, for example, for cross-origin requests. +If you have a basic web application that provides data via an API on `localhost:4000`, +and serves HTML from `localhost:4001`, you'd need to set `distributedTracingOrigins: ['https://localhost:4000']` +to ensure the origin is monitored as a part of distributed tracing. +In other words, `distributedTracingOrigins` is consulted prior to the agent adding the +distributed tracing `traceparent` header to each request. diff --git a/docs/developer/architecture/security/feature-registration.asciidoc b/docs/developer/architecture/security/feature-registration.asciidoc index b27e457940d93..8c80c2e5f2ffb 100644 --- a/docs/developer/architecture/security/feature-registration.asciidoc +++ b/docs/developer/architecture/security/feature-registration.asciidoc @@ -59,15 +59,6 @@ of features within the management screens. |See <> |The set of subfeatures that enables finer access control than the `all` and `read` feature privileges. These options are only available in the Gold subscription level and higher. -|`icon` -|`string` -|"discoverApp" -|An https://elastic.github.io/eui/#/display/icons[EUI Icon] to use for this feature. - -|`navLinkId` -|`string` -|"sample_app" -|The ID of the navigation link associated with your feature. |=== ==== Privilege definition @@ -100,8 +91,6 @@ public setup(core, { features }) { features.registerKibanaFeature({ id: 'canvas', name: 'Canvas', - icon: 'canvasApp', - navLinkId: 'canvas', category: DEFAULT_APP_CATEGORIES.kibana, app: ['canvas', 'kibana'], catalogue: ['canvas'], @@ -160,8 +149,6 @@ public setup(core, { features }) { name: i18n.translate('xpack.features.devToolsFeatureName', { defaultMessage: 'Dev Tools', }), - icon: 'devToolsApp', - navLinkId: 'dev_tools', category: DEFAULT_APP_CATEGORIES.management, app: ['kibana'], catalogue: ['console', 'searchprofiler', 'grokdebugger'], @@ -223,8 +210,6 @@ public setup(core, { features }) { defaultMessage: 'Discover', }), order: 100, - icon: 'discoverApp', - navLinkId: 'discover', category: DEFAULT_APP_CATEGORIES.kibana, app: ['kibana'], catalogue: ['discover'], diff --git a/docs/developer/best-practices/typescript.asciidoc b/docs/developer/best-practices/typescript.asciidoc index a2cda1e0b1e87..583a98f296de5 100644 --- a/docs/developer/best-practices/typescript.asciidoc +++ b/docs/developer/best-practices/typescript.asciidoc @@ -28,7 +28,7 @@ This architecture imposes several limitations to which we must comply: [discrete] ==== Prerequisites Since project refs rely on generated `d.ts` files, the migration order does matter. You can migrate your plugin only when all the plugin dependencies already have migrated. It creates a situation where commonly used plugins (such as `data` or `kibana_react`) have to migrate first. -https://github.com/elastic/kibana/issues/79343 is going to provide a tool for identifying a plugin dependency tree. +Run `node scripts/find_plugins_without_ts_refs.js --id your_plugin_id` to get a list of plugins that should be switched to TS project refs to unblock your plugin migration. [discrete] ==== Implementation diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 64ebc0efb1252..8e08c3806446d 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -286,10 +286,6 @@ which will load the visualization's editor. |To access an elasticsearch instance that has live data you have two options: -|{kib-repo}blob/{branch}/x-pack/plugins/audit_trail[auditTrail] -|WARNING: Missing README. - - |{kib-repo}blob/{branch}/x-pack/plugins/beats_management/readme.md[beatsManagement] |Notes: Failure to have auth enabled in Kibana will make for a broken UI. UI-based errors not yet in place @@ -436,10 +432,8 @@ using the CURL scripts in the scripts folder. |{kib-repo}blob/{branch}/x-pack/plugins/ml/readme.md[ml] -|To use machine learning features, you must have a Platinum or Enterprise license -or a free 14-day trial. File Data Visualizer requires a Basic license. For more -info, refer to -Set up machine learning features. +|This plugin provides access to the machine learning features provided by +Elastic. |{kib-repo}blob/{branch}/x-pack/plugins/monitoring[monitoring] @@ -471,7 +465,8 @@ Set up machine learning features. |{kib-repo}blob/{branch}/x-pack/plugins/security/README.md[security] -|See Configuring security in Kibana. +|See Configuring security in +Kibana. |{kib-repo}blob/{branch}/x-pack/plugins/security_solution/README.md[securitySolution] @@ -501,8 +496,8 @@ routes, etc. |Gathers all usage collection, retrieving them from both: OSS and X-Pack plugins. -|{kib-repo}blob/{branch}/x-pack/plugins/transform[transform] -|WARNING: Missing README. +|{kib-repo}blob/{branch}/x-pack/plugins/transform/readme.md[transform] +|This plugin provides access to the transforms features provided by Elastic. |{kib-repo}blob/{branch}/x-pack/plugins/translations[translations] diff --git a/docs/development/core/server/kibana-plugin-core-server.auditableevent.md b/docs/development/core/server/kibana-plugin-core-server.auditableevent.md deleted file mode 100644 index aa109c5064887..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.auditableevent.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [AuditableEvent](./kibana-plugin-core-server.auditableevent.md) - -## AuditableEvent interface - -Event to audit. - -Signature: - -```typescript -export interface AuditableEvent -``` - -## Remarks - -Not a complete interface. - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [message](./kibana-plugin-core-server.auditableevent.message.md) | string | | -| [type](./kibana-plugin-core-server.auditableevent.type.md) | string | | - diff --git a/docs/development/core/server/kibana-plugin-core-server.auditableevent.message.md b/docs/development/core/server/kibana-plugin-core-server.auditableevent.message.md deleted file mode 100644 index 3ac4167c6998b..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.auditableevent.message.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [AuditableEvent](./kibana-plugin-core-server.auditableevent.md) > [message](./kibana-plugin-core-server.auditableevent.message.md) - -## AuditableEvent.message property - -Signature: - -```typescript -message: string; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.auditableevent.type.md b/docs/development/core/server/kibana-plugin-core-server.auditableevent.type.md deleted file mode 100644 index 3748748366684..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.auditableevent.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [AuditableEvent](./kibana-plugin-core-server.auditableevent.md) > [type](./kibana-plugin-core-server.auditableevent.type.md) - -## AuditableEvent.type property - -Signature: - -```typescript -type: string; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.auditor.add.md b/docs/development/core/server/kibana-plugin-core-server.auditor.add.md deleted file mode 100644 index 40245a93753fc..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.auditor.add.md +++ /dev/null @@ -1,36 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Auditor](./kibana-plugin-core-server.auditor.md) > [add](./kibana-plugin-core-server.auditor.add.md) - -## Auditor.add() method - -Add a record to audit log. Service attaches to a log record: - metadata about an end-user initiating an operation - scope name, if presents - -Signature: - -```typescript -add(event: AuditableEvent): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| event | AuditableEvent | | - -Returns: - -`void` - -## Example - -How to add a record in audit log: - -```typescript -router.get({ path: '/my_endpoint', validate: false }, async (context, request, response) => { - context.core.auditor.withAuditScope('my_plugin_operation'); - const value = await context.core.elasticsearch.legacy.client.callAsCurrentUser('...'); - context.core.add({ type: 'operation.type', message: 'perform an operation in ... endpoint' }); - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.auditor.md b/docs/development/core/server/kibana-plugin-core-server.auditor.md deleted file mode 100644 index 191a34df647ab..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.auditor.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Auditor](./kibana-plugin-core-server.auditor.md) - -## Auditor interface - -Provides methods to log user actions and access events. - -Signature: - -```typescript -export interface Auditor -``` - -## Methods - -| Method | Description | -| --- | --- | -| [add(event)](./kibana-plugin-core-server.auditor.add.md) | Add a record to audit log. Service attaches to a log record: - metadata about an end-user initiating an operation - scope name, if presents | -| [withAuditScope(name)](./kibana-plugin-core-server.auditor.withauditscope.md) | Add a high-level scope name for logged events. It helps to identify the root cause of low-level events. | - diff --git a/docs/development/core/server/kibana-plugin-core-server.auditor.withauditscope.md b/docs/development/core/server/kibana-plugin-core-server.auditor.withauditscope.md deleted file mode 100644 index 0ae0c48ab92f4..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.auditor.withauditscope.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Auditor](./kibana-plugin-core-server.auditor.md) > [withAuditScope](./kibana-plugin-core-server.auditor.withauditscope.md) - -## Auditor.withAuditScope() method - -Add a high-level scope name for logged events. It helps to identify the root cause of low-level events. - -Signature: - -```typescript -withAuditScope(name: string): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| name | string | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.auditorfactory.asscoped.md b/docs/development/core/server/kibana-plugin-core-server.auditorfactory.asscoped.md deleted file mode 100644 index 4a60931e60940..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.auditorfactory.asscoped.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [AuditorFactory](./kibana-plugin-core-server.auditorfactory.md) > [asScoped](./kibana-plugin-core-server.auditorfactory.asscoped.md) - -## AuditorFactory.asScoped() method - -Signature: - -```typescript -asScoped(request: KibanaRequest): Auditor; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| request | KibanaRequest | | - -Returns: - -`Auditor` - diff --git a/docs/development/core/server/kibana-plugin-core-server.auditorfactory.md b/docs/development/core/server/kibana-plugin-core-server.auditorfactory.md deleted file mode 100644 index fd4760caa3552..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.auditorfactory.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [AuditorFactory](./kibana-plugin-core-server.auditorfactory.md) - -## AuditorFactory interface - -Creates [Auditor](./kibana-plugin-core-server.auditor.md) instance bound to the current user credentials. - -Signature: - -```typescript -export interface AuditorFactory -``` - -## Methods - -| Method | Description | -| --- | --- | -| [asScoped(request)](./kibana-plugin-core-server.auditorfactory.asscoped.md) | | - diff --git a/docs/development/core/server/kibana-plugin-core-server.audittrailsetup.md b/docs/development/core/server/kibana-plugin-core-server.audittrailsetup.md deleted file mode 100644 index 50885232a088e..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.audittrailsetup.md +++ /dev/null @@ -1,18 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [AuditTrailSetup](./kibana-plugin-core-server.audittrailsetup.md) - -## AuditTrailSetup interface - -Signature: - -```typescript -export interface AuditTrailSetup -``` - -## Methods - -| Method | Description | -| --- | --- | -| [register(auditor)](./kibana-plugin-core-server.audittrailsetup.register.md) | Register a custom [AuditorFactory](./kibana-plugin-core-server.auditorfactory.md) implementation. | - diff --git a/docs/development/core/server/kibana-plugin-core-server.audittrailsetup.register.md b/docs/development/core/server/kibana-plugin-core-server.audittrailsetup.register.md deleted file mode 100644 index 36695844ced73..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.audittrailsetup.register.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [AuditTrailSetup](./kibana-plugin-core-server.audittrailsetup.md) > [register](./kibana-plugin-core-server.audittrailsetup.register.md) - -## AuditTrailSetup.register() method - -Register a custom [AuditorFactory](./kibana-plugin-core-server.auditorfactory.md) implementation. - -Signature: - -```typescript -register(auditor: AuditorFactory): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| auditor | AuditorFactory | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.audittrailstart.md b/docs/development/core/server/kibana-plugin-core-server.audittrailstart.md deleted file mode 100644 index 4fb9f5cb93549..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.audittrailstart.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [AuditTrailStart](./kibana-plugin-core-server.audittrailstart.md) - -## AuditTrailStart type - -Signature: - -```typescript -export declare type AuditTrailStart = AuditorFactory; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.coresetup.audittrail.md b/docs/development/core/server/kibana-plugin-core-server.coresetup.audittrail.md deleted file mode 100644 index 1aa7a75b7a086..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.coresetup.audittrail.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreSetup](./kibana-plugin-core-server.coresetup.md) > [auditTrail](./kibana-plugin-core-server.coresetup.audittrail.md) - -## CoreSetup.auditTrail property - -[AuditTrailSetup](./kibana-plugin-core-server.audittrailsetup.md) - -Signature: - -```typescript -auditTrail: AuditTrailSetup; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.coresetup.md b/docs/development/core/server/kibana-plugin-core-server.coresetup.md index 75da8df2ae15a..7a733cc34dace 100644 --- a/docs/development/core/server/kibana-plugin-core-server.coresetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.coresetup.md @@ -16,7 +16,6 @@ export interface CoreSetupAuditTrailSetup | [AuditTrailSetup](./kibana-plugin-core-server.audittrailsetup.md) | | [capabilities](./kibana-plugin-core-server.coresetup.capabilities.md) | CapabilitiesSetup | [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | | [context](./kibana-plugin-core-server.coresetup.context.md) | ContextSetup | [ContextSetup](./kibana-plugin-core-server.contextsetup.md) | | [elasticsearch](./kibana-plugin-core-server.coresetup.elasticsearch.md) | ElasticsearchServiceSetup | [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) | diff --git a/docs/development/core/server/kibana-plugin-core-server.corestart.audittrail.md b/docs/development/core/server/kibana-plugin-core-server.corestart.audittrail.md deleted file mode 100644 index 879e0df836190..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.corestart.audittrail.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreStart](./kibana-plugin-core-server.corestart.md) > [auditTrail](./kibana-plugin-core-server.corestart.audittrail.md) - -## CoreStart.auditTrail property - -[AuditTrailSetup](./kibana-plugin-core-server.audittrailsetup.md) - -Signature: - -```typescript -auditTrail: AuditTrailStart; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.corestart.md b/docs/development/core/server/kibana-plugin-core-server.corestart.md index 0d5474fae5e16..f98088648689f 100644 --- a/docs/development/core/server/kibana-plugin-core-server.corestart.md +++ b/docs/development/core/server/kibana-plugin-core-server.corestart.md @@ -16,7 +16,6 @@ export interface CoreStart | Property | Type | Description | | --- | --- | --- | -| [auditTrail](./kibana-plugin-core-server.corestart.audittrail.md) | AuditTrailStart | [AuditTrailSetup](./kibana-plugin-core-server.audittrailsetup.md) | | [capabilities](./kibana-plugin-core-server.corestart.capabilities.md) | CapabilitiesStart | [CapabilitiesStart](./kibana-plugin-core-server.capabilitiesstart.md) | | [elasticsearch](./kibana-plugin-core-server.corestart.elasticsearch.md) | ElasticsearchServiceStart | [ElasticsearchServiceStart](./kibana-plugin-core-server.elasticsearchservicestart.md) | | [http](./kibana-plugin-core-server.corestart.http.md) | HttpServiceStart | [HttpServiceStart](./kibana-plugin-core-server.httpservicestart.md) | diff --git a/docs/development/core/server/kibana-plugin-core-server.kibanarequest.md b/docs/development/core/server/kibana-plugin-core-server.kibanarequest.md index 0a49ee6e63d6c..1134994faa9bd 100644 --- a/docs/development/core/server/kibana-plugin-core-server.kibanarequest.md +++ b/docs/development/core/server/kibana-plugin-core-server.kibanarequest.md @@ -30,6 +30,7 @@ export declare class KibanaRequestboolean | Whether or not the request is a "system request" rather than an application-level request. Can be set on the client using the HttpFetchOptions#asSystemRequest option. | | [params](./kibana-plugin-core-server.kibanarequest.params.md) | | Params | | | [query](./kibana-plugin-core-server.kibanarequest.query.md) | | Query | | +| [rewrittenUrl](./kibana-plugin-core-server.kibanarequest.rewrittenurl.md) | | Url | URL rewritten in onPreRouting request interceptor. | | [route](./kibana-plugin-core-server.kibanarequest.route.md) | | RecursiveReadonly<KibanaRequestRoute<Method>> | matched route details | | [socket](./kibana-plugin-core-server.kibanarequest.socket.md) | | IKibanaSocket | [IKibanaSocket](./kibana-plugin-core-server.ikibanasocket.md) | | [url](./kibana-plugin-core-server.kibanarequest.url.md) | | Url | a WHATWG URL standard object. | diff --git a/docs/development/core/server/kibana-plugin-core-server.kibanarequest.rewrittenurl.md b/docs/development/core/server/kibana-plugin-core-server.kibanarequest.rewrittenurl.md new file mode 100644 index 0000000000000..10628bafaf1d4 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.kibanarequest.rewrittenurl.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [KibanaRequest](./kibana-plugin-core-server.kibanarequest.md) > [rewrittenUrl](./kibana-plugin-core-server.kibanarequest.rewrittenurl.md) + +## KibanaRequest.rewrittenUrl property + +URL rewritten in onPreRouting request interceptor. + +Signature: + +```typescript +readonly rewrittenUrl?: Url; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient._constructor_.md b/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient._constructor_.md index 6a56d31bbd55f..823f34bd7dd23 100644 --- a/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient._constructor_.md +++ b/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `LegacyClusterClient` class Signature: ```typescript -constructor(config: LegacyElasticsearchClientConfig, log: Logger, getAuditorFactory: () => AuditorFactory, getAuthHeaders?: GetAuthHeaders); +constructor(config: LegacyElasticsearchClientConfig, log: Logger, getAuthHeaders?: GetAuthHeaders); ``` ## Parameters @@ -18,6 +18,5 @@ constructor(config: LegacyElasticsearchClientConfig, log: Logger, getAuditorFact | --- | --- | --- | | config | LegacyElasticsearchClientConfig | | | log | Logger | | -| getAuditorFactory | () => AuditorFactory | | | getAuthHeaders | GetAuthHeaders | | diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient.md b/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient.md index 668d0b2866a26..d24aeb44ca86a 100644 --- a/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient.md +++ b/docs/development/core/server/kibana-plugin-core-server.legacyclusterclient.md @@ -21,7 +21,7 @@ export declare class LegacyClusterClient implements ILegacyClusterClient | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(config, log, getAuditorFactory, getAuthHeaders)](./kibana-plugin-core-server.legacyclusterclient._constructor_.md) | | Constructs a new instance of the LegacyClusterClient class | +| [(constructor)(config, log, getAuthHeaders)](./kibana-plugin-core-server.legacyclusterclient._constructor_.md) | | Constructs a new instance of the LegacyClusterClient class | ## Properties diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient._constructor_.md b/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient._constructor_.md index ffadab7656602..bd1cd1e9f3d9b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient._constructor_.md +++ b/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `LegacyScopedClusterClient` class Signature: ```typescript -constructor(internalAPICaller: LegacyAPICaller, scopedAPICaller: LegacyAPICaller, headers?: Headers | undefined, auditor?: Auditor | undefined); +constructor(internalAPICaller: LegacyAPICaller, scopedAPICaller: LegacyAPICaller, headers?: Headers | undefined); ``` ## Parameters @@ -19,5 +19,4 @@ constructor(internalAPICaller: LegacyAPICaller, scopedAPICaller: LegacyAPICaller | internalAPICaller | LegacyAPICaller | | | scopedAPICaller | LegacyAPICaller | | | headers | Headers | undefined | | -| auditor | Auditor | undefined | | diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.md b/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.md index 7f752d70921ba..6b6649e833a92 100644 --- a/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.md +++ b/docs/development/core/server/kibana-plugin-core-server.legacyscopedclusterclient.md @@ -21,7 +21,7 @@ export declare class LegacyScopedClusterClient implements ILegacyScopedClusterCl | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(internalAPICaller, scopedAPICaller, headers, auditor)](./kibana-plugin-core-server.legacyscopedclusterclient._constructor_.md) | | Constructs a new instance of the LegacyScopedClusterClient class | +| [(constructor)(internalAPICaller, scopedAPICaller, headers)](./kibana-plugin-core-server.legacyscopedclusterclient._constructor_.md) | | Constructs a new instance of the LegacyScopedClusterClient class | ## Methods diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index a484c856ec015..29f5220794918 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -53,10 +53,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AppCategory](./kibana-plugin-core-server.appcategory.md) | A category definition for nav links to know where to sort them in the left hand nav | | [AssistanceAPIResponse](./kibana-plugin-core-server.assistanceapiresponse.md) | | | [AssistantAPIClientParams](./kibana-plugin-core-server.assistantapiclientparams.md) | | -| [AuditableEvent](./kibana-plugin-core-server.auditableevent.md) | Event to audit. | -| [Auditor](./kibana-plugin-core-server.auditor.md) | Provides methods to log user actions and access events. | -| [AuditorFactory](./kibana-plugin-core-server.auditorfactory.md) | Creates [Auditor](./kibana-plugin-core-server.auditor.md) instance bound to the current user credentials. | -| [AuditTrailSetup](./kibana-plugin-core-server.audittrailsetup.md) | | | [Authenticated](./kibana-plugin-core-server.authenticated.md) | | | [AuthNotHandled](./kibana-plugin-core-server.authnothandled.md) | | | [AuthRedirected](./kibana-plugin-core-server.authredirected.md) | | @@ -132,7 +128,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [PluginConfigDescriptor](./kibana-plugin-core-server.pluginconfigdescriptor.md) | Describes a plugin configuration properties. | | [PluginInitializerContext](./kibana-plugin-core-server.plugininitializercontext.md) | Context that's available to plugins during initialization stage. | | [PluginManifest](./kibana-plugin-core-server.pluginmanifest.md) | Describes the set of required and optional properties plugin can define in its mandatory JSON manifest file. | -| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.legacy.client](./kibana-plugin-core-server.legacyscopedclusterclient.md) - The legacy Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request - [uiSettings.auditor](./kibana-plugin-core-server.auditor.md) - AuditTrail client scoped to the incoming request | +| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.legacy.client](./kibana-plugin-core-server.legacyscopedclusterclient.md) - The legacy Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request | | [RouteConfig](./kibana-plugin-core-server.routeconfig.md) | Route specific configuration. | | [RouteConfigOptions](./kibana-plugin-core-server.routeconfigoptions.md) | Additional route options. | | [RouteConfigOptionsBody](./kibana-plugin-core-server.routeconfigoptionsbody.md) | Additional body options for a route | @@ -223,7 +219,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | Type Alias | Description | | --- | --- | | [AppenderConfigType](./kibana-plugin-core-server.appenderconfigtype.md) | | -| [AuditTrailStart](./kibana-plugin-core-server.audittrailstart.md) | | | [AuthenticationHandler](./kibana-plugin-core-server.authenticationhandler.md) | See [AuthToolkit](./kibana-plugin-core-server.authtoolkit.md). | | [AuthHeaders](./kibana-plugin-core-server.authheaders.md) | Auth Headers map | | [AuthResult](./kibana-plugin-core-server.authresult.md) | | diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md index 5b8492ec5ece1..b195e97989162 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md @@ -21,6 +21,5 @@ core: { uiSettings: { client: IUiSettingsClient; }; - auditor: Auditor; }; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md index 4e530973f9d50..1de7313f2c40e 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md @@ -6,7 +6,7 @@ Plugin specific context passed to a route handler. -Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.legacy.client](./kibana-plugin-core-server.legacyscopedclusterclient.md) - The legacy Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request - [uiSettings.auditor](./kibana-plugin-core-server.auditor.md) - AuditTrail client scoped to the incoming request +Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.legacy.client](./kibana-plugin-core-server.legacyscopedclusterclient.md) - The legacy Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request Signature: @@ -18,5 +18,5 @@ export interface RequestHandlerContext | Property | Type | Description | | --- | --- | --- | -| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | {
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
};
elasticsearch: {
client: IScopedClusterClient;
legacy: {
client: ILegacyScopedClusterClient;
};
};
uiSettings: {
client: IUiSettingsClient;
};
auditor: Auditor;
} | | +| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | {
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
};
elasticsearch: {
client: IScopedClusterClient;
legacy: {
client: ILegacyScopedClusterClient;
};
};
uiSettings: {
client: IUiSettingsClient;
};
} | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.es_field_types.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.es_field_types.md index c5e01715534d1..ad762cae489c8 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.es_field_types.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.es_field_types.md @@ -43,4 +43,5 @@ export declare enum ES_FIELD_TYPES | STRING | "string" | | | TEXT | "text" | | | TOKEN\_COUNT | "token_count" | | +| UNSIGNED\_LONG | "unsigned_long" | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.deletefieldformat.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.deletefieldformat.md new file mode 100644 index 0000000000000..26276a809a613 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.deletefieldformat.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [deleteFieldFormat](./kibana-plugin-plugins-data-public.indexpattern.deletefieldformat.md) + +## IndexPattern.deleteFieldFormat property + +Signature: + +```typescript +deleteFieldFormat: (fieldName: string) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfieldnodefault.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfieldnodefault.md new file mode 100644 index 0000000000000..0dd171108b20b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfieldnodefault.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getFormatterForFieldNoDefault](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfieldnodefault.md) + +## IndexPattern.getFormatterForFieldNoDefault() method + +Get formatter for a given field name. Return undefined if none exists + +Signature: + +```typescript +getFormatterForFieldNoDefault(fieldname: string): FieldFormat | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| fieldname | string | | + +Returns: + +`FieldFormat | undefined` + 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 c07041470d102..7e3192481dfff 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 @@ -20,6 +20,7 @@ export declare class IndexPattern implements IIndexPattern | Property | Modifiers | Type | Description | | --- | --- | --- | --- | +| [deleteFieldFormat](./kibana-plugin-plugins-data-public.indexpattern.deletefieldformat.md) | | (fieldName: string) => void | | | [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md) | | Record<string, any> | | | [fields](./kibana-plugin-plugins-data-public.indexpattern.fields.md) | | IIndexPatternFieldList & {
toSpec: () => IndexPatternFieldMap;
} | | | [flattenHit](./kibana-plugin-plugins-data-public.indexpattern.flattenhit.md) | | (hit: Record<string, any>, deep?: boolean) => Record<string, any> | | @@ -30,6 +31,7 @@ export declare class IndexPattern implements IIndexPattern | [intervalName](./kibana-plugin-plugins-data-public.indexpattern.intervalname.md) | | string | undefined | | | [metaFields](./kibana-plugin-plugins-data-public.indexpattern.metafields.md) | | string[] | | | [resetOriginalSavedObjectBody](./kibana-plugin-plugins-data-public.indexpattern.resetoriginalsavedobjectbody.md) | | () => void | Reset last saved saved object fields. used after saving | +| [setFieldFormat](./kibana-plugin-plugins-data-public.indexpattern.setfieldformat.md) | | (fieldName: string, format: SerializedFieldFormat) => void | | | [sourceFilters](./kibana-plugin-plugins-data-public.indexpattern.sourcefilters.md) | | SourceFilter[] | | | [timeFieldName](./kibana-plugin-plugins-data-public.indexpattern.timefieldname.md) | | string | undefined | | | [title](./kibana-plugin-plugins-data-public.indexpattern.title.md) | | string | | @@ -47,6 +49,7 @@ export declare class IndexPattern implements IIndexPattern | [getComputedFields()](./kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md) | | | | [getFieldByName(name)](./kibana-plugin-plugins-data-public.indexpattern.getfieldbyname.md) | | | | [getFormatterForField(field)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md) | | Provide a field, get its formatter | +| [getFormatterForFieldNoDefault(fieldname)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfieldnodefault.md) | | Get formatter for a given field name. Return undefined if none exists | | [getNonScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md) | | | | [getScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md) | | | | [getSourceFiltering()](./kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md) | | Get the source filtering configuration for that index. | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.setfieldformat.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.setfieldformat.md new file mode 100644 index 0000000000000..9774fc8c7308c --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.setfieldformat.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [setFieldFormat](./kibana-plugin-plugins-data-public.indexpattern.setfieldformat.md) + +## IndexPattern.setFieldFormat property + +Signature: + +```typescript +setFieldFormat: (fieldName: string, format: SerializedFieldFormat) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.fieldformats.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.fieldformats.md new file mode 100644 index 0000000000000..af4115e4c4e09 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.fieldformats.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [fieldFormats](./kibana-plugin-plugins-data-public.indexpatternspec.fieldformats.md) + +## IndexPatternSpec.fieldFormats property + +Signature: + +```typescript +fieldFormats?: Record; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md index 74c4df126e1bf..f3b692209ca67 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md @@ -14,6 +14,7 @@ export interface IndexPatternSpec | Property | Type | Description | | --- | --- | --- | +| [fieldFormats](./kibana-plugin-plugins-data-public.indexpatternspec.fieldformats.md) | Record<string, SerializedFieldFormat> | | | [fields](./kibana-plugin-plugins-data-public.indexpatternspec.fields.md) | IndexPatternFieldMap | | | [id](./kibana-plugin-plugins-data-public.indexpatternspec.id.md) | string | | | [intervalName](./kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md) | string | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md index c9018b0048aa3..76d0914173447 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md @@ -15,5 +15,6 @@ export interface ISearchOptions | Property | Type | Description | | --- | --- | --- | | [abortSignal](./kibana-plugin-plugins-data-public.isearchoptions.abortsignal.md) | AbortSignal | An AbortSignal that allows the caller of search to abort a search request. | +| [sessionId](./kibana-plugin-plugins-data-public.isearchoptions.sessionid.md) | string | A session ID, grouping multiple search requests into a single session. | | [strategy](./kibana-plugin-plugins-data-public.isearchoptions.strategy.md) | string | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.sessionid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.sessionid.md new file mode 100644 index 0000000000000..b1d569e58bf1d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.sessionid.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) > [sessionId](./kibana-plugin-plugins-data-public.isearchoptions.sessionid.md) + +## ISearchOptions.sessionId property + +A session ID, grouping multiple search requests into a single session. + +Signature: + +```typescript +sessionId?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md index b68c4d61e4e03..bbf856480aedd 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md @@ -17,5 +17,6 @@ export interface ISearchSetup | Property | Type | Description | | --- | --- | --- | | [aggs](./kibana-plugin-plugins-data-public.isearchsetup.aggs.md) | AggsSetup | | +| [session](./kibana-plugin-plugins-data-public.isearchsetup.session.md) | ISessionService | session management | | [usageCollector](./kibana-plugin-plugins-data-public.isearchsetup.usagecollector.md) | SearchUsageCollector | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.session.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.session.md new file mode 100644 index 0000000000000..7f39d9714a3a3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.session.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchSetup](./kibana-plugin-plugins-data-public.isearchsetup.md) > [session](./kibana-plugin-plugins-data-public.isearchsetup.session.md) + +## ISearchSetup.session property + +session management + +Signature: + +```typescript +session: ISessionService; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md index 5defe4a647614..4a69e94dd6f58 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md @@ -19,5 +19,6 @@ export interface ISearchStart | [aggs](./kibana-plugin-plugins-data-public.isearchstart.aggs.md) | AggsStart | agg config sub service [AggsStart](./kibana-plugin-plugins-data-public.aggsstart.md) | | [search](./kibana-plugin-plugins-data-public.isearchstart.search.md) | ISearchGeneric | low level search [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | | [searchSource](./kibana-plugin-plugins-data-public.isearchstart.searchsource.md) | ISearchStartSearchSource | high level search [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) | +| [session](./kibana-plugin-plugins-data-public.isearchstart.session.md) | ISessionService | session management | | [showError](./kibana-plugin-plugins-data-public.isearchstart.showerror.md) | (e: Error) => void | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.session.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.session.md new file mode 100644 index 0000000000000..de25cccd6d27a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.session.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) > [session](./kibana-plugin-plugins-data-public.isearchstart.session.md) + +## ISearchStart.session property + +session management + +Signature: + +```typescript +session: ISessionService; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md index 02db74b1a9e91..1c8b6eb41a72e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md @@ -7,7 +7,7 @@ Signature: ```typescript -protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; +protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, options?: ISearchOptions): Error; ``` ## Parameters @@ -17,7 +17,7 @@ protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal | e | any | | | request | IKibanaSearchRequest | | | timeoutSignal | AbortSignal | | -| appAbortSignal | AbortSignal | | +| options | ISearchOptions | | Returns: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md index a02a6116d7ae0..40c7055e4c059 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md @@ -27,7 +27,7 @@ export declare class SearchInterceptor | Method | Modifiers | Description | | --- | --- | --- | | [getTimeoutMode()](./kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md) | | | -| [handleSearchError(e, request, timeoutSignal, appAbortSignal)](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) | | | +| [handleSearchError(e, request, timeoutSignal, options)](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) | | | | [search(request, options)](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | Searches using the given search method. Overrides the AbortSignal with one that will abort either when cancelPending is called, when the request times out, or when the original AbortSignal is aborted. Updates pendingCount$ when the request is started/finalized. | | [showError(e)](./kibana-plugin-plugins-data-public.searchinterceptor.showerror.md) | | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.md index 63eb67ce48246..3653394d28b92 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.md @@ -15,6 +15,7 @@ export interface SearchInterceptorDeps | Property | Type | Description | | --- | --- | --- | | [http](./kibana-plugin-plugins-data-public.searchinterceptordeps.http.md) | CoreSetup['http'] | | +| [session](./kibana-plugin-plugins-data-public.searchinterceptordeps.session.md) | ISessionService | | | [startServices](./kibana-plugin-plugins-data-public.searchinterceptordeps.startservices.md) | Promise<[CoreStart, any, unknown]> | | | [toasts](./kibana-plugin-plugins-data-public.searchinterceptordeps.toasts.md) | ToastsSetup | | | [uiSettings](./kibana-plugin-plugins-data-public.searchinterceptordeps.uisettings.md) | CoreSetup['uiSettings'] | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.session.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.session.md new file mode 100644 index 0000000000000..40d00483317ba --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.session.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md) > [session](./kibana-plugin-plugins-data-public.searchinterceptordeps.session.md) + +## SearchInterceptorDeps.session property + +Signature: + +```typescript +session: ISessionService; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.es_field_types.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.es_field_types.md index d071955f4f522..545b7b9d27e10 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.es_field_types.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.es_field_types.md @@ -43,4 +43,5 @@ export declare enum ES_FIELD_TYPES | STRING | "string" | | | TEXT | "text" | | | TOKEN\_COUNT | "token_count" | | +| UNSIGNED\_LONG | "unsigned_long" | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.deletefieldformat.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.deletefieldformat.md new file mode 100644 index 0000000000000..4bfda56527474 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.deletefieldformat.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [deleteFieldFormat](./kibana-plugin-plugins-data-server.indexpattern.deletefieldformat.md) + +## IndexPattern.deleteFieldFormat property + +Signature: + +```typescript +deleteFieldFormat: (fieldName: string) => void; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getformatterforfieldnodefault.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getformatterforfieldnodefault.md new file mode 100644 index 0000000000000..77cc879e2f2f2 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getformatterforfieldnodefault.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getFormatterForFieldNoDefault](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfieldnodefault.md) + +## IndexPattern.getFormatterForFieldNoDefault() method + +Get formatter for a given field name. Return undefined if none exists + +Signature: + +```typescript +getFormatterForFieldNoDefault(fieldname: string): FieldFormat | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| fieldname | string | | + +Returns: + +`FieldFormat | undefined` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md index 603864234d34b..2e15c8d3867ec 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md @@ -20,6 +20,7 @@ export declare class IndexPattern implements IIndexPattern | Property | Modifiers | Type | Description | | --- | --- | --- | --- | +| [deleteFieldFormat](./kibana-plugin-plugins-data-server.indexpattern.deletefieldformat.md) | | (fieldName: string) => void | | | [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpattern.fieldformatmap.md) | | Record<string, any> | | | [fields](./kibana-plugin-plugins-data-server.indexpattern.fields.md) | | IIndexPatternFieldList & {
toSpec: () => IndexPatternFieldMap;
} | | | [flattenHit](./kibana-plugin-plugins-data-server.indexpattern.flattenhit.md) | | (hit: Record<string, any>, deep?: boolean) => Record<string, any> | | @@ -30,6 +31,7 @@ export declare class IndexPattern implements IIndexPattern | [intervalName](./kibana-plugin-plugins-data-server.indexpattern.intervalname.md) | | string | undefined | | | [metaFields](./kibana-plugin-plugins-data-server.indexpattern.metafields.md) | | string[] | | | [resetOriginalSavedObjectBody](./kibana-plugin-plugins-data-server.indexpattern.resetoriginalsavedobjectbody.md) | | () => void | Reset last saved saved object fields. used after saving | +| [setFieldFormat](./kibana-plugin-plugins-data-server.indexpattern.setfieldformat.md) | | (fieldName: string, format: SerializedFieldFormat) => void | | | [sourceFilters](./kibana-plugin-plugins-data-server.indexpattern.sourcefilters.md) | | SourceFilter[] | | | [timeFieldName](./kibana-plugin-plugins-data-server.indexpattern.timefieldname.md) | | string | undefined | | | [title](./kibana-plugin-plugins-data-server.indexpattern.title.md) | | string | | @@ -47,6 +49,7 @@ export declare class IndexPattern implements IIndexPattern | [getComputedFields()](./kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md) | | | | [getFieldByName(name)](./kibana-plugin-plugins-data-server.indexpattern.getfieldbyname.md) | | | | [getFormatterForField(field)](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfield.md) | | Provide a field, get its formatter | +| [getFormatterForFieldNoDefault(fieldname)](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfieldnodefault.md) | | Get formatter for a given field name. Return undefined if none exists | | [getNonScriptedFields()](./kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md) | | | | [getScriptedFields()](./kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md) | | | | [getSourceFiltering()](./kibana-plugin-plugins-data-server.indexpattern.getsourcefiltering.md) | | Get the source filtering configuration for that index. | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.setfieldformat.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.setfieldformat.md new file mode 100644 index 0000000000000..a8f2e726dd9b3 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.setfieldformat.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [setFieldFormat](./kibana-plugin-plugins-data-server.indexpattern.setfieldformat.md) + +## IndexPattern.setFieldFormat property + +Signature: + +```typescript +setFieldFormat: (fieldName: string, format: SerializedFieldFormat) => void; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md index 21ddaef3a0b94..af96e1413ba0c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md @@ -15,5 +15,6 @@ export interface ISearchOptions | Property | Type | Description | | --- | --- | --- | | [abortSignal](./kibana-plugin-plugins-data-server.isearchoptions.abortsignal.md) | AbortSignal | An AbortSignal that allows the caller of search to abort a search request. | +| [sessionId](./kibana-plugin-plugins-data-server.isearchoptions.sessionid.md) | string | A session ID, grouping multiple search requests into a single session. | | [strategy](./kibana-plugin-plugins-data-server.isearchoptions.strategy.md) | string | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.sessionid.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.sessionid.md new file mode 100644 index 0000000000000..03043de5193d2 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.sessionid.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) > [sessionId](./kibana-plugin-plugins-data-server.isearchoptions.sessionid.md) + +## ISearchOptions.sessionId property + +A session ID, grouping multiple search requests into a single session. + +Signature: + +```typescript +sessionId?: string; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md index 6d2774d86f109..f11003887a6df 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md @@ -8,7 +8,7 @@ ```typescript data: { - table: KibanaDatatable; + table: Datatable; column: number; range: number[]; timeFieldName?: string; diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md index 0f92ed86301da..f23cb44a7f014 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md @@ -14,6 +14,6 @@ export interface RangeSelectContext | Property | Type | Description | | --- | --- | --- | -| [data](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md) | {
table: KibanaDatatable;
column: number;
range: number[];
timeFieldName?: string;
} | | +| [data](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md) | {
table: Datatable;
column: number;
range: number[];
timeFieldName?: string;
} | | | [embeddable](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.embeddable.md) | T | | diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md index 92c33affc47a9..e7c1be172cd70 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md @@ -9,7 +9,7 @@ ```typescript data: { data: Array<{ - table: Pick; + table: Pick; column: number; row: number; value: any; diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md index 13133095956c6..875c8d276160e 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md @@ -14,6 +14,6 @@ export interface ValueClickContext | Property | Type | Description | | --- | --- | --- | -| [data](./kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md) | {
data: Array<{
table: Pick<KibanaDatatable, 'rows' | 'columns'>;
column: number;
row: number;
value: any;
}>;
timeFieldName?: string;
negate?: boolean;
} | | +| [data](./kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md) | {
data: Array<{
table: Pick<Datatable, 'rows' | 'columns'>;
column: number;
row: number;
value: any;
}>;
timeFieldName?: string;
negate?: boolean;
} | | | [embeddable](./kibana-plugin-plugins-embeddable-public.valueclickcontext.embeddable.md) | T | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md index a06ab351e62c3..8f134bd3bfe95 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md @@ -4,10 +4,10 @@ ## DatatableColumnType type -This type represents the `type` of any `DatatableColumn` in a `Datatable`. +This type represents the `type` of any `DatatableColumn` in a `Datatable`. its duplicated from KBN\_FIELD\_TYPES Signature: ```typescript -export declare type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +export declare type DatatableColumnType = '_source' | 'attachment' | 'boolean' | 'date' | 'geo_point' | 'geo_shape' | 'ip' | 'murmur3' | 'number' | 'string' | 'unknown' | 'conflict' | 'object' | 'nested' | 'histogram' | 'null'; ``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md index 2f96ad6e040bd..013624f30b45a 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md @@ -41,6 +41,6 @@ export declare class Executor = RecordSignature: ```typescript -run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext): Promise; +run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext, options?: ExpressionExecOptions): Promise; ``` ## Parameters @@ -19,6 +19,7 @@ run = Recordstring | ExpressionAstExpression | | | input | Input | | | context | ExtraContext | | +| options | ExpressionExecOptions | | Returns: diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.cumulative_sum.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.cumulative_sum.md new file mode 100644 index 0000000000000..ad1de0cc5f45b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.cumulative_sum.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) > [cumulative\_sum](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.cumulative_sum.md) + +## ExpressionFunctionDefinitions.cumulative\_sum property + +Signature: + +```typescript +cumulative_sum: ExpressionFunctionCumulativeSum; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md index 914c5d6ebe2f6..d1703a1e019e6 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md @@ -17,6 +17,7 @@ export interface ExpressionFunctionDefinitions | Property | Type | Description | | --- | --- | --- | | [clog](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.clog.md) | ExpressionFunctionClog | | +| [cumulative\_sum](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.cumulative_sum.md) | ExpressionFunctionCumulativeSum | | | [font](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.font.md) | ExpressionFunctionFont | | | [kibana\_context](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana_context.md) | ExpressionFunctionKibanaContext | | | [kibana](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana.md) | ExpressionFunctionKibana | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md index b8211a6bff27c..18b856b946da4 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md @@ -9,5 +9,5 @@ Starts expression execution and immediately returns `ExecutionContract` instance Signature: ```typescript -execute: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => ExecutionContract; +execute: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext, options?: ExpressionExecOptions) => ExecutionContract; ``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.md index 34bf16c121326..def572abead22 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.md @@ -16,12 +16,12 @@ export interface ExpressionsServiceStart | Property | Type | Description | | --- | --- | --- | -| [execute](./kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md) | <Input = unknown, Output = unknown, ExtraContext extends Record<string, unknown> = Record<string, unknown>>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => ExecutionContract<ExtraContext, Input, Output> | Starts expression execution and immediately returns ExecutionContract instance that tracks the progress of the execution and can be used to interact with the execution. | +| [execute](./kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md) | <Input = unknown, Output = unknown, ExtraContext extends Record<string, unknown> = Record<string, unknown>>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext, options?: ExpressionExecOptions) => ExecutionContract<ExtraContext, Input, Output> | Starts expression execution and immediately returns ExecutionContract instance that tracks the progress of the execution and can be used to interact with the execution. | | [fork](./kibana-plugin-plugins-expressions-public.expressionsservicestart.fork.md) | () => ExpressionsService | Create a new instance of ExpressionsService. The new instance inherits all state of the original ExpressionsService, including all expression types, expression functions and context. Also, all new types and functions registered in the original services AFTER the forking event will be available in the forked instance. However, all new types and functions registered in the forked instances will NOT be available to the original service. | | [getFunction](./kibana-plugin-plugins-expressions-public.expressionsservicestart.getfunction.md) | (name: string) => ReturnType<Executor['getFunction']> | Get a registered ExpressionFunction by its name, which was registered using the registerFunction method. The returned ExpressionFunction instance is an internal representation of the function in Expressions service - do not mutate that object. | | [getRenderer](./kibana-plugin-plugins-expressions-public.expressionsservicestart.getrenderer.md) | (name: string) => ReturnType<ExpressionRendererRegistry['get']> | Get a registered ExpressionRenderer by its name, which was registered using the registerRenderer method. The returned ExpressionRenderer instance is an internal representation of the renderer in Expressions service - do not mutate that object. | | [getType](./kibana-plugin-plugins-expressions-public.expressionsservicestart.gettype.md) | (name: string) => ReturnType<Executor['getType']> | Get a registered ExpressionType by its name, which was registered using the registerType method. The returned ExpressionType instance is an internal representation of the type in Expressions service - do not mutate that object. | -| [run](./kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md) | <Input, Output, ExtraContext extends Record<string, unknown> = Record<string, unknown>>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => Promise<Output> | Executes expression string or a parsed expression AST and immediately returns the result.Below example will execute sleep 100 | clog expression with 123 initial input to the first function. +| [run](./kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md) | <Input, Output, ExtraContext extends Record<string, unknown> = Record<string, unknown>>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext, options?: ExpressionExecOptions) => Promise<Output> | Executes expression string or a parsed expression AST and immediately returns the result.Below example will execute sleep 100 | clog expression with 123 initial input to the first function. ```ts expressions.run('sleep 100 | clog', 123); diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md index 578c583624ad0..d717af51a00fa 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md @@ -24,5 +24,5 @@ expressions.run('...', null, { elasticsearchClient }); Signature: ```typescript -run: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => Promise; +run: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext, options?: ExpressionExecOptions) => Promise; ``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.debug.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.debug.md new file mode 100644 index 0000000000000..b27246449cc7c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.debug.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [debug](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.debug.md) + +## IExpressionLoaderParams.debug property + +Signature: + +```typescript +debug?: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md index b8a174f93fb99..d6e02350bae3f 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md @@ -17,6 +17,7 @@ export interface IExpressionLoaderParams | [context](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.context.md) | ExpressionValue | | | [customFunctions](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customfunctions.md) | [] | | | [customRenderers](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customrenderers.md) | [] | | +| [debug](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.debug.md) | boolean | | | [disableCaching](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.disablecaching.md) | boolean | | | [inspectorAdapters](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.inspectoradapters.md) | Adapters | | | [onRenderError](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.onrendererror.md) | RenderErrorHandlerFnType | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md deleted file mode 100644 index c8aa768a883d6..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) > [columns](./kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md) - -## KibanaDatatable.columns property - -Signature: - -```typescript -columns: KibanaDatatableColumn[]; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.md deleted file mode 100644 index 4ea1d6f42b66d..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) - -## KibanaDatatable interface - -Signature: - -```typescript -export interface KibanaDatatable -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [columns](./kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md) | KibanaDatatableColumn[] | | -| [rows](./kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md) | KibanaDatatableRow[] | | -| [type](./kibana-plugin-plugins-expressions-public.kibanadatatable.type.md) | typeof name | | - diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md deleted file mode 100644 index 43f3243dc4fa7..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) > [rows](./kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md) - -## KibanaDatatable.rows property - -Signature: - -```typescript -rows: KibanaDatatableRow[]; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.type.md deleted file mode 100644 index 996f59cbb77a1..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) > [type](./kibana-plugin-plugins-expressions-public.kibanadatatable.type.md) - -## KibanaDatatable.type property - -Signature: - -```typescript -type: typeof name; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md deleted file mode 100644 index b517c1610261b..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [formatHint](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md) - -## KibanaDatatableColumn.formatHint property - -Signature: - -```typescript -formatHint?: SerializedFieldFormat; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md deleted file mode 100644 index e7d43190589a7..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [id](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md) - -## KibanaDatatableColumn.id property - -Signature: - -```typescript -id: string; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md deleted file mode 100644 index 138c19f0ec7bd..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) - -## KibanaDatatableColumn interface - -Signature: - -```typescript -export interface KibanaDatatableColumn -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [formatHint](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md) | SerializedFieldFormat | | -| [id](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md) | string | | -| [meta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md) | KibanaDatatableColumnMeta | | -| [name](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md) | string | | - diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md deleted file mode 100644 index df2d09bf3cc55..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [meta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md) - -## KibanaDatatableColumn.meta property - -Signature: - -```typescript -meta?: KibanaDatatableColumnMeta; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md deleted file mode 100644 index 841ad67f3f521..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [name](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md) - -## KibanaDatatableColumn.name property - -Signature: - -```typescript -name: string; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md deleted file mode 100644 index 2ec6edda4cbca..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) > [aggConfigParams](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md) - -## KibanaDatatableColumnMeta.aggConfigParams property - -Signature: - -```typescript -aggConfigParams?: Record; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md deleted file mode 100644 index 2287c28398f7f..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) > [indexPatternId](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md) - -## KibanaDatatableColumnMeta.indexPatternId property - -Signature: - -```typescript -indexPatternId?: string; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md deleted file mode 100644 index b2f8c9d06a727..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) - -## KibanaDatatableColumnMeta interface - -Signature: - -```typescript -export interface KibanaDatatableColumnMeta -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [aggConfigParams](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md) | Record<string, any> | | -| [indexPatternId](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md) | string | | -| [type](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md) | string | | - diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md deleted file mode 100644 index 98d4a0c2d43c3..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) > [type](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md) - -## KibanaDatatableColumnMeta.type property - -Signature: - -```typescript -type: string; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablerow.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablerow.md deleted file mode 100644 index cb5f1ad70f628..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablerow.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableRow](./kibana-plugin-plugins-expressions-public.kibanadatatablerow.md) - -## KibanaDatatableRow interface - -Signature: - -```typescript -export interface KibanaDatatableRow -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md index b0c732188a46e..db09f966e2fa5 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md @@ -72,10 +72,6 @@ | [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) | | | [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) | | | [IRegistry](./kibana-plugin-plugins-expressions-public.iregistry.md) | | -| [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) | | -| [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) | | -| [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) | | -| [KibanaDatatableRow](./kibana-plugin-plugins-expressions-public.kibanadatatablerow.md) | | | [PointSeriesColumn](./kibana-plugin-plugins-expressions-public.pointseriescolumn.md) | Column in a PointSeries | | [Range](./kibana-plugin-plugins-expressions-public.range.md) | | | [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) | | @@ -95,7 +91,7 @@ | [AnyExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.anyexpressionfunctiondefinition.md) | Type to capture every possible expression function definition. | | [AnyExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.anyexpressiontypedefinition.md) | | | [ArgumentType](./kibana-plugin-plugins-expressions-public.argumenttype.md) | This type represents all of the possible combinations of properties of an Argument in an Expression Function. The presence or absence of certain fields influence the shape and presence of others within each arg in the specification. | -| [DatatableColumnType](./kibana-plugin-plugins-expressions-public.datatablecolumntype.md) | This type represents the type of any DatatableColumn in a Datatable. | +| [DatatableColumnType](./kibana-plugin-plugins-expressions-public.datatablecolumntype.md) | This type represents the type of any DatatableColumn in a Datatable. its duplicated from KBN\_FIELD\_TYPES | | [DatatableRow](./kibana-plugin-plugins-expressions-public.datatablerow.md) | This type represents a row in a Datatable. | | [ExecutionContainer](./kibana-plugin-plugins-expressions-public.executioncontainer.md) | | | [ExecutorContainer](./kibana-plugin-plugins-expressions-public.executorcontainer.md) | | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md index 4afce913526de..dc98acffa1236 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md @@ -4,10 +4,10 @@ ## DatatableColumnType type -This type represents the `type` of any `DatatableColumn` in a `Datatable`. +This type represents the `type` of any `DatatableColumn` in a `Datatable`. its duplicated from KBN\_FIELD\_TYPES Signature: ```typescript -export declare type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +export declare type DatatableColumnType = '_source' | 'attachment' | 'boolean' | 'date' | 'geo_point' | 'geo_shape' | 'ip' | 'murmur3' | 'number' | 'string' | 'unknown' | 'conflict' | 'object' | 'nested' | 'histogram' | 'null'; ``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md index ec4e0bdcc4569..46ad60ae07126 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md @@ -41,6 +41,6 @@ export declare class Executor = RecordSignature: ```typescript -run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext): Promise; +run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext, options?: ExpressionExecOptions): Promise; ``` ## Parameters @@ -19,6 +19,7 @@ run = Recordstring | ExpressionAstExpression | | | input | Input | | | context | ExtraContext | | +| options | ExpressionExecOptions | | Returns: diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.cumulative_sum.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.cumulative_sum.md new file mode 100644 index 0000000000000..2fb8cde92e877 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.cumulative_sum.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) > [cumulative\_sum](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.cumulative_sum.md) + +## ExpressionFunctionDefinitions.cumulative\_sum property + +Signature: + +```typescript +cumulative_sum: ExpressionFunctionCumulativeSum; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md index 71cd0b98a68c2..05b4ddce4ccde 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md @@ -17,6 +17,7 @@ export interface ExpressionFunctionDefinitions | Property | Type | Description | | --- | --- | --- | | [clog](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.clog.md) | ExpressionFunctionClog | | +| [cumulative\_sum](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.cumulative_sum.md) | ExpressionFunctionCumulativeSum | | | [font](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.font.md) | ExpressionFunctionFont | | | [kibana\_context](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana_context.md) | ExpressionFunctionKibanaContext | | | [kibana](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana.md) | ExpressionFunctionKibana | | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md deleted file mode 100644 index 423e543e4307a..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) > [columns](./kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md) - -## KibanaDatatable.columns property - -Signature: - -```typescript -columns: KibanaDatatableColumn[]; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.md deleted file mode 100644 index 30ee3ac2fcd13..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) - -## KibanaDatatable interface - -Signature: - -```typescript -export interface KibanaDatatable -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [columns](./kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md) | KibanaDatatableColumn[] | | -| [rows](./kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md) | KibanaDatatableRow[] | | -| [type](./kibana-plugin-plugins-expressions-server.kibanadatatable.type.md) | typeof name | | - diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md deleted file mode 100644 index 42170a83fc3c8..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) > [rows](./kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md) - -## KibanaDatatable.rows property - -Signature: - -```typescript -rows: KibanaDatatableRow[]; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.type.md deleted file mode 100644 index c36674540a1ba..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) > [type](./kibana-plugin-plugins-expressions-server.kibanadatatable.type.md) - -## KibanaDatatable.type property - -Signature: - -```typescript -type: typeof name; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md deleted file mode 100644 index a1e6949019dcb..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [formatHint](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md) - -## KibanaDatatableColumn.formatHint property - -Signature: - -```typescript -formatHint?: SerializedFieldFormat; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md deleted file mode 100644 index 6f90da1ac9c94..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [id](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md) - -## KibanaDatatableColumn.id property - -Signature: - -```typescript -id: string; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md deleted file mode 100644 index 171477911502f..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) - -## KibanaDatatableColumn interface - -Signature: - -```typescript -export interface KibanaDatatableColumn -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [formatHint](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md) | SerializedFieldFormat | | -| [id](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md) | string | | -| [meta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md) | KibanaDatatableColumnMeta | | -| [name](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md) | string | | - diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md deleted file mode 100644 index 40b20d51e6ec6..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [meta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md) - -## KibanaDatatableColumn.meta property - -Signature: - -```typescript -meta?: KibanaDatatableColumnMeta; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md deleted file mode 100644 index 3a85e2325483a..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [name](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md) - -## KibanaDatatableColumn.name property - -Signature: - -```typescript -name: string; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md deleted file mode 100644 index 539b24174f725..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) > [aggConfigParams](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md) - -## KibanaDatatableColumnMeta.aggConfigParams property - -Signature: - -```typescript -aggConfigParams?: Record; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md deleted file mode 100644 index 2704915a15071..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) > [indexPatternId](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md) - -## KibanaDatatableColumnMeta.indexPatternId property - -Signature: - -```typescript -indexPatternId?: string; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md deleted file mode 100644 index d9a96e665f010..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) - -## KibanaDatatableColumnMeta interface - -Signature: - -```typescript -export interface KibanaDatatableColumnMeta -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [aggConfigParams](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md) | Record<string, any> | | -| [indexPatternId](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md) | string | | -| [type](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md) | string | | - diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md deleted file mode 100644 index 56e3757ef621a..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) > [type](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md) - -## KibanaDatatableColumnMeta.type property - -Signature: - -```typescript -type: string; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablerow.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablerow.md deleted file mode 100644 index dd0f3f4cb2f60..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablerow.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableRow](./kibana-plugin-plugins-expressions-server.kibanadatatablerow.md) - -## KibanaDatatableRow interface - -Signature: - -```typescript -export interface KibanaDatatableRow -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md index dd7c7af466bd0..9e2189dad2732 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md @@ -63,10 +63,6 @@ | [Font](./kibana-plugin-plugins-expressions-server.font.md) | An interface representing a font in Canvas, with a textual label and the CSS font-value. | | [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) | | | [IRegistry](./kibana-plugin-plugins-expressions-server.iregistry.md) | | -| [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) | | -| [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) | | -| [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) | | -| [KibanaDatatableRow](./kibana-plugin-plugins-expressions-server.kibanadatatablerow.md) | | | [PointSeriesColumn](./kibana-plugin-plugins-expressions-server.pointseriescolumn.md) | Column in a PointSeries | | [Range](./kibana-plugin-plugins-expressions-server.range.md) | | | [SerializedDatatable](./kibana-plugin-plugins-expressions-server.serializeddatatable.md) | | @@ -79,7 +75,7 @@ | [AnyExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.anyexpressionfunctiondefinition.md) | Type to capture every possible expression function definition. | | [AnyExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.anyexpressiontypedefinition.md) | | | [ArgumentType](./kibana-plugin-plugins-expressions-server.argumenttype.md) | This type represents all of the possible combinations of properties of an Argument in an Expression Function. The presence or absence of certain fields influence the shape and presence of others within each arg in the specification. | -| [DatatableColumnType](./kibana-plugin-plugins-expressions-server.datatablecolumntype.md) | This type represents the type of any DatatableColumn in a Datatable. | +| [DatatableColumnType](./kibana-plugin-plugins-expressions-server.datatablecolumntype.md) | This type represents the type of any DatatableColumn in a Datatable. its duplicated from KBN\_FIELD\_TYPES | | [DatatableRow](./kibana-plugin-plugins-expressions-server.datatablerow.md) | This type represents a row in a Datatable. | | [ExecutionContainer](./kibana-plugin-plugins-expressions-server.executioncontainer.md) | | | [ExecutorContainer](./kibana-plugin-plugins-expressions-server.executorcontainer.md) | | diff --git a/docs/discover/kuery.asciidoc b/docs/discover/kuery.asciidoc index f306f2b8f763f..35f1160ee834d 100644 --- a/docs/discover/kuery.asciidoc +++ b/docs/discover/kuery.asciidoc @@ -115,7 +115,7 @@ KQL supports `>`, `>=`, `<`, and `<=`. For example: [source,yaml] ------------------- -account_number:>=100 and items_sold:<=200 +account_number >= 100 and items_sold <= 200 ------------------- [discrete] diff --git a/docs/management/images/management-index-templates-mappings.png b/docs/management/images/management-index-templates-mappings.png old mode 100755 new mode 100644 index 62321fc0e4666..beb964b348171 Binary files a/docs/management/images/management-index-templates-mappings.png and b/docs/management/images/management-index-templates-mappings.png differ diff --git a/docs/management/images/management-index-templates.png b/docs/management/images/management-index-templates.png old mode 100755 new mode 100644 index 6f2564af72b5c..07f1fb9a7add1 Binary files a/docs/management/images/management-index-templates.png and b/docs/management/images/management-index-templates.png differ diff --git a/docs/management/images/management_index_component_template.png b/docs/management/images/management_index_component_template.png new file mode 100644 index 0000000000000..c03029fd172f0 Binary files /dev/null and b/docs/management/images/management_index_component_template.png differ diff --git a/docs/management/images/management_index_create_wizard.png b/docs/management/images/management_index_create_wizard.png old mode 100755 new mode 100644 index b18c36366be94..bff1dd4cd0e7a Binary files a/docs/management/images/management_index_create_wizard.png and b/docs/management/images/management_index_create_wizard.png differ diff --git a/docs/management/images/management_index_data_stream_backing_index.png b/docs/management/images/management_index_data_stream_backing_index.png new file mode 100644 index 0000000000000..a5c577affbbb2 Binary files /dev/null and b/docs/management/images/management_index_data_stream_backing_index.png differ diff --git a/docs/management/images/management_index_data_stream_stats.png b/docs/management/images/management_index_data_stream_stats.png new file mode 100644 index 0000000000000..a67ab4a7deb32 Binary files /dev/null and b/docs/management/images/management_index_data_stream_stats.png differ diff --git a/docs/management/images/management_index_details.png b/docs/management/images/management_index_details.png index 77aeaba472307..b199d13218f5a 100644 Binary files a/docs/management/images/management_index_details.png and b/docs/management/images/management_index_details.png differ diff --git a/docs/management/images/management_index_labels.png b/docs/management/images/management_index_labels.png old mode 100755 new mode 100644 index 79e378e367e78..a89c32e08beff Binary files a/docs/management/images/management_index_labels.png and b/docs/management/images/management_index_labels.png differ diff --git a/docs/management/managing-indices.asciidoc b/docs/management/managing-indices.asciidoc index 24cd094c877c6..b199e076443ab 100644 --- a/docs/management/managing-indices.asciidoc +++ b/docs/management/managing-indices.asciidoc @@ -2,32 +2,48 @@ [[managing-indices]] == Index Management -*Index Management* enables you to view index settings, -mappings, and statistics and perform index-level operations. -These include refreshing, flushing, clearing the cache, force merging segments, -freezing indices, and more. Practicing good index management helps ensure -that your data is stored in the most cost-effective way possible. +*Index Management* features are an easy, convenient way to manage your +{es} cluster's indices, data streams, and index templates. Practicing good index +management ensures your data is stored correctly and in the most cost-effective +way possible. -*Index Management* also helps you create index templates. A template reduces -the amount of bookkeeping when working with indices. Instead of manually -setting up your indices, you can create them automatically from a template, -ensuring that your settings, mappings, and aliases are consistently defined. +[float] +=== What you'll learn + +This page shows you how to use *Index Management* features to: -To manage your indices, open the menu, then go to *Stack Management > Data > Index Management*. +* View and edit index settings. +* View mappings and statistics for an index. +* Perform index-level operations, such as refreshes and freezes. +* View and manage data streams. +* Create index templates to automatically configure new data streams and +indices. + +To manage your indices, open the menu, then click *Stack Management > Index +Management*. [role="screenshot"] image::images/management_index_labels.png[Index Management UI] -If security is enabled, -you must have the `monitor` cluster privilege and the `view_index_metadata` +[float] +=== Before you start + +Before using this feature, you should be familiar with index management +operations. Refer to the {ref}/indices.html[index management APIs], the +{ref}/indices-templates.html[index template APIs], and the +{ref}/data-streams.html[data streams documentation]. + +[float] +=== Required permissions + +The minimum required permissions to access *Index Management* are +the `monitor` cluster privilege and the `view_index_metadata` and `manage` index privileges to view the data. For index templates, you must have the `manage_index_templates` cluster privilege. See {ref}/security-privileges.html[Security privileges] for more information. -Before using this feature, you should be familiar with index management -operations. Refer to the {ref}/indices.html[index management APIs] -and the {ref}/indices-templates.html[index template APIs]. +You can add these privileges in *Stack Management > Security > Roles*. [float] === View and edit indices @@ -50,7 +66,7 @@ image::images/management_index_details.png[Index Management UI] [float] === Perform index-level operations -Use the *Manage* menu to perform index-level operations. This menu +Use the *Manage* menu to perform index-level operations. This menu is available in the index details view, or when you select the checkbox of one or more indices on the overview page. The menu includes the following actions: @@ -78,16 +94,37 @@ searchable, but queries take longer. * *Delete index*. Permanently removes the index and all of its documents. -* *Add lifecycle policy*. Specifies a policy for managing the lifecycle of the +* *Add lifecycle policy*. Specifies a policy for managing the lifecycle of the index. +[float] +[[manage-data-streams]] +=== Manage data streams + +A {ref}/data-streams.html[data stream] lets you store time series data across +multiple backing indices while giving you a single named resource to use in +requests. The *Data Streams* view lists your data streams and lets you examine +or delete them. + +To view more information about a data stream, such as its generation or its +current index lifecycle policy, click the stream's name. + +[role="screenshot"] +image::images/management_index_data_stream_stats.png[Data stream details] + +To view information about the stream's backing indices, click the number in the +*Indices* column. + +[role="screenshot"] +image::images/management_index_data_stream_backing_index.png[Backing index] + [float] [[manage-index-templates]] === Manage index templates An index template defines {ref}/index-modules.html#index-modules-settings[settings], {ref}/mapping.html[mappings], and {ref}/indices-add-alias.html[aliases] -that you can automatically apply when creating a new index. {es} applies a +that you can automatically apply when creating a new index. {es} applies a template to a new index based on an index pattern that matches the index name. The *Index Templates* view lists your templates and enables you to examine, edit, clone, and @@ -103,33 +140,56 @@ so you must create the template before you create the indices. [float] -==== Example: Create an index template +==== Try it: Create an index template -In this example, you’ll create an index template for randomly generated log files. +In this tutorial, you’ll create an index template for randomly generated log +files. You'll then use the template to configure two new indices. -Open the *Create template* wizard, and enter `logs_template` in the *Name* -field. Set *Index pattern* to `logstash*` so the template matches any index -with that index pattern. The merge order and version are both optional, -and you'll leave them blank in this example. +*Step 1. Add a name and index pattern* +. In the *Index Templates* view, open the *Create template* wizard. ++ [role="screenshot"] image::images/management_index_create_wizard.png[Create wizard] -The second step in the *Create template* wizard allows you to define index settings. -These settings are optional, and this example skips this step. +. In the *Name* field, enter `my-index-template`. -The logs data set requires a -mapping to label the latitude and longitude pairs as geographic locations -by applying the geo_point type. In the third step of the wizard, define this mapping -under the *Mapped fields* tab as follows: +. Set *Index pattern* to `my-index-*` so the template matches any index +with that index pattern. +. Leave *Data Stream*, *Priority*, *Version*, and *_meta field* as-is or blank. + +. Click *Next*. + +*Step 2. Add settings, mappings, and index aliases* + +. Add component templates to your index template. ++ +{ref}/indices-component-template.html[Component templates] are pre-configured +sets of mappings, index settings, and index aliases you can reuse across +multiple index templates. Badges indicate whether a component template contains +mappings (*M*), index settings (*S*), index aliases (*A*), or a combination of +the three. ++ +Component templates are optional. For this tutorial, do not add any component +templates. ++ [role="screenshot"] -image::images/management-index-templates-mappings.png[Mapped fields page] +image::images/management_index_component_template.png[Component templates page] -Alternatively, you can click the *Load JSON* link and define the mapping as JSON: +. Define index settings. These are optional. For this tutorial, leave this +section blank. +. Define a mapping that contains an object field named `geo` with a child +geo-point field named `coordinates`: ++ +[role="screenshot"] +image::images/management-index-templates-mappings.png[Mapped fields page] ++ +Alternatively, you can click the *Load JSON* link and define the mapping as JSON: ++ [source,js] ----------------------------------- +---- { "properties": { "geo": { @@ -141,28 +201,33 @@ Alternatively, you can click the *Load JSON* link and define the mapping as JSON } } } ----------------------------------- - +---- ++ You can create additional mapping configurations in the *Dynamic templates* and -*Advanced options* tabs. No additional mappings are required for this example. - -In the fourth step, define an alias named `logstash`. +*Advanced options* tabs. No additional mappings are required for this tutorial. +. Define an index alias named `my-index`: ++ [source,js] ----------------------------------- +---- { - "logstash": {} + "my-index": {} } ----------------------------------- +---- + +. On the review page, check the summary. If everything looks right, click +*Create template*. -A summary of the template is in step 5. If everything looks right, click *Create template*. +*Step 3. Create new indices* -At this point, you’re ready to use the {es} index API to load the logs data. -In the {kib} *Console*, index two documents: +You’re now ready to load the logs data and create new indices using your index +template. +. In the {kib} *Console*, index the following documents: ++ [source,js] ----------------------------------- -POST /logstash-2019.05.18/_doc +---- +POST /my-index-000001/_doc { "@timestamp": "2019-05-18T15:57:27.541Z", "ip": "225.44.217.191", @@ -177,7 +242,7 @@ POST /logstash-2019.05.18/_doc "url": "https://media-for-the-masses.theacademyofperformingartsandscience.org/uploads/charles-fullerton.jpg" } -POST /logstash-2019.05.20/_doc +POST /my-index-000002/_doc { "@timestamp": "2019-05-20T03:44:20.844Z", "ip": "198.247.165.49", @@ -192,7 +257,10 @@ POST /logstash-2019.05.20/_doc "memory": 241720, "url": "https://theacademyofperformingartsandscience.org/people/type:astronauts/name:laurel-b-clark/profile" } ----------------------------------- +---- ++ +These requests create two indices: `my-index-000001` and `my-index-000002`. -The mappings and alias are configured automatically based on the template. To verify, you -can view one of the newly created indices using the {ref}/indices-get-index.html#indices-get-index[index API]. +. Use the {es} {ref}/indices-get-index.html#indices-get-index[get index API] to +view one of the newly created indices. The index's mappings and alias are +configured automatically based on the template. diff --git a/docs/maps/geojson-upload.asciidoc b/docs/maps/geojson-upload.asciidoc deleted file mode 100644 index 6c28840087252..0000000000000 --- a/docs/maps/geojson-upload.asciidoc +++ /dev/null @@ -1,44 +0,0 @@ -[role="xpack"] -[[geojson-upload]] -== Upload GeoJSON data - -Maps makes it easy to import geospatial data into the Elastic Stack. -Using the GeoJSON Upload feature, you can drag and drop your point and shape -data files directly into {es}, and then use them as layers -in the map. You can also use the GeoJSON data in the broader Kibana ecosystem, -for example, in visualizations and Canvas workpads. - -[float] -=== Why GeoJSON? -GeoJSON is an open-standard file format for storing geospatial vector data. -Although many vector data formats are available in the GIS community, -GeoJSON is the most commonly used and flexible option. -[float] - -=== Upload a GeoJSON file -Follow these instructions to upload a GeoJSON data file, or try the -<>. - -. Open the menu, go to *Maps*, and then click *Add layer*. -. Click *Uploaded GeoJSON*. -+ -[role="screenshot"] -image::maps/images/fu_gs_select_source_file_upload.png[] - -. Use the file chooser to select a valid GeoJSON file. The file will load -a preview of the data on the map. -. Use the default *Index type* of {ref}/geo-point.html[geo_point] for point data, -or override it and select {ref}/geo-shape.html[geo_shape]. -All other shapes will default to a type of `geo_shape`. -. Leave the default *Index name* and *Index pattern* names (the name of the uploaded -file minus its extension). You might need to change the index name if it is invalid. -. Click *Import file*. -+ -Upon completing the indexing process and creating the associated index pattern, -the Elasticsearch responses are shown on the *Layer add panel* and the indexed data -appears on the map. The geospatial data on the map -should be identical to the locally-previewed data, but now it's indexed data from Elasticsearch. - -. To continue adding data to the map, click *Add layer*. -. In *Layer settings*, adjust any settings or <> as needed. -. Click *Save & close*. diff --git a/docs/maps/images/fu_gs_select_source_file_upload.png b/docs/maps/images/fu_gs_select_source_file_upload.png deleted file mode 100644 index 4fe1162acb29c..0000000000000 Binary files a/docs/maps/images/fu_gs_select_source_file_upload.png and /dev/null differ diff --git a/docs/maps/import-geospatial-data.asciidoc b/docs/maps/import-geospatial-data.asciidoc new file mode 100644 index 0000000000000..194d09c491cee --- /dev/null +++ b/docs/maps/import-geospatial-data.asciidoc @@ -0,0 +1,46 @@ +[role="xpack"] +[[import-geospatial-data]] +== Import geospatial data + +To import geospatical data into the Elastic Stack, the data must be indexed as {ref}/geo-point.html[geo_point] or {ref}/geo-shape.html[geo_shape]. +Geospatial data comes in many formats. +Choose an import tool based on the format of your geospatial data. + +[discrete] +=== Upload CSV with latitude and longitude columns + +*File Data Visualizer* indexes CSV files with latitude and longitude columns as a geo_point. + +. Open the side navigation menu, and click *Machine Learning*. +. Select the *Data Visualizer* tab, then click *Upload file*. +. Use the file chooser to select a CSV file. +. Click *Import*. +. Select the *Advanced* tab. +. Set *Index name*. +. Click *Add combined field*, then click *Add geo point field*. +. Fill out the form and click *Add*. +. Click *Import*. + +[discrete] +=== Upload a GeoJSON file + +*Upload GeoJSON* indexes GeoJSON features as a geo_point or geo_shape. + +. <>. +. Click *Add layer*. +. Select *Upload GeoJSON*. +. Use the file chooser to select a GeoJSON file. +. Click *Import file*. + +[discrete] +=== Upload data with IP addresses + +The GeoIP processor adds information about the geographical location of IP addresses. +See {ref}/geoip-processor.html[GeoIP processor] for details. +For private IP addresses, see https://www.elastic.co/blog/enriching-elasticsearch-data-geo-ips-internal-private-ip-addresses[Enriching data with GeoIPs from internal, private IP addresses]. + +[discrete] +=== Upload data with GDAL + +https://www.gdal.org/[GDAL] (Geospatial Data Abstraction Library) contains command line tools that can convert geospatial data between 75 different geospatial file formats and index that geospatial data into {es}. +See https://www.elastic.co/blog/how-to-ingest-geospatial-data-into-elasticsearch-with-gdal[Ingest geospatial data into Elasticsearch with GDAL] for details. diff --git a/docs/maps/index.asciidoc b/docs/maps/index.asciidoc index 8999b9fe20b11..3c3537826a6a8 100644 --- a/docs/maps/index.asciidoc +++ b/docs/maps/index.asciidoc @@ -56,6 +56,6 @@ include::maps-aggregations.asciidoc[] include::search.asciidoc[] include::map-settings.asciidoc[] include::connect-to-ems.asciidoc[] -include::geojson-upload.asciidoc[] +include::import-geospatial-data.asciidoc[] include::indexing-geojson-data-tutorial.asciidoc[] include::trouble-shooting.asciidoc[] diff --git a/docs/maps/indexing-geojson-data-tutorial.asciidoc b/docs/maps/indexing-geojson-data-tutorial.asciidoc index d1a6593f61fe1..434c9ab369a5b 100644 --- a/docs/maps/indexing-geojson-data-tutorial.asciidoc +++ b/docs/maps/indexing-geojson-data-tutorial.asciidoc @@ -47,7 +47,7 @@ image::maps/images/fu_gs_new_england_map.png[] For each GeoJSON file you downloaded, complete the following steps: . Click *Add layer*. -. From the list of layer types, click *Uploaded GeoJSON*. +. From the list of layer types, click *Upload GeoJSON*. . Using the File Picker, upload the GeoJSON file. + Depending on the geometry type of your features, this will diff --git a/docs/maps/map-settings.asciidoc b/docs/maps/map-settings.asciidoc index e11be438a2237..f606511a6f391 100644 --- a/docs/maps/map-settings.asciidoc +++ b/docs/maps/map-settings.asciidoc @@ -9,12 +9,16 @@ To access these settings, click *Map settings* in the application toolbar. [[maps-settings-navigation]] === Navigation +*Auto fit map to bounds*:: +When enabled, the map will automatically pan and zoom to show the filtered data bounds. + *Zoom range*:: Constrain the map to the defined zoom range. *Initial map location*:: Configure the initial map center and zoom. * *Map location at save*: Use the map center and zoom from the map position at the time of the latest save. +* *Auto fit map to bounds*: Set the initial map location to show the filtered data bounds. * *Fixed location*: Lock the map center and zoom to fixed values. * *Browser location*: Set the initial map center to the browser location. diff --git a/docs/maps/maps-getting-started.asciidoc b/docs/maps/maps-getting-started.asciidoc index e0d43a571a331..f48ff268755d2 100644 --- a/docs/maps/maps-getting-started.asciidoc +++ b/docs/maps/maps-getting-started.asciidoc @@ -50,7 +50,7 @@ In this tutorial, you'll learn to: The first thing to do is to create a new map. -. If you haven't already, open the menu, then click *{kib} > Maps*. +. If you haven't already, click *{kib} > Maps* from the side navigation. . On the maps list page, click *Create map*. . Set the time range to *Last 7 days*. + diff --git a/docs/maps/search.asciidoc b/docs/maps/search.asciidoc index 0c4042a37f700..09d9788cd37e0 100644 --- a/docs/maps/search.asciidoc +++ b/docs/maps/search.asciidoc @@ -49,6 +49,7 @@ Spatial filters have the following properties: * *Geometry label* enables you to provide a meaningful name for your spatial filter. * *Spatial field* specifies the geo_point or geo_shape field used to determine if a document matches the spatial relation with the specified geometry. * *Spatial relation* determines the {ref}/query-dsl-geo-shape-query.html#_spatial_relations[spatial relation operator] to use at search time. Only available when *Spatial field* is set to geo_shape. +* *Action* specifies whether to apply the filter to the current view or to a drilldown action. Only available when the map is a panel in a {kibana-ref}/dashboard.html[dashboard] with {kibana-ref}/drilldowns.html[drilldowns]. [float] [[maps-phrase-filter]] @@ -56,6 +57,7 @@ Spatial filters have the following properties: A phrase filter narrows search results to documents that contain the specified text. You can create a phrase filter by clicking the plus icon image:maps/images/gs_plus_icon.png[] in a <>. +If the map is a dashboard panel with drilldowns, you can apply a phrase filter to a drilldown by selecting the drilldown action. [role="screenshot"] image::maps/images/create_phrase_filter.png[] diff --git a/docs/maps/vector-layer.asciidoc b/docs/maps/vector-layer.asciidoc index 494bd915b7f56..f7128b0f99f88 100644 --- a/docs/maps/vector-layer.asciidoc +++ b/docs/maps/vector-layer.asciidoc @@ -16,9 +16,20 @@ The index must contain at least one field mapped as {ref}/geo-point.html[geo_poi *Documents*:: Points, lines, and polyons from Elasticsearch. The index must contain at least one field mapped as {ref}/geo-point.html[geo_point] or {ref}/geo-shape.html[geo_shape]. ++ +Results are limited to the `index.max_result_window` index setting, which defaults to 10000. +Select the appropriate *Scaling* option for your use case. ++ +* *Limit results to 10000.* The layer displays features from the first `index.max_result_window` documents. +Results exceeding `index.max_result_window` are not displayed. -NOTE: Document results are limited to the `index.max_result_window` index setting, which defaults to 10000. -Use <> to plot large data sets. +* *Show top hits per entity.* The layer displays the <>. + +* *Show clusters when results exceed 10000.* When results exceed `index.max_result_window`, the layer uses {ref}/search-aggregations-bucket-geotilegrid-aggregation.html[GeoTile grid aggregation] to group your documents into clusters and displays metrics for each cluster. When results are less then `index.max_result_window`, the layer displays features from individual documents. + +* *Use vector tiles.* Vector tiles partition your map into 6 to 8 tiles. +Each tile request is limited to the `index.max_result_window` index setting. +Tiles exceeding `index.max_result_window` have a visual indicator when there are too many features to display. *EMS Boundaries*:: Administrative boundaries from https://www.elastic.co/elastic-maps-service[Elastic Maps Service]. diff --git a/docs/plugins/known-plugins.asciidoc b/docs/plugins/known-plugins.asciidoc index 8fc2b7381de83..7b24de42d8e1c 100644 --- a/docs/plugins/known-plugins.asciidoc +++ b/docs/plugins/known-plugins.asciidoc @@ -14,7 +14,6 @@ This list of plugins is not guaranteed to work on your version of Kibana. Instea * https://github.com/sivasamyk/logtrail[LogTrail] - View, analyze, search and tail log events in realtime with a developer/sysadmin friendly interface * https://github.com/wtakase/kibana-own-home[Own Home] (wtakase) - enables multi-tenancy * https://github.com/asileon/kibana_shard_allocation[Shard Allocation] (asileon) - visualize elasticsearch shard allocation -* https://github.com/samtecspg/conveyor[Conveyor] - Simple (GUI) interface for importing data into Elasticsearch. * https://github.com/wazuh/wazuh-kibana-app[Wazuh] - Wazuh provides host-based security visibility using lightweight multi-platform agents. * https://github.com/TrumanDu/indices_view[Indices View] - View indices related information. * https://github.com/johtani/analyze-api-ui-plugin[Analyze UI] (johtani) - UI for elasticsearch _analyze API diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index d8c200450d7e5..7fc8fe5114e1e 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -28,12 +28,12 @@ This page has moved. Please see the new section in the {heartbeat-ref}/securing- [role="exclude",id="infra-read-only-access"] == Configure source read-only access -This page has moved. Please see the new section in the {metrics-guide}/configure-metrics-source.html[Metrics Monitoring Guide]. +This page has moved. Please see {observability-guide}/configure-settings.html[configure settings]. [role="exclude",id="logs-read-only-access"] == Configure source read-only access -This page has moved. Please see {logs-guide}/configure-logs-source.html[logs configuration]. +This page has moved. Please see {observability-guide}/configure-data-sources.html[configure data sources]. [role="exclude",id="extend"] == Extend your use case diff --git a/docs/settings/monitoring-settings.asciidoc b/docs/settings/monitoring-settings.asciidoc index 917821ad09e2f..f48dbeab9d61a 100644 --- a/docs/settings/monitoring-settings.asciidoc +++ b/docs/settings/monitoring-settings.asciidoc @@ -76,6 +76,9 @@ a|`monitoring.cluster_alerts.` health checks. By default, it matches the <> setting, which has a default value of `30000`. +| `monitoring.ui.elasticsearch.ssl` + | Shares the same configuration as <>. These settings configure encrypted communication between {kib} and the monitoring cluster. + |=== [float] diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc index adfc3964d4204..d44c42db92f41 100644 --- a/docs/settings/reporting-settings.asciidoc +++ b/docs/settings/reporting-settings.asciidoc @@ -10,6 +10,7 @@ You can configure `xpack.reporting` settings in your `kibana.yml` to: * <> * <> * <> +* <> [float] [[general-reporting-settings]] @@ -65,7 +66,7 @@ proxy host requires that the {kib} server has network access to the proxy. [NOTE] ============ -Reporting authenticates requests on the Kibana page only when the hostname matches the +Reporting authenticates requests on the {kib} page only when the hostname matches the <> setting. Therefore Reporting would fail if the set value redirects to another server. For that reason, `"0"` is an invalid setting because, in the Reporting browser, it becomes an automatic redirect to `"0.0.0.0"`. @@ -214,6 +215,23 @@ a| `xpack.reporting.capture.browser` | The maximum {ref}/common-options.html#byte-units[byte size] of a CSV file before being truncated. This setting exists to prevent large exports from causing performance and storage issues. Can be specified as number of bytes. Defaults to `10mb`. +|=== + +[NOTE] +============ +Setting `xpack.reporting.csv.maxSizeBytes` much larger than the default 10 MB limit has the potential to negatively affect the +performance of {kib} and your {es} cluster. There is no enforced maximum for this setting, but a reasonable maximum value depends +on multiple factors: + +* The `http.max_content_length` setting in {es}. +* Network proxies, which are often configured by default to block large requests with a 413 error. +* The amount of memory available to the {kib} server, which limits the size of CSV data that must be held temporarily. + +For information about {kib} memory limits, see <>. +============ + +[cols="2*<"] +|=== | `xpack.reporting.csv.scroll.size` | Number of documents retrieved from {es} for each scroll iteration during a CSV @@ -248,6 +266,11 @@ a| `xpack.reporting.capture.browser` exist. Configure this to a unique value, beginning with `.reporting-`, for every {kib} instance that has a unique <> setting. Defaults to `.reporting`. +| `xpack.reporting.capture.networkPolicy` + | Capturing a screenshot from a {kib} page involves sending out requests for all the linked web assets. For example, a Markdown + visualization can show an image from a remote server. You can configure what type of requests to allow or filter by setting a + <> for Reporting. + | `xpack.reporting.roles.allow` | Specifies the roles in addition to superusers that can use reporting. Defaults to `[ "reporting_user" ]`. + diff --git a/docs/settings/security-settings.asciidoc b/docs/settings/security-settings.asciidoc index 00e5f973f7d87..6b01094f7248a 100644 --- a/docs/settings/security-settings.asciidoc +++ b/docs/settings/security-settings.asciidoc @@ -155,7 +155,7 @@ There is a very limited set of cases when you'd want to change these settings. F | `xpack.security.authc.http.autoSchemesEnabled` | Determines if HTTP authentication schemes used by the enabled authentication providers should be automatically supported during HTTP authentication. By default, this setting is set to `true`. -| `xpack.security.authc.http.schemes` +| `xpack.security.authc.http.schemes[]` | List of HTTP authentication schemes that {kib} HTTP authentication should support. By default, this setting is set to `['apikey']` to support HTTP authentication with <> scheme. |=== @@ -239,3 +239,133 @@ The format is a string of `[ms\|s\|m\|h\|d\|w\|M\|Y]` (e.g. '20m', '24h', ============ |=== + +[[security-encrypted-saved-objects-settings]] +==== Encrypted saved objects settings + +These settings control the encryption of saved objects with sensitive data. For more details, refer to <>. + +[IMPORTANT] +============ +In high-availability deployments, make sure you use the same encryption and decryption keys for all instances of {kib}. Although the keys can be specified in clear text in `kibana.yml`, it's recommended to store them securely in the <>. +============ + +[cols="2*<"] +|=== +| [[xpack-encryptedSavedObjects-encryptionKey]] `xpack.encryptedSavedObjects.` +`encryptionKey` +| An arbitrary string of at least 32 characters that is used to encrypt sensitive properties of saved objects before they're stored in {es}. If not set, {kib} will generate a random key on startup, but certain features won't be available until you set the encryption key explicitly. + +| [[xpack-encryptedSavedObjects-keyRotation-decryptionOnlyKeys]] `xpack.encryptedSavedObjects.` +`keyRotation.decryptionOnlyKeys` +| An optional list of previously used encryption keys. Like <>, these must be at least 32 characters in length. {kib} doesn't use these keys for encryption, but may still require them to decrypt some existing saved objects. Use this setting if you wish to change your encryption key, but don't want to lose access to saved objects that were previously encrypted with a different key. + +|=== + +[float] +[[audit-logging-settings]] +===== Audit logging settings + +You can enable audit logging to support compliance, accountability, and security. When enabled, {kib} will capture: + +- Who performed an action +- What action was performed +- When the action occurred + +For more details and a reference of audit events, refer to <>. + +[cols="2*<"] +|=== +| `xpack.security.audit.enabled` +| Set to `true` to enable audit logging for security events. *Default:* `false` +|=== + +[float] +[[ecs-audit-logging-settings]] +===== ECS audit logging settings + +To enable the <>, specify where you want to write the audit events using `xpack.security.audit.appender`. + +[cols="2*<"] +|=== +| `xpack.security.audit.appender` +| Optional. Specifies where audit logs should be written to and how they should be formatted. + +2+a| For example: + +[source,yaml] +---------------------------------------- +xpack.security.audit.appender: + kind: file + path: /path/to/audit.log + layout: + kind: json +---------------------------------------- + +| `xpack.security.audit.appender.kind` +| Required. Specifies where audit logs should be written to. Allowed values are `console` or `file`. +|=== + +[float] +[[audit-logging-file-appender]] +===== File appender + +The file appender can be configured using the following settings: + +[cols="2*<"] +|=== +| `xpack.security.audit.appender.path` +| Required. Full file path the log file should be written to. + +| `xpack.security.audit.appender.layout.kind` +| Required. Specifies how audit logs should be formatted. Allowed values are `json` or `pattern`. +|=== + +[float] +[[audit-logging-pattern-layout]] +===== Pattern layout + +The pattern layout can be configured using the following settings: + +[cols="2*<"] +|=== +| `xpack.security.audit.appender.layout.highlight` +| Optional. Set to `true` to enable highlighting log messages with colors. + +| `xpack.security.audit.appender.layout.pattern` +| Optional. Specifies how the log line should be formatted. *Default:* `[%date][%level][%logger]%meta %message` +|=== + +[float] +[[audit-logging-ignore-filters]] +===== Ignore filters + +[cols="2*<"] +|=== +| `xpack.security.audit.ignore_filters[]` +| List of filters that determine which events should be excluded from the audit log. An event will get filtered out if at least one of the provided filters matches. + +2+a| For example: + +[source,yaml] +---------------------------------------- +xpack.security.audit.ignore_filters: +- actions: [http_request] <1> +- categories: [database] + types: [creation, change, deletion] <2> +---------------------------------------- +<1> Filters out HTTP request events +<2> Filters out any data write events + +| `xpack.security.audit.ignore_filters[].actions[]` +| List of values matched against the `event.action` field of an audit event. Refer to <> for a list of available events. + +| `xpack.security.audit.ignore_filters[].categories[]` +| List of values matched against the `event.category` field of an audit event. Refer to https://www.elastic.co/guide/en/ecs/1.5/ecs-allowed-values-event-category.html[ECS categorization field] for allowed values. + +| `xpack.security.audit.ignore_filters[].types[]` +| List of values matched against the `event.type` field of an audit event. Refer to https://www.elastic.co/guide/en/ecs/1.5/ecs-allowed-values-event-type.html[ECS type field] for allowed values. + +| `xpack.security.audit.ignore_filters[].outcomes[]` +| List of values matched against the `event.outcome` field of an audit event. Refer to https://www.elastic.co/guide/en/ecs/1.5/ecs-allowed-values-event-outcome.html[ECS outcome field] for allowed values. +|=== diff --git a/docs/setup/connect-to-elasticsearch.asciidoc b/docs/setup/connect-to-elasticsearch.asciidoc index 0daa3f1e0e55e..3db562319641c 100644 --- a/docs/setup/connect-to-elasticsearch.asciidoc +++ b/docs/setup/connect-to-elasticsearch.asciidoc @@ -20,10 +20,10 @@ to see all that you can do in {kib}. experimental[] -To visualize data in a CSV, JSON, or log file, you can upload it using the File -Data Visualizer. On the home page, click *Import a CSV, NDSON, or log file*, and -then drag your file into the File Data Visualizer. Alternatively, you can open -it by navigating to *Machine Learning* from the side navigation and selecting +To visualize data in a CSV, JSON, or log file, you can upload it using the File +Data Visualizer. On the home page, click *Import a CSV, NDSON, or log file*, and +then drag your file into the File Data Visualizer. Alternatively, you can open +it by navigating to *Machine Learning* from the side navigation and selecting *Data Visualizer*. [role="screenshot"] @@ -45,16 +45,12 @@ repeated production process, but rather for the initial exploration of your data [[upload-geoipdata-kibana]] === Upload geospatial data -To visualize geospatial data in a point or shape file, you can upload it using the <> +To visualize geospatial data in a point or shape file, you can upload it using the <> feature in Maps, and then use that data as a layer in a map. The data is also available for use in the broader Kibana ecosystem, for example, in visualizations and Canvas workpads. With GeoJSON Upload, you can upload a file up to 50 MB. -[role="screenshot"] -image::images/fu_gs_select_source_file_upload.png[] - - [float] [[add-data-tutorial-kibana]] === Index metrics, log, security, and application data diff --git a/docs/user/dashboard/dashboard-drilldown.asciidoc b/docs/user/dashboard/dashboard-drilldown.asciidoc index 5e928fd731bb4..bdff7355d7467 100644 --- a/docs/user/dashboard/dashboard-drilldown.asciidoc +++ b/docs/user/dashboard/dashboard-drilldown.asciidoc @@ -57,8 +57,8 @@ TIP: If you don’t see data for a panel, try changing the time range. . Set a search and filter. + [%hardbreaks] -Search: `extension.keyword:( “gz” or “css” or “deb”)` -Filter: `geo.src : CN` +Search: `extension.keyword: ("gz" or "css" or "deb")` +Filter: `geo.src: CN` *Create the drilldown* @@ -94,4 +94,3 @@ image::images/drilldown_on_panel.png[Drilldown on pie chart that navigates to an + You are navigated to your destination dashboard. Verify that the search query, filters, and time range are carried over. - diff --git a/docs/user/dashboard/url-drilldown.asciidoc b/docs/user/dashboard/url-drilldown.asciidoc index b71dfb016c765..cdb17e9daa5e3 100644 --- a/docs/user/dashboard/url-drilldown.asciidoc +++ b/docs/user/dashboard/url-drilldown.asciidoc @@ -135,6 +135,92 @@ Example: `{{ date event.from “YYYY MM DD”}}` + `{{date “now-15”}}` + +|formatNumber +a|Format numbers. Numbers can be formatted to look like currency, percentages, times or numbers with decimal places, thousands, and abbreviations. +Refer to the http://numeraljs.com/#format[numeral.js] for different formatting options. + +Example: + +`{{formatNumber event.value "0.0"}}` + +|lowercase +a|Converts a string to lower case. + +Example: + +`{{lowercase event.value}}` + +|uppercase +a|Converts a string to upper case. + +Example: + +`{{uppercase event.value}}` + +|trim +a|Removes leading and trailing spaces from a string. + +Example: + +`{{trim event.value}}` + +|trimLeft +a|Removes leading spaces from a string. + +Example: + +`{{trimLeft event.value}}` + +|trimRight +a|Removes trailing spaces from a string. + +Example: + +`{{trimRight event.value}}` + +|mid +a|Extracts a substring from a string by start position and number of characters to extract. + +Example: + +`{{mid event.value 3 5}}` - extracts five characters starting from a third character. + +|left +a|Extracts a number of characters from a string (starting from left). + +Example: + +`{{left event.value 3}}` + +|right +a|Extracts a number of characters from a string (starting from right). + +Example: + +`{{right event.value 3}}` + +|concat +a|Concatenates two or more strings. + +Example: + +`{{concat event.value "," event.key}}` + +|replace +a|Replaces all substrings within a string. + +Example: + +`{{replace event.value "stringToReplace" "stringToReplaceWith"}}` + +|split +a|Splits a string using a provided splitter. + +Example: + +`{{split event.value ","}}` + |=== diff --git a/docs/user/dashboard/vega-reference.asciidoc b/docs/user/dashboard/vega-reference.asciidoc index 378f7a53a6650..d6593143e4f6d 100644 --- a/docs/user/dashboard/vega-reference.asciidoc +++ b/docs/user/dashboard/vega-reference.asciidoc @@ -296,7 +296,8 @@ a configuration option for changing the tooltip position and padding: kibana: { tooltips: { position: 'top', - padding: 15 + padding: 15, + textTruncate: true, } } } diff --git a/docs/user/reporting/configuring-reporting.asciidoc b/docs/user/reporting/configuring-reporting.asciidoc index 6a0c44cf4c2a4..a8b76f36b9a84 100644 --- a/docs/user/reporting/configuring-reporting.asciidoc +++ b/docs/user/reporting/configuring-reporting.asciidoc @@ -75,3 +75,4 @@ to point to a proxy host requires that the Kibana server has network access to the proxy. include::{kib-repo-dir}/user/security/reporting.asciidoc[] +include::network-policy.asciidoc[] diff --git a/docs/user/reporting/network-policy.asciidoc b/docs/user/reporting/network-policy.asciidoc new file mode 100644 index 0000000000000..782473a3b0f18 --- /dev/null +++ b/docs/user/reporting/network-policy.asciidoc @@ -0,0 +1,71 @@ +[role="xpack"] +[[reporting-network-policy]] +=== Restrict requests with a Reporting network policy + +When Reporting generates PDF reports, it uses the Chromium browser to fully load the {kib} page on the server. This +potentially involves sending requests to external hosts. For example, a request might go to an external image server to show a +field formatted as an image, or to show an image in a Markdown visualization. + +If the Chromium browser is asked to send a request that violates the network policy, Reporting stops processing the page +before the request goes out, and the report is marked as a failure. Additional information about the event is in +the Kibana server logs. + +[NOTE] +============ +{kib} installations are not designed to be publicly accessible over the Internet. The Reporting network policy and other capabilities +of the Elastic Stack security features do not change this condition. +============ + +==== Configure a Reporting network policy + +You configure the network policy by specifying the `xpack.reporting.capture.networkPolicy.rules` setting in `kibana.yml`. A policy is specified as +an array of objects that describe what to allow or deny based on a host or protocol. If a host or protocol +is not specified, the rule matches any host or protocol. + +The rule objects are evaluated sequentially from the beginning to the end of the array, and continue until there is a matching rule. +If no rules allow a request, the request is denied. + +[source,yaml] +------------------------------------------------------- +# Only allow requests to placeholder.com +xpack.reporting.capture.networkPolicy: + rules: [ { allow: true, host: "placeholder.com" } ] +------------------------------------------------------- + +[source,yaml] +------------------------------------------------------- +# Only allow requests to https://placeholder.com +xpack.reporting.capture.networkPolicy: + rules: [ { allow: true, host: "placeholder.com", protocol: "https:" } ] +------------------------------------------------------- + +A final `allow` rule with no host or protocol will allow all requests that are not explicitly denied. + +[source,yaml] +------------------------------------------------------- +# Denies requests from http://placeholder.com, but anything else is allowed. +xpack.reporting.capture.networkPolicy: + rules: [{ allow: false, host: "placeholder.com", protocol: "http:" }, { allow: true }]; +------------------------------------------------------- + +A network policy can be composed of multiple rules. + +[source,yaml] +------------------------------------------------------- +# Allow any request to http://placeholder.com but for any other host, https is required +xpack.reporting.capture.networkPolicy + rules: [ + { allow: true, host: "placeholder.com", protocol: "http:" }, + { allow: true, protocol: "https:" }, + ] +------------------------------------------------------- + +[NOTE] +============ +The `file:` protocol is always denied, even if no network policy is configured. +============ + +==== Disable a Reporting network policy + +You can use the `xpack.reporting.capture.networkPolicy.enabled: false` setting to disable the network policy feature. The default for +this configuration property is `true`, so it is not necessary to explicitly enable it. diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index a7359af38c1cb..d4370c4d840c0 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -3,30 +3,30 @@ === Audit logs You can enable auditing to keep track of security-related events such as -authorization success and failures. Logging these events enables you -to monitor {kib} for suspicious activity and provides evidence in the -event of an attack. +authorization success and failures. Logging these events enables you to monitor +{kib} for suspicious activity and provides evidence in the event of an attack. -Use the {kib} audit logs in conjunction with {es}'s -audit logging to get a holistic view of all security related events. -{kib} defers to {es}'s security model for authentication, data -index authorization, and features that are driven by cluster-wide privileges. -For more information on enabling audit logging in {es}, see -{ref}/auditing.html[Auditing security events]. +Use the {kib} audit logs in conjunction with {ref}/enable-audit-logging.html[{es} audit logging] to get a +holistic view of all security related events. {kib} defers to the {es} security +model for authentication, data index authorization, and features that are driven +by cluster-wide privileges. For more information on enabling audit logging in +{es}, refer to {ref}/auditing.html[Auditing security events]. [IMPORTANT] ============================================================================ -Audit logs are **disabled** by default. To enable this functionality, you -must set `xpack.security.audit.enabled` to `true` in `kibana.yml`. +Audit logs are **disabled** by default. To enable this functionality, you must +set `xpack.security.audit.enabled` to `true` in `kibana.yml`. ============================================================================ -Audit logging uses the standard {kib} logging output, which can be configured -in the `kibana.yml` and is discussed in <>. +The current version of the audit logger uses the standard {kib} logging output, +which can be configured in `kibana.yml`. For more information, refer to <>. +The audit logger uses a separate logger and can be configured using +the options in <>. ==== Audit event types -When you are auditing security events, each request can generate -multiple audit events. The following is a list of the events that can be generated: +When you are auditing security events, each request can generate multiple audit +events. The following is a list of the events that can be generated: |====== | `saved_objects_authorization_success` | Logged when a user is authorized to access a saved @@ -34,3 +34,110 @@ multiple audit events. The following is a list of the events that can be generat | `saved_objects_authorization_failure` | Logged when a user isn't authorized to access a saved objects when using a role with <> |====== + +[[xpack-security-ecs-audit-logging]] +==== ECS audit events + +[IMPORTANT] +============================================================================ +The following events are only logged if the ECS audit logger is enabled. +For information on how to configure `xpack.security.audit.appender`, refer to +<>. +============================================================================ + +Refer to the table of events that can be logged for auditing purposes. + +Each event is broken down into `category`, `type`, `action` and `outcome` fields +to make it easy to filter, query and aggregate the resulting logs. + +[NOTE] +============================================================================ +To ensure that a record of every operation is persisted even in case of an +unexpected error, asynchronous write operations are logged immediately after all +authorization checks have passed, but before the response from {es} is received. +Refer to the corresponding {es} logs for potential write errors. +============================================================================ + + +[cols="3*<"] +|====== +3+a| +===== Category: authentication + +| *Action* +| *Outcome* +| *Description* + +.2+| `user_login` +| `success` | User has logged in successfully. +| `failure` | Failed login attempt (e.g. due to invalid credentials). + +3+a| +===== Category: database +====== Type: creation + +| *Action* +| *Outcome* +| *Description* + +.2+| `saved_object_create` +| `unknown` | User is creating a saved object. +| `failure` | User is not authorized to create a saved object. + + +3+a| +====== Type: change + +| *Action* +| *Outcome* +| *Description* + +.2+| `saved_object_update` +| `unknown` | User is updating a saved object. +| `failure` | User is not authorized to update a saved object. + +.2+| `saved_object_add_to_spaces` +| `unknown` | User is adding a saved object to other spaces. +| `failure` | User is not authorized to add a saved object to other spaces. + +.2+| `saved_object_delete_from_spaces` +| `unknown` | User is removing a saved object from other spaces. +| `failure` | User is not authorized to remove a saved object from other spaces. + +3+a| +====== Type: deletion + +| *Action* +| *Outcome* +| *Description* + +.2+| `saved_object_delete` +| `unknown` | User is deleting a saved object. +| `failure` | User is not authorized to delete a saved object. + +3+a| +====== Type: access + +| *Action* +| *Outcome* +| *Description* + +.2+| `saved_object_get` +| `success` | User has accessed a saved object. +| `failure` | User is not authorized to access a saved object. + +.2+| `saved_object_find` +| `success` | User has accessed a saved object as part of a search operation. +| `failure` | User is not authorized to search for saved objects. + + +3+a| +===== Category: web + +| *Action* +| *Outcome* +| *Description* + +| `http_request` +| `unknown` | User is making an HTTP request. +|====== diff --git a/docs/user/security/secure-saved-objects.asciidoc b/docs/user/security/secure-saved-objects.asciidoc new file mode 100644 index 0000000000000..3b15a576500f1 --- /dev/null +++ b/docs/user/security/secure-saved-objects.asciidoc @@ -0,0 +1,47 @@ +[role="xpack"] +[[xpack-security-secure-saved-objects]] +=== Secure saved objects + +{kib} stores entities such as dashboards, visualizations, alerts, actions, and advanced settings as saved objects, which are kept in a dedicated, internal {es} index. If such an object includes sensitive information, for example a PagerDuty integration key or email server credentials used by the alert action, {kib} encrypts it and makes sure it cannot be accidentally leaked or tampered with. + +Encrypting sensitive information means that a malicious party with access to the {kib} internal indices won't be able to extract that information without also knowing the encryption key. + +Example `kibana.yml`: + +[source,yaml] +-------------------------------------------------------------------------------- +xpack.encryptedSavedObjects: + encryptionKey: "min-32-byte-long-strong-encryption-key" +-------------------------------------------------------------------------------- + +[IMPORTANT] +============================================================================ +If you don't specify an encryption key, {kib} automatically generates a random key at startup. Every time you restart {kib}, it uses a new ephemeral encryption key and is unable to decrypt saved objects encrypted with another key. To prevent data loss, {kib} might disable features that rely on this encryption until you explicitly set an encryption key. +============================================================================ + +[[encryption-key-rotation]] +==== Encryption key rotation + +Many policies and best practices stipulate that encryption keys should be periodically rotated to decrease the amount of content encrypted with one key and therefore limit the potential damage if the key is compromised. {kib} allows you to rotate encryption keys whenever there is a need. + +When you change an encryption key, be sure to keep the old one for some time. Although {kib} only uses a new encryption key to encrypt all new and updated data, it still may need the old one to decrypt data that was encrypted using the old key. It's possible to have multiple old keys used only for decryption. {kib} doesn't automatically re-encrypt existing saved objects with the new encryption key. Re-encryption only happens when you update existing object or use the <>. + +Here is how your `kibana.yml` might look if you use key rotation functionality: + +[source,yaml] +-------------------------------------------------------------------------------- +xpack.encryptedSavedObjects: + encryptionKey: "min-32-byte-long-NEW-encryption-key" <1> + keyRotation: + decryptionOnlyKeys: ["min-32-byte-long-OLD#1-encryption-key", "min-32-byte-long-OLD#2-encryption-key"] <2> +-------------------------------------------------------------------------------- + +<1> The encryption key {kib} will use to encrypt all new or updated saved objects. This is known as the primary encryption key. +<2> A list of encryption keys {kib} will try to use to decrypt existing saved objects if decryption with the primary encryption key isn't possible. These keys are known as the decryption-only or secondary encryption keys. + +[NOTE] +============================================================================ +You might also leverage this functionality if multiple {kib} instances connected to the same {es} cluster use different encryption keys. In this case, you might have a mix of saved objects encrypted with different keys, and every {kib} instance can only deal with a specific subset of objects. To fix this, you must choose a single primary encryption key for `xpack.encryptedSavedObjects.encryptionKey`, move all other encryption keys to `xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys`, and sync this configuration across all {kib} instances. +============================================================================ + +At some point, you might want to dispose of old encryption keys completely. Make sure there are no saved objects that {kib} encrypted with these encryption keys. You can use the <> to determine which existing saved objects require decryption-only keys and re-encrypt them with the primary key. diff --git a/docs/user/security/securing-kibana.asciidoc b/docs/user/security/securing-kibana.asciidoc index 0f02279eaf1f3..e7bd297a3ebb5 100644 --- a/docs/user/security/securing-kibana.asciidoc +++ b/docs/user/security/securing-kibana.asciidoc @@ -129,3 +129,4 @@ include::securing-communications/elasticsearch-mutual-tls.asciidoc[] include::audit-logging.asciidoc[] include::access-agreement.asciidoc[] include::session-management.asciidoc[] +include::secure-saved-objects.asciidoc[] diff --git a/examples/ui_actions_explorer/public/context_menu_examples/panel_edit_with_drilldowns_and_context_actions.tsx b/examples/ui_actions_explorer/public/context_menu_examples/panel_edit_with_drilldowns_and_context_actions.tsx index e9543814ff015..5ef2cb73b5937 100644 --- a/examples/ui_actions_explorer/public/context_menu_examples/panel_edit_with_drilldowns_and_context_actions.tsx +++ b/examples/ui_actions_explorer/public/context_menu_examples/panel_edit_with_drilldowns_and_context_actions.tsx @@ -39,7 +39,7 @@ export const PanelEditWithDrilldownsAndContextActions: React.FC = () => { const customActionGrouping: Action['grouping'] = [ { id: 'actions', - getDisplayName: () => 'Custom actions', + getDisplayName: () => 'API actions', getIconType: () => 'cloudStormy', order: 20, }, diff --git a/package.json b/package.json index 732ee1fd3038b..b965f50cd468c 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "dependencies": { "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "7.9.1", - "@elastic/eui": "29.3.0", + "@elastic/eui": "29.5.0", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "^2.5.0", "@elastic/request-crypto": "1.1.4", @@ -131,10 +131,7 @@ "@kbn/i18n": "1.0.0", "@kbn/interpreter": "1.0.0", "@kbn/logging": "1.0.0", - "@kbn/pm": "1.0.0", "@kbn/std": "1.0.0", - "@kbn/telemetry-tools": "1.0.0", - "@kbn/test-subj-selector": "0.2.1", "@kbn/ui-framework": "1.0.0", "@kbn/ace": "1.0.0", "@kbn/monaco": "1.0.0", @@ -247,12 +244,22 @@ "@kbn/expect": "1.0.0", "@kbn/optimizer": "1.0.0", "@kbn/plugin-generator": "1.0.0", + "@kbn/pm": "1.0.0", "@kbn/release-notes": "1.0.0", + "@kbn/telemetry-tools": "1.0.0", "@kbn/test": "1.0.0", + "@kbn/test-subj-selector": "0.2.1", "@kbn/utility-types": "1.0.0", "@microsoft/api-documenter": "7.7.2", "@microsoft/api-extractor": "7.7.0", "@percy/agent": "^0.26.0", + "@storybook/addon-a11y": "^6.0.26", + "@storybook/addon-actions": "^6.0.26", + "@storybook/addon-essentials": "^6.0.26", + "@storybook/addon-knobs": "^6.0.26", + "@storybook/addon-storyshots": "^6.0.26", + "@storybook/react": "^6.0.26", + "@storybook/theming": "^6.0.26", "@testing-library/dom": "^7.24.2", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.0.4", @@ -301,6 +308,7 @@ "@types/json5": "^0.0.30", "@types/license-checker": "15.0.0", "@types/listr": "^0.14.0", + "@types/loader-utils": "^1.1.3", "@types/lodash": "^4.14.159", "@types/lru-cache": "^5.1.0", "@types/markdown-it": "^0.0.7", @@ -346,6 +354,7 @@ "@types/vinyl-fs": "^2.4.11", "@types/webpack": "^4.41.3", "@types/webpack-env": "^1.15.2", + "@types/webpack-merge": "^4.1.5", "@types/zen-observable": "^0.8.0", "@typescript-eslint/eslint-plugin": "^3.10.0", "@typescript-eslint/parser": "^3.10.0", diff --git a/packages/elastic-eslint-config-kibana/package.json b/packages/elastic-eslint-config-kibana/package.json index 3f2c6e9edb261..9d0d579086543 100644 --- a/packages/elastic-eslint-config-kibana/package.json +++ b/packages/elastic-eslint-config-kibana/package.json @@ -7,6 +7,9 @@ "type": "git", "url": "git+https://github.com/elastic/kibana.git" }, + "kibana": { + "devOnly": true + }, "keywords": [], "author": "Spencer Alger ", "license": "Apache-2.0", diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index 79d2fd8687dae..2fab970c5c71f 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "private": true, "license": "Apache-2.0", + "kibana": { + "devOnly": true + }, "dependencies": { "@babel/plugin-proposal-class-properties": "^7.10.4", "@babel/plugin-proposal-export-namespace-from": "^7.10.4", diff --git a/packages/kbn-config/package.json b/packages/kbn-config/package.json index 6d2d56b929ead..f994836af8847 100644 --- a/packages/kbn-config/package.json +++ b/packages/kbn-config/package.json @@ -12,10 +12,8 @@ "dependencies": { "@elastic/safer-lodash-set": "0.0.0", "@kbn/config-schema": "1.0.0", - "@kbn/dev-utils": "1.0.0", "@kbn/logging": "1.0.0", "@kbn/std": "1.0.0", - "@kbn/utility-types": "1.0.0", "js-yaml": "^3.14.0", "load-json-file": "^6.2.0", "lodash": "^4.17.20", @@ -24,6 +22,8 @@ "type-detect": "^4.0.8" }, "devDependencies": { + "@kbn/dev-utils": "1.0.0", + "@kbn/utility-types": "1.0.0", "typescript": "4.0.2", "tsd": "^0.13.1" } diff --git a/packages/kbn-config/src/env.ts b/packages/kbn-config/src/env.ts index e4585056696f9..e7b4658262235 100644 --- a/packages/kbn-config/src/env.ts +++ b/packages/kbn-config/src/env.ts @@ -19,6 +19,7 @@ import { resolve, join } from 'path'; import loadJsonFile from 'load-json-file'; +import { getPluginSearchPaths } from './plugins'; import { PackageInfo, EnvironmentMode } from './types'; /** @internal */ @@ -114,21 +115,11 @@ export class Env { this.binDir = resolve(this.homeDir, 'bin'); this.logDir = resolve(this.homeDir, 'log'); - /** - * BEWARE: this needs to stay roughly synchronized with the @kbn/optimizer - * `packages/kbn-optimizer/src/optimizer_config.ts` determines the paths - * that should be searched for plugins to build - */ - this.pluginSearchPaths = [ - resolve(this.homeDir, 'src', 'plugins'), - ...(options.cliArgs.oss ? [] : [resolve(this.homeDir, 'x-pack', 'plugins')]), - resolve(this.homeDir, 'plugins'), - ...(options.cliArgs.runExamples ? [resolve(this.homeDir, 'examples')] : []), - ...(options.cliArgs.runExamples && !options.cliArgs.oss - ? [resolve(this.homeDir, 'x-pack', 'examples')] - : []), - resolve(this.homeDir, '..', 'kibana-extra'), - ]; + this.pluginSearchPaths = getPluginSearchPaths({ + rootDir: this.homeDir, + oss: options.cliArgs.oss, + examples: options.cliArgs.runExamples, + }); this.cliArgs = Object.freeze(options.cliArgs); this.configs = Object.freeze(options.configs); diff --git a/packages/kbn-config/src/index.ts b/packages/kbn-config/src/index.ts index f02514a92e606..68609c6d5c7c3 100644 --- a/packages/kbn-config/src/index.ts +++ b/packages/kbn-config/src/index.ts @@ -35,3 +35,4 @@ export { ObjectToConfigAdapter } from './object_to_config_adapter'; export { CliArgs, Env, RawPackageInfo } from './env'; export { EnvironmentMode, PackageInfo } from './types'; export { LegacyObjectToConfigAdapter, LegacyLoggingConfig } from './legacy'; +export { getPluginSearchPaths } from './plugins'; diff --git a/packages/kbn-config/src/plugins/index.ts b/packages/kbn-config/src/plugins/index.ts new file mode 100644 index 0000000000000..7d02f9fb984c2 --- /dev/null +++ b/packages/kbn-config/src/plugins/index.ts @@ -0,0 +1,19 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +export { getPluginSearchPaths } from './plugin_search_paths'; diff --git a/packages/kbn-config/src/plugins/plugin_search_paths.ts b/packages/kbn-config/src/plugins/plugin_search_paths.ts new file mode 100644 index 0000000000000..a7d151c3275c8 --- /dev/null +++ b/packages/kbn-config/src/plugins/plugin_search_paths.ts @@ -0,0 +1,36 @@ +/* + * 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'; + +interface SearchOptions { + rootDir: string; + oss: boolean; + examples: boolean; +} + +export function getPluginSearchPaths({ rootDir, oss, examples }: SearchOptions) { + return [ + resolve(rootDir, 'src', 'plugins'), + ...(oss ? [] : [resolve(rootDir, 'x-pack', 'plugins')]), + resolve(rootDir, 'plugins'), + ...(examples ? [resolve(rootDir, 'examples')] : []), + ...(examples && !oss ? [resolve(rootDir, 'x-pack', 'examples')] : []), + resolve(rootDir, '..', 'kibana-extra'), + ]; +} diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index a51734168cf76..7fd9a9e7d67e1 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -9,6 +9,9 @@ "kbn:bootstrap": "yarn build", "kbn:watch": "yarn build --watch" }, + "kibana": { + "devOnly": true + }, "dependencies": { "@babel/core": "^7.11.6", "@kbn/utils": "1.0.0", diff --git a/packages/kbn-dev-utils/src/index.ts b/packages/kbn-dev-utils/src/index.ts index 6a845825f0fd4..98385b49dafa9 100644 --- a/packages/kbn-dev-utils/src/index.ts +++ b/packages/kbn-dev-utils/src/index.ts @@ -40,7 +40,6 @@ export * from './axios'; export * from './stdio'; export * from './ci_stats_reporter'; export * from './plugin_list'; -export * from './simple_kibana_platform_plugin_discovery'; +export * from './plugins'; export * from './streams'; export * from './babel'; -export * from './parse_kibana_platform_plugin'; diff --git a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts index e8f6735205b19..9782067e61343 100644 --- a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts +++ b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts @@ -24,7 +24,7 @@ import MarkdownIt from 'markdown-it'; import cheerio from 'cheerio'; import { REPO_ROOT } from '@kbn/utils'; -import { simpleKibanaPlatformPluginDiscovery } from '../simple_kibana_platform_plugin_discovery'; +import { simpleKibanaPlatformPluginDiscovery } from '../plugins'; import { extractAsciidocInfo } from './extract_asciidoc_info'; export interface Plugin { diff --git a/src/core/server/audit_trail/index.ts b/packages/kbn-dev-utils/src/plugins/index.ts similarity index 83% rename from src/core/server/audit_trail/index.ts rename to packages/kbn-dev-utils/src/plugins/index.ts index 3f01e6fa3582d..8705682f355c7 100644 --- a/src/core/server/audit_trail/index.ts +++ b/packages/kbn-dev-utils/src/plugins/index.ts @@ -17,5 +17,5 @@ * under the License. */ -export { AuditTrailService } from './audit_trail_service'; -export { AuditableEvent, Auditor, AuditorFactory, AuditTrailSetup, AuditTrailStart } from './types'; +export * from './parse_kibana_platform_plugin'; +export * from './simple_kibana_platform_plugin_discovery'; diff --git a/packages/kbn-dev-utils/src/parse_kibana_platform_plugin.ts b/packages/kbn-dev-utils/src/plugins/parse_kibana_platform_plugin.ts similarity index 56% rename from packages/kbn-dev-utils/src/parse_kibana_platform_plugin.ts rename to packages/kbn-dev-utils/src/plugins/parse_kibana_platform_plugin.ts index 83d8c2684d7ca..16aaecb3e478d 100644 --- a/packages/kbn-dev-utils/src/parse_kibana_platform_plugin.ts +++ b/packages/kbn-dev-utils/src/plugins/parse_kibana_platform_plugin.ts @@ -23,12 +23,27 @@ import loadJsonFile from 'load-json-file'; export interface KibanaPlatformPlugin { readonly directory: string; readonly manifestPath: string; - readonly manifest: { - id: string; - ui: boolean; - server: boolean; - [key: string]: unknown; - }; + readonly manifest: Manifest; +} + +function isValidDepsDeclaration(input: unknown, type: string): string[] { + if (typeof input === 'undefined') return []; + if (Array.isArray(input) && input.every((i) => typeof i === 'string')) { + return input; + } + throw new TypeError(`The "${type}" in plugin manifest should be an array of strings.`); +} + +interface Manifest { + id: string; + ui: boolean; + server: boolean; + kibanaVersion: string; + version: string; + requiredPlugins: readonly string[]; + optionalPlugins: readonly string[]; + requiredBundles: readonly string[]; + extraPublicDirs: readonly string[]; } export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformPlugin { @@ -36,7 +51,7 @@ export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformP throw new TypeError('expected new platform manifest path to be absolute'); } - const manifest = loadJsonFile.sync(manifestPath); + const manifest: Partial = loadJsonFile.sync(manifestPath); if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) { throw new TypeError('expected new platform plugin manifest to be a JSON encoded object'); } @@ -45,6 +60,10 @@ export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformP throw new TypeError('expected new platform plugin manifest to have a string id'); } + if (typeof manifest.version !== 'string') { + throw new TypeError('expected new platform plugin manifest to have a string version'); + } + return { directory: Path.dirname(manifestPath), manifestPath, @@ -54,6 +73,12 @@ export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformP ui: !!manifest.ui, server: !!manifest.server, id: manifest.id, + version: manifest.version, + kibanaVersion: manifest.kibanaVersion || manifest.version, + requiredPlugins: isValidDepsDeclaration(manifest.requiredPlugins, 'requiredPlugins'), + optionalPlugins: isValidDepsDeclaration(manifest.optionalPlugins, 'optionalPlugins'), + requiredBundles: isValidDepsDeclaration(manifest.requiredBundles, 'requiredBundles'), + extraPublicDirs: isValidDepsDeclaration(manifest.extraPublicDirs, 'extraPublicDirs'), }, }; } diff --git a/packages/kbn-dev-utils/src/simple_kibana_platform_plugin_discovery.ts b/packages/kbn-dev-utils/src/plugins/simple_kibana_platform_plugin_discovery.ts similarity index 100% rename from packages/kbn-dev-utils/src/simple_kibana_platform_plugin_discovery.ts rename to packages/kbn-dev-utils/src/plugins/simple_kibana_platform_plugin_discovery.ts diff --git a/packages/kbn-es-archiver/package.json b/packages/kbn-es-archiver/package.json index 81c1747bb2727..645abd6195909 100644 --- a/packages/kbn-es-archiver/package.json +++ b/packages/kbn-es-archiver/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "license": "Apache-2.0", "main": "target/index.js", + "kibana": { + "devOnly": true + }, "scripts": { "kbn:bootstrap": "rm -rf target && tsc", "kbn:watch": "rm -rf target && tsc --watch" diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index c3733094350be..6ed3ae2eb2fb7 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -4,6 +4,9 @@ "version": "1.0.0", "license": "Apache-2.0", "private": true, + "kibana": { + "devOnly": true + }, "scripts": { "kbn:bootstrap": "node scripts/build", "kbn:watch": "node scripts/build --watch" diff --git a/packages/kbn-eslint-import-resolver-kibana/package.json b/packages/kbn-eslint-import-resolver-kibana/package.json index 223c73e97908e..ffbd94810a405 100755 --- a/packages/kbn-eslint-import-resolver-kibana/package.json +++ b/packages/kbn-eslint-import-resolver-kibana/package.json @@ -5,6 +5,9 @@ "version": "2.0.0", "main": "import_resolver_kibana.js", "license": "Apache-2.0", + "kibana": { + "devOnly": true + }, "repository": { "type": "git", "url": "https://github.com/elastic/kibana/tree/master/packages/kbn-eslint-import-resolver-kibana" diff --git a/packages/kbn-eslint-plugin-eslint/package.json b/packages/kbn-eslint-plugin-eslint/package.json index 026938213ac83..72b8577cb0945 100644 --- a/packages/kbn-eslint-plugin-eslint/package.json +++ b/packages/kbn-eslint-plugin-eslint/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "private": true, "license": "Apache-2.0", + "kibana": { + "devOnly": true + }, "peerDependencies": { "eslint": "6.8.0", "babel-eslint": "^10.0.3" diff --git a/packages/kbn-expect/package.json b/packages/kbn-expect/package.json index 0975f5762fa1c..8ca37c7c88673 100644 --- a/packages/kbn-expect/package.json +++ b/packages/kbn-expect/package.json @@ -3,5 +3,8 @@ "main": "./expect.js", "version": "1.0.0", "license": "MIT", - "private": true + "private": true, + "kibana": { + "devOnly": true + } } diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index b075a678bff38..fd0be15affab3 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -68,7 +68,7 @@ pageLoadAssetSize: searchprofiler: 67080 security: 189428 securityOss: 30806 - securitySolution: 622387 + securitySolution: 283440 share: 99061 snapshotRestore: 79032 spaces: 387915 diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index c9e414dbc5177..63146fc7a1834 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -14,6 +14,7 @@ "@babel/core": "^7.11.6", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", + "@kbn/config": "1.0.0", "@kbn/std": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", "autoprefixer": "^9.7.4", diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json index 33f53e336598d..a5e9f34a22aa6 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json @@ -1,5 +1,6 @@ { "id": "bar", "ui": true, - "requiredBundles": ["foo"] + "requiredBundles": ["foo"], + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json index 256856181ccd8..27730df199887 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json @@ -1,4 +1,5 @@ { "id": "foo", - "ui": true + "ui": true, + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json index 6e4e9c70a115c..a8f991ee11465 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json @@ -1,3 +1,4 @@ { - "id": "baz" + "id": "baz", + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json index b9e044523a6a5..d8a8b2e548e4a 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json @@ -1,3 +1,4 @@ { - "id": "test_baz" + "id": "test_baz", + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json index 10602d2e7981a..64ec7ff5ccf3e 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json @@ -1,4 +1,5 @@ { "id": "baz", - "ui": true + "ui": true, + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/optimizer/get_output_stats.ts b/packages/kbn-optimizer/src/optimizer/get_output_stats.ts index cc4cd05f42c3f..82d1a276ccdad 100644 --- a/packages/kbn-optimizer/src/optimizer/get_output_stats.ts +++ b/packages/kbn-optimizer/src/optimizer/get_output_stats.ts @@ -109,6 +109,11 @@ export function getMetrics(log: ToolingLog, config: OptimizerConfig) { id: bundle.id, value: sumSize(asyncChunks), }, + { + group: `async chunk count`, + id: bundle.id, + value: asyncChunks.length, + }, { group: `miscellaneous assets size`, id: bundle.id, diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index 8091f6aa90508..1443fccda04d8 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -19,6 +19,7 @@ import Path from 'path'; import Os from 'os'; +import { getPluginSearchPaths } from '@kbn/config'; import { Bundle, @@ -167,19 +168,14 @@ export class OptimizerConfig { throw new TypeError('outputRoot must be an absolute path'); } - /** - * BEWARE: this needs to stay roughly synchronized with - * `src/core/server/config/env.ts` which determines which paths - * should be searched for plugins to load - */ - const pluginScanDirs = options.pluginScanDirs || [ - Path.resolve(repoRoot, 'src/plugins'), - ...(oss ? [] : [Path.resolve(repoRoot, 'x-pack/plugins')]), - Path.resolve(repoRoot, 'plugins'), - ...(examples ? [Path.resolve('examples')] : []), - ...(examples && !oss ? [Path.resolve('x-pack/examples')] : []), - Path.resolve(repoRoot, '../kibana-extra'), - ]; + const pluginScanDirs = + options.pluginScanDirs || + getPluginSearchPaths({ + rootDir: repoRoot, + oss, + examples, + }); + if (!pluginScanDirs.every((p) => Path.isAbsolute(p))) { throw new TypeError('pluginScanDirs must all be absolute paths'); } diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 9678dd5de868b..7987dd71f765c 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -23,7 +23,6 @@ import { stringifyRequest } from 'loader-utils'; import webpack from 'webpack'; // @ts-expect-error import TerserPlugin from 'terser-webpack-plugin'; -// @ts-expect-error import webpackMerge from 'webpack-merge'; import { CleanWebpackPlugin } from 'clean-webpack-plugin'; import CompressionPlugin from 'compression-webpack-plugin'; @@ -64,6 +63,14 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: optimization: { noEmitOnErrors: true, + splitChunks: { + maxAsyncRequests: 10, + cacheGroups: { + default: { + reuseExistingChunk: false, + }, + }, + }, }, externals: [UiSharedDeps.externals], diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 2e50f4214beb4..c053445285c03 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -150,7 +150,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _commands__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(128); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(500); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(501); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(144); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -8897,9 +8897,9 @@ exports.ToolingLogCollectingWriter = ToolingLogCollectingWriter; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "commands", function() { return commands; }); /* harmony import */ var _bootstrap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(129); -/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(280); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(399); -/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(400); +/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(281); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(400); +/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(401); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -8943,7 +8943,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(273); /* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(278); /* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(275); -/* harmony import */ var _utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(279); +/* harmony import */ var _utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(279); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -9002,7 +9002,7 @@ const BootstrapCommand = { const yarnLock = await Object(_utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__["readYarnLock"])(kbn); if (options.validate) { - await Object(_utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__["validateYarnLock"])(kbn, yarnLock); + await Object(_utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_7__["validateDependencies"])(kbn, yarnLock); } await Object(_utils_link_project_executables__WEBPACK_IMPORTED_MODULE_0__["linkProjectExecutables"])(projects, projectGraph); @@ -14713,6 +14713,10 @@ class Project { return this.json.kibana && this.json.kibana.clean || {}; } + isFlaggedAsDevOnly() { + return !!(this.json.kibana && this.json.kibana.devOnly); + } + hasScript(name) { return name in this.scripts; } @@ -37954,13 +37958,16 @@ class BootstrapCacheFile { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validateYarnLock", function() { return validateYarnLock; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validateDependencies", function() { return validateDependencies; }); /* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); /* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(131); -/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(144); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(113); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(131); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(144); +/* harmony import */ var _projects_tree__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(280); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -37984,7 +37991,9 @@ __webpack_require__.r(__webpack_exports__); -async function validateYarnLock(kbn, yarnLock) { + + +async function validateDependencies(kbn, yarnLock) { // look through all of the packages in the yarn.lock file to see if // we have accidentally installed multiple lodash v4 versions const lodash4Versions = new Set(); @@ -38005,8 +38014,8 @@ async function validateYarnLock(kbn, yarnLock) { delete yarnLock[req]; } - await Object(_fs__WEBPACK_IMPORTED_MODULE_2__["writeFile"])(kbn.getAbsolute('yarn.lock'), Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["stringify"])(yarnLock), 'utf8'); - _log__WEBPACK_IMPORTED_MODULE_3__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + await Object(_fs__WEBPACK_IMPORTED_MODULE_3__["writeFile"])(kbn.getAbsolute('yarn.lock'), Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["stringify"])(yarnLock), 'utf8'); + _log__WEBPACK_IMPORTED_MODULE_4__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` Multiple version of lodash v4 were detected, so they have been removed from the yarn.lock file. Please rerun yarn kbn bootstrap to coalese the @@ -38025,7 +38034,7 @@ async function validateYarnLock(kbn, yarnLock) { // of lodash v3 in the distributable - const prodDependencies = kbn.resolveAllProductionDependencies(yarnLock, _log__WEBPACK_IMPORTED_MODULE_3__["log"]); + const prodDependencies = kbn.resolveAllProductionDependencies(yarnLock, _log__WEBPACK_IMPORTED_MODULE_4__["log"]); const lodash3Versions = new Set(); for (const dep of prodDependencies.values()) { @@ -38036,7 +38045,7 @@ async function validateYarnLock(kbn, yarnLock) { if (lodash3Versions.size) { - _log__WEBPACK_IMPORTED_MODULE_3__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + _log__WEBPACK_IMPORTED_MODULE_4__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` Due to changes in the yarn.lock file and/or package.json files a version of lodash 3 is now included in the production dependencies. To reduce the size of @@ -38088,7 +38097,7 @@ async function validateYarnLock(kbn, yarnLock) { }) => ` ${range} => ${projects.map(p => p.name).join(', ')}`)], []).join('\n '); if (duplicateRanges) { - _log__WEBPACK_IMPORTED_MODULE_3__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + _log__WEBPACK_IMPORTED_MODULE_4__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` [single_version_dependencies] Multiple version ranges for the same dependency were found declared across different package.json files. Please consolidate @@ -38102,21 +38111,207 @@ async function validateYarnLock(kbn, yarnLock) { ${duplicateRanges} `); process.exit(1); + } // look for packages that have the the `kibana.devOnly` flag in their package.json + // and make sure they aren't included in the production dependencies of Kibana + + + const devOnlyProjectsInProduction = getDevOnlyProductionDepsTree(kbn, 'kibana'); + + if (devOnlyProjectsInProduction) { + _log__WEBPACK_IMPORTED_MODULE_4__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + Some of the packages in the production dependency chain for Kibana and X-Pack are + flagged with "kibana.devOnly" in their package.json. Please check changes made to + packages and their dependencies to ensure they don't end up in production. + + The devOnly dependencies that are being dependend on in production are: + + ${Object(_projects_tree__WEBPACK_IMPORTED_MODULE_5__["treeToString"])(devOnlyProjectsInProduction).split('\n').join('\n ')} + `); + process.exit(1); } - _log__WEBPACK_IMPORTED_MODULE_3__["log"].success('yarn.lock analysis completed without any issues'); + _log__WEBPACK_IMPORTED_MODULE_4__["log"].success('yarn.lock analysis completed without any issues'); +} + +function getDevOnlyProductionDepsTree(kbn, projectName) { + const project = kbn.getProject(projectName); + const childProjectNames = [...Object.keys(project.productionDependencies).filter(name => kbn.hasProject(name)), ...(projectName === 'kibana' ? ['x-pack'] : [])]; + const children = childProjectNames.map(n => getDevOnlyProductionDepsTree(kbn, n)).filter(t => !!t); + + if (!children.length && !project.isFlaggedAsDevOnly()) { + return; + } + + const tree = { + name: project.isFlaggedAsDevOnly() ? chalk__WEBPACK_IMPORTED_MODULE_2___default.a.red.bold(projectName) : projectName, + children + }; + return tree; } /***/ }), /* 280 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "renderProjectsTree", function() { return renderProjectsTree; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "treeToString", function() { return treeToString; }); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(113); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +/* + * 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 projectKey = Symbol('__project'); +function renderProjectsTree(rootPath, projects) { + const projectsTree = buildProjectsTree(rootPath, projects); + return treeToString(createTreeStructure(projectsTree)); +} +function treeToString(tree) { + return [tree.name].concat(childrenToStrings(tree.children, '')).join('\n'); +} + +function childrenToStrings(tree, treePrefix) { + if (tree === undefined) { + return []; + } + + let strings = []; + tree.forEach((node, index) => { + const isLastNode = tree.length - 1 === index; + const nodePrefix = isLastNode ? '└── ' : '├── '; + const childPrefix = isLastNode ? ' ' : '│ '; + const childrenPrefix = treePrefix + childPrefix; + strings.push(`${treePrefix}${nodePrefix}${node.name}`); + strings = strings.concat(childrenToStrings(node.children, childrenPrefix)); + }); + return strings; +} + +function createTreeStructure(tree) { + let name; + const children = []; + + for (const [dir, project] of tree.entries()) { + // This is a leaf node (aka a project) + if (typeof project === 'string') { + name = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(project); + continue; + } // If there's only one project and the key indicates it's a leaf node, we + // know that we're at a package folder that contains a package.json, so we + // "inline it" so we don't get unnecessary levels, i.e. we'll just see + // `foo` instead of `foo -> foo`. + + + if (project.size === 1 && project.has(projectKey)) { + const projectName = project.get(projectKey); + children.push({ + children: [], + name: dirOrProjectName(dir, projectName) + }); + continue; + } + + const subtree = createTreeStructure(project); // If the name is specified, we know there's a package at the "root" of the + // subtree itself. + + if (subtree.name !== undefined) { + const projectName = subtree.name; + children.push({ + children: subtree.children, + name: dirOrProjectName(dir, projectName) + }); + continue; + } // Special-case whenever we have one child, so we don't get unnecessary + // folders in the output. E.g. instead of `foo -> bar -> baz` we get + // `foo/bar/baz` instead. + + + if (subtree.children && subtree.children.length === 1) { + const child = subtree.children[0]; + const newName = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(path__WEBPACK_IMPORTED_MODULE_1___default.a.join(dir.toString(), child.name)); + children.push({ + children: child.children, + name: newName + }); + continue; + } + + children.push({ + children: subtree.children, + name: chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(dir.toString()) + }); + } + + return { + name, + children + }; +} + +function dirOrProjectName(dir, projectName) { + return dir === projectName ? chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(dir) : chalk__WEBPACK_IMPORTED_MODULE_0___default.a`{dim ${dir.toString()} ({reset.green ${projectName}})}`; +} + +function buildProjectsTree(rootPath, projects) { + const tree = new Map(); + + for (const project of projects.values()) { + if (rootPath === project.path) { + tree.set(projectKey, project.name); + } else { + const relativeProjectPath = path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(rootPath, project.path); + addProjectToTree(tree, relativeProjectPath.split(path__WEBPACK_IMPORTED_MODULE_1___default.a.sep), project); + } + } + + return tree; +} + +function addProjectToTree(tree, pathParts, project) { + if (pathParts.length === 0) { + tree.set(projectKey, project.name); + } else { + const [currentDir, ...rest] = pathParts; + + if (!tree.has(currentDir)) { + tree.set(currentDir, new Map()); + } + + const subtree = tree.get(currentDir); + addProjectToTree(subtree, rest, project); + } +} + +/***/ }), +/* 281 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CleanCommand", function() { return CleanCommand; }); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(281); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(282); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(367); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(368); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); @@ -38216,21 +38411,21 @@ const CleanCommand = { }; /***/ }), -/* 281 */ +/* 282 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(112); const path = __webpack_require__(4); -const globby = __webpack_require__(282); -const isGlob = __webpack_require__(294); -const slash = __webpack_require__(358); +const globby = __webpack_require__(283); +const isGlob = __webpack_require__(295); +const slash = __webpack_require__(359); const gracefulFs = __webpack_require__(133); -const isPathCwd = __webpack_require__(360); -const isPathInside = __webpack_require__(361); -const rimraf = __webpack_require__(362); -const pMap = __webpack_require__(363); +const isPathCwd = __webpack_require__(361); +const isPathInside = __webpack_require__(362); +const rimraf = __webpack_require__(363); +const pMap = __webpack_require__(364); const rimrafP = promisify(rimraf); @@ -38344,19 +38539,19 @@ module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options /***/ }), -/* 282 */ +/* 283 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const arrayUnion = __webpack_require__(283); -const merge2 = __webpack_require__(284); +const arrayUnion = __webpack_require__(284); +const merge2 = __webpack_require__(285); const glob = __webpack_require__(147); -const fastGlob = __webpack_require__(285); -const dirGlob = __webpack_require__(354); -const gitignore = __webpack_require__(356); -const {FilterStream, UniqueStream} = __webpack_require__(359); +const fastGlob = __webpack_require__(286); +const dirGlob = __webpack_require__(355); +const gitignore = __webpack_require__(357); +const {FilterStream, UniqueStream} = __webpack_require__(360); const DEFAULT_FILTER = () => false; @@ -38529,7 +38724,7 @@ module.exports.gitignore = gitignore; /***/ }), -/* 283 */ +/* 284 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38541,7 +38736,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 284 */ +/* 285 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38692,17 +38887,17 @@ function pauseStreams (streams, options) { /***/ }), -/* 285 */ +/* 286 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const taskManager = __webpack_require__(286); -const async_1 = __webpack_require__(315); -const stream_1 = __webpack_require__(350); -const sync_1 = __webpack_require__(351); -const settings_1 = __webpack_require__(353); -const utils = __webpack_require__(287); +const taskManager = __webpack_require__(287); +const async_1 = __webpack_require__(316); +const stream_1 = __webpack_require__(351); +const sync_1 = __webpack_require__(352); +const settings_1 = __webpack_require__(354); +const utils = __webpack_require__(288); async function FastGlob(source, options) { assertPatternsInput(source); const works = getWorks(source, async_1.default, options); @@ -38766,13 +38961,13 @@ module.exports = FastGlob; /***/ }), -/* 286 */ +/* 287 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); +const utils = __webpack_require__(288); function generate(patterns, settings) { const positivePatterns = getPositivePatterns(patterns); const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); @@ -38837,30 +39032,30 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 287 */ +/* 288 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const array = __webpack_require__(288); +const array = __webpack_require__(289); exports.array = array; -const errno = __webpack_require__(289); +const errno = __webpack_require__(290); exports.errno = errno; -const fs = __webpack_require__(290); +const fs = __webpack_require__(291); exports.fs = fs; -const path = __webpack_require__(291); +const path = __webpack_require__(292); exports.path = path; -const pattern = __webpack_require__(292); +const pattern = __webpack_require__(293); exports.pattern = pattern; -const stream = __webpack_require__(313); +const stream = __webpack_require__(314); exports.stream = stream; -const string = __webpack_require__(314); +const string = __webpack_require__(315); exports.string = string; /***/ }), -/* 288 */ +/* 289 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38888,7 +39083,7 @@ exports.splitWhen = splitWhen; /***/ }), -/* 289 */ +/* 290 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38901,7 +39096,7 @@ exports.isEnoentCodeError = isEnoentCodeError; /***/ }), -/* 290 */ +/* 291 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38926,7 +39121,7 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 291 */ +/* 292 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38965,16 +39160,16 @@ exports.removeLeadingDotSegment = removeLeadingDotSegment; /***/ }), -/* 292 */ +/* 293 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const globParent = __webpack_require__(293); -const micromatch = __webpack_require__(296); -const picomatch = __webpack_require__(307); +const globParent = __webpack_require__(294); +const micromatch = __webpack_require__(297); +const picomatch = __webpack_require__(308); const GLOBSTAR = '**'; const ESCAPE_SYMBOL = '\\'; const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; @@ -39084,13 +39279,13 @@ exports.matchAny = matchAny; /***/ }), -/* 293 */ +/* 294 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isGlob = __webpack_require__(294); +var isGlob = __webpack_require__(295); var pathPosixDirname = __webpack_require__(4).posix.dirname; var isWin32 = __webpack_require__(121).platform() === 'win32'; @@ -39132,7 +39327,7 @@ module.exports = function globParent(str, opts) { /***/ }), -/* 294 */ +/* 295 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -39142,7 +39337,7 @@ module.exports = function globParent(str, opts) { * Released under the MIT License. */ -var isExtglob = __webpack_require__(295); +var isExtglob = __webpack_require__(296); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -39186,7 +39381,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 295 */ +/* 296 */ /***/ (function(module, exports) { /*! @@ -39212,16 +39407,16 @@ module.exports = function isExtglob(str) { /***/ }), -/* 296 */ +/* 297 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const util = __webpack_require__(112); -const braces = __webpack_require__(297); -const picomatch = __webpack_require__(307); -const utils = __webpack_require__(310); +const braces = __webpack_require__(298); +const picomatch = __webpack_require__(308); +const utils = __webpack_require__(311); const isEmptyString = val => typeof val === 'string' && (val === '' || val === './'); /** @@ -39686,16 +39881,16 @@ module.exports = micromatch; /***/ }), -/* 297 */ +/* 298 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(298); -const compile = __webpack_require__(300); -const expand = __webpack_require__(304); -const parse = __webpack_require__(305); +const stringify = __webpack_require__(299); +const compile = __webpack_require__(301); +const expand = __webpack_require__(305); +const parse = __webpack_require__(306); /** * Expand the given pattern or create a regex-compatible string. @@ -39863,13 +40058,13 @@ module.exports = braces; /***/ }), -/* 298 */ +/* 299 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(299); +const utils = __webpack_require__(300); module.exports = (ast, options = {}) => { let stringify = (node, parent = {}) => { @@ -39902,7 +40097,7 @@ module.exports = (ast, options = {}) => { /***/ }), -/* 299 */ +/* 300 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40021,14 +40216,14 @@ exports.flatten = (...args) => { /***/ }), -/* 300 */ +/* 301 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(301); -const utils = __webpack_require__(299); +const fill = __webpack_require__(302); +const utils = __webpack_require__(300); const compile = (ast, options = {}) => { let walk = (node, parent = {}) => { @@ -40085,7 +40280,7 @@ module.exports = compile; /***/ }), -/* 301 */ +/* 302 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40099,7 +40294,7 @@ module.exports = compile; const util = __webpack_require__(112); -const toRegexRange = __webpack_require__(302); +const toRegexRange = __webpack_require__(303); const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); @@ -40341,7 +40536,7 @@ module.exports = fill; /***/ }), -/* 302 */ +/* 303 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40354,7 +40549,7 @@ module.exports = fill; -const isNumber = __webpack_require__(303); +const isNumber = __webpack_require__(304); const toRegexRange = (min, max, options) => { if (isNumber(min) === false) { @@ -40636,7 +40831,7 @@ module.exports = toRegexRange; /***/ }), -/* 303 */ +/* 304 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40661,15 +40856,15 @@ module.exports = function(num) { /***/ }), -/* 304 */ +/* 305 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(301); -const stringify = __webpack_require__(298); -const utils = __webpack_require__(299); +const fill = __webpack_require__(302); +const stringify = __webpack_require__(299); +const utils = __webpack_require__(300); const append = (queue = '', stash = '', enclose = false) => { let result = []; @@ -40781,13 +40976,13 @@ module.exports = expand; /***/ }), -/* 305 */ +/* 306 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(298); +const stringify = __webpack_require__(299); /** * Constants @@ -40809,7 +41004,7 @@ const { CHAR_SINGLE_QUOTE, /* ' */ CHAR_NO_BREAK_SPACE, CHAR_ZERO_WIDTH_NOBREAK_SPACE -} = __webpack_require__(306); +} = __webpack_require__(307); /** * parse @@ -41121,7 +41316,7 @@ module.exports = parse; /***/ }), -/* 306 */ +/* 307 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41185,27 +41380,27 @@ module.exports = { /***/ }), -/* 307 */ +/* 308 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(308); +module.exports = __webpack_require__(309); /***/ }), -/* 308 */ +/* 309 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const scan = __webpack_require__(309); -const parse = __webpack_require__(312); -const utils = __webpack_require__(310); -const constants = __webpack_require__(311); +const scan = __webpack_require__(310); +const parse = __webpack_require__(313); +const utils = __webpack_require__(311); +const constants = __webpack_require__(312); const isObject = val => val && typeof val === 'object' && !Array.isArray(val); /** @@ -41541,13 +41736,13 @@ module.exports = picomatch; /***/ }), -/* 309 */ +/* 310 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(310); +const utils = __webpack_require__(311); const { CHAR_ASTERISK, /* * */ CHAR_AT, /* @ */ @@ -41564,7 +41759,7 @@ const { CHAR_RIGHT_CURLY_BRACE, /* } */ CHAR_RIGHT_PARENTHESES, /* ) */ CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(311); +} = __webpack_require__(312); const isPathSeparator = code => { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; @@ -41931,7 +42126,7 @@ module.exports = scan; /***/ }), -/* 310 */ +/* 311 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41944,7 +42139,7 @@ const { REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL -} = __webpack_require__(311); +} = __webpack_require__(312); exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); @@ -42002,7 +42197,7 @@ exports.wrapOutput = (input, state = {}, options = {}) => { /***/ }), -/* 311 */ +/* 312 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -42188,14 +42383,14 @@ module.exports = { /***/ }), -/* 312 */ +/* 313 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const constants = __webpack_require__(311); -const utils = __webpack_require__(310); +const constants = __webpack_require__(312); +const utils = __webpack_require__(311); /** * Constants @@ -43273,13 +43468,13 @@ module.exports = parse; /***/ }), -/* 313 */ +/* 314 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const merge2 = __webpack_require__(284); +const merge2 = __webpack_require__(285); function merge(streams) { const mergedStream = merge2(streams); streams.forEach((stream) => { @@ -43296,7 +43491,7 @@ function propagateCloseEventToSources(streams) { /***/ }), -/* 314 */ +/* 315 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43313,14 +43508,14 @@ exports.isEmpty = isEmpty; /***/ }), -/* 315 */ +/* 316 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(316); -const provider_1 = __webpack_require__(343); +const stream_1 = __webpack_require__(317); +const provider_1 = __webpack_require__(344); class ProviderAsync extends provider_1.default { constructor() { super(...arguments); @@ -43348,16 +43543,16 @@ exports.default = ProviderAsync; /***/ }), -/* 316 */ +/* 317 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(138); -const fsStat = __webpack_require__(317); -const fsWalk = __webpack_require__(322); -const reader_1 = __webpack_require__(342); +const fsStat = __webpack_require__(318); +const fsWalk = __webpack_require__(323); +const reader_1 = __webpack_require__(343); class ReaderStream extends reader_1.default { constructor() { super(...arguments); @@ -43410,15 +43605,15 @@ exports.default = ReaderStream; /***/ }), -/* 317 */ +/* 318 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(318); -const sync = __webpack_require__(319); -const settings_1 = __webpack_require__(320); +const async = __webpack_require__(319); +const sync = __webpack_require__(320); +const settings_1 = __webpack_require__(321); exports.Settings = settings_1.default; function stat(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43441,7 +43636,7 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 318 */ +/* 319 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43479,7 +43674,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 319 */ +/* 320 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43508,13 +43703,13 @@ exports.read = read; /***/ }), -/* 320 */ +/* 321 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(321); +const fs = __webpack_require__(322); class Settings { constructor(_options = {}) { this._options = _options; @@ -43531,7 +43726,7 @@ exports.default = Settings; /***/ }), -/* 321 */ +/* 322 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43554,16 +43749,16 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 322 */ +/* 323 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(323); -const stream_1 = __webpack_require__(338); -const sync_1 = __webpack_require__(339); -const settings_1 = __webpack_require__(341); +const async_1 = __webpack_require__(324); +const stream_1 = __webpack_require__(339); +const sync_1 = __webpack_require__(340); +const settings_1 = __webpack_require__(342); exports.Settings = settings_1.default; function walk(directory, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43593,13 +43788,13 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 323 */ +/* 324 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(324); +const async_1 = __webpack_require__(325); class AsyncProvider { constructor(_root, _settings) { this._root = _root; @@ -43630,17 +43825,17 @@ function callSuccessCallback(callback, entries) { /***/ }), -/* 324 */ +/* 325 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = __webpack_require__(156); -const fsScandir = __webpack_require__(325); -const fastq = __webpack_require__(334); -const common = __webpack_require__(336); -const reader_1 = __webpack_require__(337); +const fsScandir = __webpack_require__(326); +const fastq = __webpack_require__(335); +const common = __webpack_require__(337); +const reader_1 = __webpack_require__(338); class AsyncReader extends reader_1.default { constructor(_root, _settings) { super(_root, _settings); @@ -43730,15 +43925,15 @@ exports.default = AsyncReader; /***/ }), -/* 325 */ +/* 326 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(326); -const sync = __webpack_require__(331); -const settings_1 = __webpack_require__(332); +const async = __webpack_require__(327); +const sync = __webpack_require__(332); +const settings_1 = __webpack_require__(333); exports.Settings = settings_1.default; function scandir(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43761,16 +43956,16 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 326 */ +/* 327 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(317); -const rpl = __webpack_require__(327); -const constants_1 = __webpack_require__(328); -const utils = __webpack_require__(329); +const fsStat = __webpack_require__(318); +const rpl = __webpack_require__(328); +const constants_1 = __webpack_require__(329); +const utils = __webpack_require__(330); function read(directory, settings, callback) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings, callback); @@ -43858,7 +44053,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 327 */ +/* 328 */ /***/ (function(module, exports) { module.exports = runParallel @@ -43912,7 +44107,7 @@ function runParallel (tasks, cb) { /***/ }), -/* 328 */ +/* 329 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43932,18 +44127,18 @@ exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_B /***/ }), -/* 329 */ +/* 330 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(330); +const fs = __webpack_require__(331); exports.fs = fs; /***/ }), -/* 330 */ +/* 331 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43968,15 +44163,15 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 331 */ +/* 332 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(317); -const constants_1 = __webpack_require__(328); -const utils = __webpack_require__(329); +const fsStat = __webpack_require__(318); +const constants_1 = __webpack_require__(329); +const utils = __webpack_require__(330); function read(directory, settings) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings); @@ -44027,15 +44222,15 @@ exports.readdir = readdir; /***/ }), -/* 332 */ +/* 333 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(317); -const fs = __webpack_require__(333); +const fsStat = __webpack_require__(318); +const fs = __webpack_require__(334); class Settings { constructor(_options = {}) { this._options = _options; @@ -44058,7 +44253,7 @@ exports.default = Settings; /***/ }), -/* 333 */ +/* 334 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44083,13 +44278,13 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 334 */ +/* 335 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var reusify = __webpack_require__(335) +var reusify = __webpack_require__(336) function fastqueue (context, worker, concurrency) { if (typeof context === 'function') { @@ -44263,7 +44458,7 @@ module.exports = fastqueue /***/ }), -/* 335 */ +/* 336 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44303,7 +44498,7 @@ module.exports = reusify /***/ }), -/* 336 */ +/* 337 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44334,13 +44529,13 @@ exports.joinPathSegments = joinPathSegments; /***/ }), -/* 337 */ +/* 338 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const common = __webpack_require__(336); +const common = __webpack_require__(337); class Reader { constructor(_root, _settings) { this._root = _root; @@ -44352,14 +44547,14 @@ exports.default = Reader; /***/ }), -/* 338 */ +/* 339 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(138); -const async_1 = __webpack_require__(324); +const async_1 = __webpack_require__(325); class StreamProvider { constructor(_root, _settings) { this._root = _root; @@ -44389,13 +44584,13 @@ exports.default = StreamProvider; /***/ }), -/* 339 */ +/* 340 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(340); +const sync_1 = __webpack_require__(341); class SyncProvider { constructor(_root, _settings) { this._root = _root; @@ -44410,15 +44605,15 @@ exports.default = SyncProvider; /***/ }), -/* 340 */ +/* 341 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsScandir = __webpack_require__(325); -const common = __webpack_require__(336); -const reader_1 = __webpack_require__(337); +const fsScandir = __webpack_require__(326); +const common = __webpack_require__(337); +const reader_1 = __webpack_require__(338); class SyncReader extends reader_1.default { constructor() { super(...arguments); @@ -44476,14 +44671,14 @@ exports.default = SyncReader; /***/ }), -/* 341 */ +/* 342 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsScandir = __webpack_require__(325); +const fsScandir = __webpack_require__(326); class Settings { constructor(_options = {}) { this._options = _options; @@ -44509,15 +44704,15 @@ exports.default = Settings; /***/ }), -/* 342 */ +/* 343 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(317); -const utils = __webpack_require__(287); +const fsStat = __webpack_require__(318); +const utils = __webpack_require__(288); class Reader { constructor(_settings) { this._settings = _settings; @@ -44549,17 +44744,17 @@ exports.default = Reader; /***/ }), -/* 343 */ +/* 344 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const deep_1 = __webpack_require__(344); -const entry_1 = __webpack_require__(347); -const error_1 = __webpack_require__(348); -const entry_2 = __webpack_require__(349); +const deep_1 = __webpack_require__(345); +const entry_1 = __webpack_require__(348); +const error_1 = __webpack_require__(349); +const entry_2 = __webpack_require__(350); class Provider { constructor(_settings) { this._settings = _settings; @@ -44604,14 +44799,14 @@ exports.default = Provider; /***/ }), -/* 344 */ +/* 345 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); -const partial_1 = __webpack_require__(345); +const utils = __webpack_require__(288); +const partial_1 = __webpack_require__(346); class DeepFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -44665,13 +44860,13 @@ exports.default = DeepFilter; /***/ }), -/* 345 */ +/* 346 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(346); +const matcher_1 = __webpack_require__(347); class PartialMatcher extends matcher_1.default { match(filepath) { const parts = filepath.split('/'); @@ -44710,13 +44905,13 @@ exports.default = PartialMatcher; /***/ }), -/* 346 */ +/* 347 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); +const utils = __webpack_require__(288); class Matcher { constructor(_patterns, _settings, _micromatchOptions) { this._patterns = _patterns; @@ -44767,13 +44962,13 @@ exports.default = Matcher; /***/ }), -/* 347 */ +/* 348 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); +const utils = __webpack_require__(288); class EntryFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -44829,13 +45024,13 @@ exports.default = EntryFilter; /***/ }), -/* 348 */ +/* 349 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); +const utils = __webpack_require__(288); class ErrorFilter { constructor(_settings) { this._settings = _settings; @@ -44851,13 +45046,13 @@ exports.default = ErrorFilter; /***/ }), -/* 349 */ +/* 350 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); +const utils = __webpack_require__(288); class EntryTransformer { constructor(_settings) { this._settings = _settings; @@ -44884,15 +45079,15 @@ exports.default = EntryTransformer; /***/ }), -/* 350 */ +/* 351 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(138); -const stream_2 = __webpack_require__(316); -const provider_1 = __webpack_require__(343); +const stream_2 = __webpack_require__(317); +const provider_1 = __webpack_require__(344); class ProviderStream extends provider_1.default { constructor() { super(...arguments); @@ -44922,14 +45117,14 @@ exports.default = ProviderStream; /***/ }), -/* 351 */ +/* 352 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(352); -const provider_1 = __webpack_require__(343); +const sync_1 = __webpack_require__(353); +const provider_1 = __webpack_require__(344); class ProviderSync extends provider_1.default { constructor() { super(...arguments); @@ -44952,15 +45147,15 @@ exports.default = ProviderSync; /***/ }), -/* 352 */ +/* 353 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(317); -const fsWalk = __webpack_require__(322); -const reader_1 = __webpack_require__(342); +const fsStat = __webpack_require__(318); +const fsWalk = __webpack_require__(323); +const reader_1 = __webpack_require__(343); class ReaderSync extends reader_1.default { constructor() { super(...arguments); @@ -45002,7 +45197,7 @@ exports.default = ReaderSync; /***/ }), -/* 353 */ +/* 354 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45061,13 +45256,13 @@ exports.default = Settings; /***/ }), -/* 354 */ +/* 355 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(355); +const pathType = __webpack_require__(356); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -45143,7 +45338,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 355 */ +/* 356 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45193,7 +45388,7 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 356 */ +/* 357 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45201,9 +45396,9 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); const {promisify} = __webpack_require__(112); const fs = __webpack_require__(134); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(285); -const gitIgnore = __webpack_require__(357); -const slash = __webpack_require__(358); +const fastGlob = __webpack_require__(286); +const gitIgnore = __webpack_require__(358); +const slash = __webpack_require__(359); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -45317,7 +45512,7 @@ module.exports.sync = options => { /***/ }), -/* 357 */ +/* 358 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -45920,7 +46115,7 @@ if ( /***/ }), -/* 358 */ +/* 359 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45938,7 +46133,7 @@ module.exports = path => { /***/ }), -/* 359 */ +/* 360 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45991,7 +46186,7 @@ module.exports = { /***/ }), -/* 360 */ +/* 361 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46013,7 +46208,7 @@ module.exports = path_ => { /***/ }), -/* 361 */ +/* 362 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46041,7 +46236,7 @@ module.exports = (childPath, parentPath) => { /***/ }), -/* 362 */ +/* 363 */ /***/ (function(module, exports, __webpack_require__) { const assert = __webpack_require__(140) @@ -46407,12 +46602,12 @@ rimraf.sync = rimrafSync /***/ }), -/* 363 */ +/* 364 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(364); +const AggregateError = __webpack_require__(365); module.exports = async ( iterable, @@ -46495,13 +46690,13 @@ module.exports = async ( /***/ }), -/* 364 */ +/* 365 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const indentString = __webpack_require__(365); -const cleanStack = __webpack_require__(366); +const indentString = __webpack_require__(366); +const cleanStack = __webpack_require__(367); const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); @@ -46549,7 +46744,7 @@ module.exports = AggregateError; /***/ }), -/* 365 */ +/* 366 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46591,7 +46786,7 @@ module.exports = (string, count = 1, options) => { /***/ }), -/* 366 */ +/* 367 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46638,20 +46833,20 @@ module.exports = (stack, options) => { /***/ }), -/* 367 */ +/* 368 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readline = __webpack_require__(368); -const chalk = __webpack_require__(369); -const cliCursor = __webpack_require__(376); -const cliSpinners = __webpack_require__(380); -const logSymbols = __webpack_require__(382); -const stripAnsi = __webpack_require__(391); -const wcwidth = __webpack_require__(393); -const isInteractive = __webpack_require__(397); -const MuteStream = __webpack_require__(398); +const readline = __webpack_require__(369); +const chalk = __webpack_require__(370); +const cliCursor = __webpack_require__(377); +const cliSpinners = __webpack_require__(381); +const logSymbols = __webpack_require__(383); +const stripAnsi = __webpack_require__(392); +const wcwidth = __webpack_require__(394); +const isInteractive = __webpack_require__(398); +const MuteStream = __webpack_require__(399); const TEXT = Symbol('text'); const PREFIX_TEXT = Symbol('prefixText'); @@ -47004,23 +47199,23 @@ module.exports.promise = (action, options) => { /***/ }), -/* 368 */ +/* 369 */ /***/ (function(module, exports) { module.exports = require("readline"); /***/ }), -/* 369 */ +/* 370 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiStyles = __webpack_require__(370); +const ansiStyles = __webpack_require__(371); const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(120); const { stringReplaceAll, stringEncaseCRLFWithFirstIndex -} = __webpack_require__(374); +} = __webpack_require__(375); // `supportsColor.level` → `ansiStyles.color[name]` mapping const levelMapping = [ @@ -47221,7 +47416,7 @@ const chalkTag = (chalk, ...strings) => { } if (template === undefined) { - template = __webpack_require__(375); + template = __webpack_require__(376); } return template(chalk, parts.join('')); @@ -47250,7 +47445,7 @@ module.exports = chalk; /***/ }), -/* 370 */ +/* 371 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47296,7 +47491,7 @@ const setLazyProperty = (object, property, get) => { let colorConvert; const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => { if (colorConvert === undefined) { - colorConvert = __webpack_require__(371); + colorConvert = __webpack_require__(372); } const offset = isBackground ? 10 : 0; @@ -47421,11 +47616,11 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) /***/ }), -/* 371 */ +/* 372 */ /***/ (function(module, exports, __webpack_require__) { -const conversions = __webpack_require__(372); -const route = __webpack_require__(373); +const conversions = __webpack_require__(373); +const route = __webpack_require__(374); const convert = {}; @@ -47508,7 +47703,7 @@ module.exports = convert; /***/ }), -/* 372 */ +/* 373 */ /***/ (function(module, exports, __webpack_require__) { /* MIT license */ @@ -48353,10 +48548,10 @@ convert.rgb.gray = function (rgb) { /***/ }), -/* 373 */ +/* 374 */ /***/ (function(module, exports, __webpack_require__) { -const conversions = __webpack_require__(372); +const conversions = __webpack_require__(373); /* This function routes a model to all other models. @@ -48456,7 +48651,7 @@ module.exports = function (fromModel) { /***/ }), -/* 374 */ +/* 375 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48502,7 +48697,7 @@ module.exports = { /***/ }), -/* 375 */ +/* 376 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48643,12 +48838,12 @@ module.exports = (chalk, temporary) => { /***/ }), -/* 376 */ +/* 377 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const restoreCursor = __webpack_require__(377); +const restoreCursor = __webpack_require__(378); let isHidden = false; @@ -48685,12 +48880,12 @@ exports.toggle = (force, writableStream) => { /***/ }), -/* 377 */ +/* 378 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const onetime = __webpack_require__(378); +const onetime = __webpack_require__(379); const signalExit = __webpack_require__(218); module.exports = onetime(() => { @@ -48701,12 +48896,12 @@ module.exports = onetime(() => { /***/ }), -/* 378 */ +/* 379 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const mimicFn = __webpack_require__(379); +const mimicFn = __webpack_require__(380); const calledFunctions = new WeakMap(); @@ -48758,7 +48953,7 @@ module.exports.callCount = fn => { /***/ }), -/* 379 */ +/* 380 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48778,13 +48973,13 @@ module.exports.default = mimicFn; /***/ }), -/* 380 */ +/* 381 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const spinners = Object.assign({}, __webpack_require__(381)); +const spinners = Object.assign({}, __webpack_require__(382)); const spinnersList = Object.keys(spinners); @@ -48802,18 +48997,18 @@ module.exports.default = spinners; /***/ }), -/* 381 */ +/* 382 */ /***/ (function(module) { module.exports = JSON.parse("{\"dots\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠹\",\"⠸\",\"⠼\",\"⠴\",\"⠦\",\"⠧\",\"⠇\",\"⠏\"]},\"dots2\":{\"interval\":80,\"frames\":[\"⣾\",\"⣽\",\"⣻\",\"⢿\",\"⡿\",\"⣟\",\"⣯\",\"⣷\"]},\"dots3\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠞\",\"⠖\",\"⠦\",\"⠴\",\"⠲\",\"⠳\",\"⠓\"]},\"dots4\":{\"interval\":80,\"frames\":[\"⠄\",\"⠆\",\"⠇\",\"⠋\",\"⠙\",\"⠸\",\"⠰\",\"⠠\",\"⠰\",\"⠸\",\"⠙\",\"⠋\",\"⠇\",\"⠆\"]},\"dots5\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\"]},\"dots6\":{\"interval\":80,\"frames\":[\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠴\",\"⠲\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠚\",\"⠙\",\"⠉\",\"⠁\"]},\"dots7\":{\"interval\":80,\"frames\":[\"⠈\",\"⠉\",\"⠋\",\"⠓\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠖\",\"⠦\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\"]},\"dots8\":{\"interval\":80,\"frames\":[\"⠁\",\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\",\"⠈\"]},\"dots9\":{\"interval\":80,\"frames\":[\"⢹\",\"⢺\",\"⢼\",\"⣸\",\"⣇\",\"⡧\",\"⡗\",\"⡏\"]},\"dots10\":{\"interval\":80,\"frames\":[\"⢄\",\"⢂\",\"⢁\",\"⡁\",\"⡈\",\"⡐\",\"⡠\"]},\"dots11\":{\"interval\":100,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⡀\",\"⢀\",\"⠠\",\"⠐\",\"⠈\"]},\"dots12\":{\"interval\":80,\"frames\":[\"⢀⠀\",\"⡀⠀\",\"⠄⠀\",\"⢂⠀\",\"⡂⠀\",\"⠅⠀\",\"⢃⠀\",\"⡃⠀\",\"⠍⠀\",\"⢋⠀\",\"⡋⠀\",\"⠍⠁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⢈⠩\",\"⡀⢙\",\"⠄⡙\",\"⢂⠩\",\"⡂⢘\",\"⠅⡘\",\"⢃⠨\",\"⡃⢐\",\"⠍⡐\",\"⢋⠠\",\"⡋⢀\",\"⠍⡁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⠈⠩\",\"⠀⢙\",\"⠀⡙\",\"⠀⠩\",\"⠀⢘\",\"⠀⡘\",\"⠀⠨\",\"⠀⢐\",\"⠀⡐\",\"⠀⠠\",\"⠀⢀\",\"⠀⡀\"]},\"dots8Bit\":{\"interval\":80,\"frames\":[\"⠀\",\"⠁\",\"⠂\",\"⠃\",\"⠄\",\"⠅\",\"⠆\",\"⠇\",\"⡀\",\"⡁\",\"⡂\",\"⡃\",\"⡄\",\"⡅\",\"⡆\",\"⡇\",\"⠈\",\"⠉\",\"⠊\",\"⠋\",\"⠌\",\"⠍\",\"⠎\",\"⠏\",\"⡈\",\"⡉\",\"⡊\",\"⡋\",\"⡌\",\"⡍\",\"⡎\",\"⡏\",\"⠐\",\"⠑\",\"⠒\",\"⠓\",\"⠔\",\"⠕\",\"⠖\",\"⠗\",\"⡐\",\"⡑\",\"⡒\",\"⡓\",\"⡔\",\"⡕\",\"⡖\",\"⡗\",\"⠘\",\"⠙\",\"⠚\",\"⠛\",\"⠜\",\"⠝\",\"⠞\",\"⠟\",\"⡘\",\"⡙\",\"⡚\",\"⡛\",\"⡜\",\"⡝\",\"⡞\",\"⡟\",\"⠠\",\"⠡\",\"⠢\",\"⠣\",\"⠤\",\"⠥\",\"⠦\",\"⠧\",\"⡠\",\"⡡\",\"⡢\",\"⡣\",\"⡤\",\"⡥\",\"⡦\",\"⡧\",\"⠨\",\"⠩\",\"⠪\",\"⠫\",\"⠬\",\"⠭\",\"⠮\",\"⠯\",\"⡨\",\"⡩\",\"⡪\",\"⡫\",\"⡬\",\"⡭\",\"⡮\",\"⡯\",\"⠰\",\"⠱\",\"⠲\",\"⠳\",\"⠴\",\"⠵\",\"⠶\",\"⠷\",\"⡰\",\"⡱\",\"⡲\",\"⡳\",\"⡴\",\"⡵\",\"⡶\",\"⡷\",\"⠸\",\"⠹\",\"⠺\",\"⠻\",\"⠼\",\"⠽\",\"⠾\",\"⠿\",\"⡸\",\"⡹\",\"⡺\",\"⡻\",\"⡼\",\"⡽\",\"⡾\",\"⡿\",\"⢀\",\"⢁\",\"⢂\",\"⢃\",\"⢄\",\"⢅\",\"⢆\",\"⢇\",\"⣀\",\"⣁\",\"⣂\",\"⣃\",\"⣄\",\"⣅\",\"⣆\",\"⣇\",\"⢈\",\"⢉\",\"⢊\",\"⢋\",\"⢌\",\"⢍\",\"⢎\",\"⢏\",\"⣈\",\"⣉\",\"⣊\",\"⣋\",\"⣌\",\"⣍\",\"⣎\",\"⣏\",\"⢐\",\"⢑\",\"⢒\",\"⢓\",\"⢔\",\"⢕\",\"⢖\",\"⢗\",\"⣐\",\"⣑\",\"⣒\",\"⣓\",\"⣔\",\"⣕\",\"⣖\",\"⣗\",\"⢘\",\"⢙\",\"⢚\",\"⢛\",\"⢜\",\"⢝\",\"⢞\",\"⢟\",\"⣘\",\"⣙\",\"⣚\",\"⣛\",\"⣜\",\"⣝\",\"⣞\",\"⣟\",\"⢠\",\"⢡\",\"⢢\",\"⢣\",\"⢤\",\"⢥\",\"⢦\",\"⢧\",\"⣠\",\"⣡\",\"⣢\",\"⣣\",\"⣤\",\"⣥\",\"⣦\",\"⣧\",\"⢨\",\"⢩\",\"⢪\",\"⢫\",\"⢬\",\"⢭\",\"⢮\",\"⢯\",\"⣨\",\"⣩\",\"⣪\",\"⣫\",\"⣬\",\"⣭\",\"⣮\",\"⣯\",\"⢰\",\"⢱\",\"⢲\",\"⢳\",\"⢴\",\"⢵\",\"⢶\",\"⢷\",\"⣰\",\"⣱\",\"⣲\",\"⣳\",\"⣴\",\"⣵\",\"⣶\",\"⣷\",\"⢸\",\"⢹\",\"⢺\",\"⢻\",\"⢼\",\"⢽\",\"⢾\",\"⢿\",\"⣸\",\"⣹\",\"⣺\",\"⣻\",\"⣼\",\"⣽\",\"⣾\",\"⣿\"]},\"line\":{\"interval\":130,\"frames\":[\"-\",\"\\\\\",\"|\",\"/\"]},\"line2\":{\"interval\":100,\"frames\":[\"⠂\",\"-\",\"–\",\"—\",\"–\",\"-\"]},\"pipe\":{\"interval\":100,\"frames\":[\"┤\",\"┘\",\"┴\",\"└\",\"├\",\"┌\",\"┬\",\"┐\"]},\"simpleDots\":{\"interval\":400,\"frames\":[\". \",\".. \",\"...\",\" \"]},\"simpleDotsScrolling\":{\"interval\":200,\"frames\":[\". \",\".. \",\"...\",\" ..\",\" .\",\" \"]},\"star\":{\"interval\":70,\"frames\":[\"✶\",\"✸\",\"✹\",\"✺\",\"✹\",\"✷\"]},\"star2\":{\"interval\":80,\"frames\":[\"+\",\"x\",\"*\"]},\"flip\":{\"interval\":70,\"frames\":[\"_\",\"_\",\"_\",\"-\",\"`\",\"`\",\"'\",\"´\",\"-\",\"_\",\"_\",\"_\"]},\"hamburger\":{\"interval\":100,\"frames\":[\"☱\",\"☲\",\"☴\"]},\"growVertical\":{\"interval\":120,\"frames\":[\"▁\",\"▃\",\"▄\",\"▅\",\"▆\",\"▇\",\"▆\",\"▅\",\"▄\",\"▃\"]},\"growHorizontal\":{\"interval\":120,\"frames\":[\"▏\",\"▎\",\"▍\",\"▌\",\"▋\",\"▊\",\"▉\",\"▊\",\"▋\",\"▌\",\"▍\",\"▎\"]},\"balloon\":{\"interval\":140,\"frames\":[\" \",\".\",\"o\",\"O\",\"@\",\"*\",\" \"]},\"balloon2\":{\"interval\":120,\"frames\":[\".\",\"o\",\"O\",\"°\",\"O\",\"o\",\".\"]},\"noise\":{\"interval\":100,\"frames\":[\"▓\",\"▒\",\"░\"]},\"bounce\":{\"interval\":120,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⠂\"]},\"boxBounce\":{\"interval\":120,\"frames\":[\"▖\",\"▘\",\"▝\",\"▗\"]},\"boxBounce2\":{\"interval\":100,\"frames\":[\"▌\",\"▀\",\"▐\",\"▄\"]},\"triangle\":{\"interval\":50,\"frames\":[\"◢\",\"◣\",\"◤\",\"◥\"]},\"arc\":{\"interval\":100,\"frames\":[\"◜\",\"◠\",\"◝\",\"◞\",\"◡\",\"◟\"]},\"circle\":{\"interval\":120,\"frames\":[\"◡\",\"⊙\",\"◠\"]},\"squareCorners\":{\"interval\":180,\"frames\":[\"◰\",\"◳\",\"◲\",\"◱\"]},\"circleQuarters\":{\"interval\":120,\"frames\":[\"◴\",\"◷\",\"◶\",\"◵\"]},\"circleHalves\":{\"interval\":50,\"frames\":[\"◐\",\"◓\",\"◑\",\"◒\"]},\"squish\":{\"interval\":100,\"frames\":[\"╫\",\"╪\"]},\"toggle\":{\"interval\":250,\"frames\":[\"⊶\",\"⊷\"]},\"toggle2\":{\"interval\":80,\"frames\":[\"▫\",\"▪\"]},\"toggle3\":{\"interval\":120,\"frames\":[\"□\",\"■\"]},\"toggle4\":{\"interval\":100,\"frames\":[\"■\",\"□\",\"▪\",\"▫\"]},\"toggle5\":{\"interval\":100,\"frames\":[\"▮\",\"▯\"]},\"toggle6\":{\"interval\":300,\"frames\":[\"ဝ\",\"၀\"]},\"toggle7\":{\"interval\":80,\"frames\":[\"⦾\",\"⦿\"]},\"toggle8\":{\"interval\":100,\"frames\":[\"◍\",\"◌\"]},\"toggle9\":{\"interval\":100,\"frames\":[\"◉\",\"◎\"]},\"toggle10\":{\"interval\":100,\"frames\":[\"㊂\",\"㊀\",\"㊁\"]},\"toggle11\":{\"interval\":50,\"frames\":[\"⧇\",\"⧆\"]},\"toggle12\":{\"interval\":120,\"frames\":[\"☗\",\"☖\"]},\"toggle13\":{\"interval\":80,\"frames\":[\"=\",\"*\",\"-\"]},\"arrow\":{\"interval\":100,\"frames\":[\"←\",\"↖\",\"↑\",\"↗\",\"→\",\"↘\",\"↓\",\"↙\"]},\"arrow2\":{\"interval\":80,\"frames\":[\"⬆️ \",\"↗️ \",\"➡️ \",\"↘️ \",\"⬇️ \",\"↙️ \",\"⬅️ \",\"↖️ \"]},\"arrow3\":{\"interval\":120,\"frames\":[\"▹▹▹▹▹\",\"▸▹▹▹▹\",\"▹▸▹▹▹\",\"▹▹▸▹▹\",\"▹▹▹▸▹\",\"▹▹▹▹▸\"]},\"bouncingBar\":{\"interval\":80,\"frames\":[\"[ ]\",\"[= ]\",\"[== ]\",\"[=== ]\",\"[ ===]\",\"[ ==]\",\"[ =]\",\"[ ]\",\"[ =]\",\"[ ==]\",\"[ ===]\",\"[====]\",\"[=== ]\",\"[== ]\",\"[= ]\"]},\"bouncingBall\":{\"interval\":80,\"frames\":[\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ●)\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"(● )\"]},\"smiley\":{\"interval\":200,\"frames\":[\"😄 \",\"😝 \"]},\"monkey\":{\"interval\":300,\"frames\":[\"🙈 \",\"🙈 \",\"🙉 \",\"🙊 \"]},\"hearts\":{\"interval\":100,\"frames\":[\"💛 \",\"💙 \",\"💜 \",\"💚 \",\"❤️ \"]},\"clock\":{\"interval\":100,\"frames\":[\"🕛 \",\"🕐 \",\"🕑 \",\"🕒 \",\"🕓 \",\"🕔 \",\"🕕 \",\"🕖 \",\"🕗 \",\"🕘 \",\"🕙 \",\"🕚 \"]},\"earth\":{\"interval\":180,\"frames\":[\"🌍 \",\"🌎 \",\"🌏 \"]},\"material\":{\"interval\":17,\"frames\":[\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███████▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"██████████▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"█████████████▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁██████████████▁▁▁▁\",\"▁▁▁██████████████▁▁▁\",\"▁▁▁▁█████████████▁▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁▁█████████████▁▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁▁███████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁▁█████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\"]},\"moon\":{\"interval\":80,\"frames\":[\"🌑 \",\"🌒 \",\"🌓 \",\"🌔 \",\"🌕 \",\"🌖 \",\"🌗 \",\"🌘 \"]},\"runner\":{\"interval\":140,\"frames\":[\"🚶 \",\"🏃 \"]},\"pong\":{\"interval\":80,\"frames\":[\"▐⠂ ▌\",\"▐⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂▌\",\"▐ ⠠▌\",\"▐ ⡀▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐⠠ ▌\"]},\"shark\":{\"interval\":120,\"frames\":[\"▐|\\\\____________▌\",\"▐_|\\\\___________▌\",\"▐__|\\\\__________▌\",\"▐___|\\\\_________▌\",\"▐____|\\\\________▌\",\"▐_____|\\\\_______▌\",\"▐______|\\\\______▌\",\"▐_______|\\\\_____▌\",\"▐________|\\\\____▌\",\"▐_________|\\\\___▌\",\"▐__________|\\\\__▌\",\"▐___________|\\\\_▌\",\"▐____________|\\\\▌\",\"▐____________/|▌\",\"▐___________/|_▌\",\"▐__________/|__▌\",\"▐_________/|___▌\",\"▐________/|____▌\",\"▐_______/|_____▌\",\"▐______/|______▌\",\"▐_____/|_______▌\",\"▐____/|________▌\",\"▐___/|_________▌\",\"▐__/|__________▌\",\"▐_/|___________▌\",\"▐/|____________▌\"]},\"dqpb\":{\"interval\":100,\"frames\":[\"d\",\"q\",\"p\",\"b\"]},\"weather\":{\"interval\":100,\"frames\":[\"☀️ \",\"☀️ \",\"☀️ \",\"🌤 \",\"⛅️ \",\"🌥 \",\"☁️ \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"⛈ \",\"🌨 \",\"🌧 \",\"🌨 \",\"☁️ \",\"🌥 \",\"⛅️ \",\"🌤 \",\"☀️ \",\"☀️ \"]},\"christmas\":{\"interval\":400,\"frames\":[\"🌲\",\"🎄\"]},\"grenade\":{\"interval\":80,\"frames\":[\"، \",\"′ \",\" ´ \",\" ‾ \",\" ⸌\",\" ⸊\",\" |\",\" ⁎\",\" ⁕\",\" ෴ \",\" ⁓\",\" \",\" \",\" \"]},\"point\":{\"interval\":125,\"frames\":[\"∙∙∙\",\"●∙∙\",\"∙●∙\",\"∙∙●\",\"∙∙∙\"]},\"layer\":{\"interval\":150,\"frames\":[\"-\",\"=\",\"≡\"]},\"betaWave\":{\"interval\":80,\"frames\":[\"ρββββββ\",\"βρβββββ\",\"ββρββββ\",\"βββρβββ\",\"ββββρββ\",\"βββββρβ\",\"ββββββρ\"]}}"); /***/ }), -/* 382 */ +/* 383 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(383); +const chalk = __webpack_require__(384); const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; @@ -48835,16 +49030,16 @@ module.exports = isSupported ? main : fallbacks; /***/ }), -/* 383 */ +/* 384 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const escapeStringRegexp = __webpack_require__(179); -const ansiStyles = __webpack_require__(384); -const stdoutColor = __webpack_require__(389).stdout; +const ansiStyles = __webpack_require__(385); +const stdoutColor = __webpack_require__(390).stdout; -const template = __webpack_require__(390); +const template = __webpack_require__(391); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -49070,12 +49265,12 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 384 */ +/* 385 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(module) { -const colorConvert = __webpack_require__(385); +const colorConvert = __webpack_require__(386); const wrapAnsi16 = (fn, offset) => function () { const code = fn.apply(colorConvert, arguments); @@ -49243,11 +49438,11 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) /***/ }), -/* 385 */ +/* 386 */ /***/ (function(module, exports, __webpack_require__) { -var conversions = __webpack_require__(386); -var route = __webpack_require__(388); +var conversions = __webpack_require__(387); +var route = __webpack_require__(389); var convert = {}; @@ -49327,11 +49522,11 @@ module.exports = convert; /***/ }), -/* 386 */ +/* 387 */ /***/ (function(module, exports, __webpack_require__) { /* MIT license */ -var cssKeywords = __webpack_require__(387); +var cssKeywords = __webpack_require__(388); // NOTE: conversions should only return primitive values (i.e. arrays, or // values that give correct `typeof` results). @@ -50201,7 +50396,7 @@ convert.rgb.gray = function (rgb) { /***/ }), -/* 387 */ +/* 388 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50360,10 +50555,10 @@ module.exports = { /***/ }), -/* 388 */ +/* 389 */ /***/ (function(module, exports, __webpack_require__) { -var conversions = __webpack_require__(386); +var conversions = __webpack_require__(387); /* this function routes a model to all other models. @@ -50463,7 +50658,7 @@ module.exports = function (fromModel) { /***/ }), -/* 389 */ +/* 390 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50601,7 +50796,7 @@ module.exports = { /***/ }), -/* 390 */ +/* 391 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50736,18 +50931,18 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 391 */ +/* 392 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiRegex = __webpack_require__(392); +const ansiRegex = __webpack_require__(393); module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; /***/ }), -/* 392 */ +/* 393 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50764,14 +50959,14 @@ module.exports = ({onlyFirst = false} = {}) => { /***/ }), -/* 393 */ +/* 394 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var defaults = __webpack_require__(394) -var combining = __webpack_require__(396) +var defaults = __webpack_require__(395) +var combining = __webpack_require__(397) var DEFAULTS = { nul: 0, @@ -50870,10 +51065,10 @@ function bisearch(ucs) { /***/ }), -/* 394 */ +/* 395 */ /***/ (function(module, exports, __webpack_require__) { -var clone = __webpack_require__(395); +var clone = __webpack_require__(396); module.exports = function(options, defaults) { options = options || {}; @@ -50888,7 +51083,7 @@ module.exports = function(options, defaults) { }; /***/ }), -/* 395 */ +/* 396 */ /***/ (function(module, exports, __webpack_require__) { var clone = (function() { @@ -51060,7 +51255,7 @@ if ( true && module.exports) { /***/ }), -/* 396 */ +/* 397 */ /***/ (function(module, exports) { module.exports = [ @@ -51116,7 +51311,7 @@ module.exports = [ /***/ }), -/* 397 */ +/* 398 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -51132,7 +51327,7 @@ module.exports = ({stream = process.stdout} = {}) => { /***/ }), -/* 398 */ +/* 399 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(138) @@ -51283,7 +51478,7 @@ MuteStream.prototype.close = proxy('close') /***/ }), -/* 399 */ +/* 400 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51344,7 +51539,7 @@ const RunCommand = { }; /***/ }), -/* 400 */ +/* 401 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51354,7 +51549,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(144); /* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(146); -/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(401); +/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(402); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -51439,14 +51634,14 @@ const WatchCommand = { }; /***/ }), -/* 401 */ +/* 402 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "waitUntilWatchIsReady", function() { return waitUntilWatchIsReady; }); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(8); -/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(402); +/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(403); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -51513,141 +51708,141 @@ function waitUntilWatchIsReady(stream, opts = {}) { } /***/ }), -/* 402 */ +/* 403 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(403); +/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(404); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__["audit"]; }); -/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(404); +/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(405); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__["auditTime"]; }); -/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(405); +/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(406); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__["buffer"]; }); -/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(406); +/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(407); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__["bufferCount"]; }); -/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(407); +/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(408); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__["bufferTime"]; }); -/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(408); +/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(409); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__["bufferToggle"]; }); -/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(409); +/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(410); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__["bufferWhen"]; }); -/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(410); +/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(411); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__["catchError"]; }); -/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(411); +/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(412); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__["combineAll"]; }); -/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(412); +/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(413); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__["combineLatest"]; }); -/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(413); +/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(414); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__["concat"]; }); /* harmony import */ var _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(80); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__["concatAll"]; }); -/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(414); +/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(415); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__["concatMap"]; }); -/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(415); +/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(416); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__["concatMapTo"]; }); -/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(416); +/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(417); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "count", function() { return _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__["count"]; }); -/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(417); +/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(418); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__["debounce"]; }); -/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(418); +/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(419); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__["debounceTime"]; }); -/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(419); +/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(420); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__["defaultIfEmpty"]; }); -/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(420); +/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(421); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__["delay"]; }); -/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(422); +/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(423); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__["delayWhen"]; }); -/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(423); +/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(424); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__["dematerialize"]; }); -/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(424); +/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(425); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__["distinct"]; }); -/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(425); +/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(426); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__["distinctUntilChanged"]; }); -/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(426); +/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(427); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__["distinctUntilKeyChanged"]; }); -/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(427); +/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(428); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__["elementAt"]; }); -/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(430); +/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(431); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__["endWith"]; }); -/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(431); +/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(432); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "every", function() { return _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__["every"]; }); -/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(432); +/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(433); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__["exhaust"]; }); -/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(433); +/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(434); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__["exhaustMap"]; }); -/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(434); +/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(435); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__["expand"]; }); /* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(105); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__["filter"]; }); -/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(435); +/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(436); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__["finalize"]; }); -/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(436); +/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(437); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "find", function() { return _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__["find"]; }); -/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(437); +/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(438); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__["findIndex"]; }); -/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(438); +/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(439); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "first", function() { return _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__["first"]; }); /* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(31); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "groupBy", function() { return _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__["groupBy"]; }); -/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(439); +/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(440); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__["ignoreElements"]; }); -/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(440); +/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(441); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__["isEmpty"]; }); -/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(441); +/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(442); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "last", function() { return _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__["last"]; }); /* harmony import */ var _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(66); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "map", function() { return _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__["map"]; }); -/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(443); +/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(444); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__["mapTo"]; }); -/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(444); +/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(445); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__["materialize"]; }); -/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(445); +/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(446); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "max", function() { return _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__["max"]; }); -/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(448); +/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(449); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__["merge"]; }); /* harmony import */ var _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(81); @@ -51658,175 +51853,175 @@ __webpack_require__.r(__webpack_exports__); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["flatMap"]; }); -/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(449); +/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(450); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__["mergeMapTo"]; }); -/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(450); +/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(451); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__["mergeScan"]; }); -/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(451); +/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(452); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "min", function() { return _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__["min"]; }); -/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(452); +/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(453); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__["multicast"]; }); /* harmony import */ var _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(41); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observeOn", function() { return _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__["observeOn"]; }); -/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(453); +/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(454); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__["onErrorResumeNext"]; }); -/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(454); +/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(455); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__["pairwise"]; }); -/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(455); +/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(456); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__["partition"]; }); -/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(456); +/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(457); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__["pluck"]; }); -/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(457); +/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(458); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__["publish"]; }); -/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(458); +/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(459); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__["publishBehavior"]; }); -/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(459); +/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(460); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__["publishLast"]; }); -/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(460); +/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(461); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__["publishReplay"]; }); -/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(461); +/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(462); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); -/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(446); +/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(447); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__["reduce"]; }); -/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(462); +/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(463); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); -/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(463); +/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(464); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__["repeatWhen"]; }); -/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(464); +/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(465); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); -/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(465); +/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(466); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); /* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(30); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "refCount", function() { return _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__["refCount"]; }); -/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(466); +/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(467); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__["sample"]; }); -/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(467); +/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(468); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); -/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(447); +/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(448); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__["scan"]; }); -/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(468); +/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(469); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); -/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(469); +/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(470); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); -/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(470); +/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(471); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__["shareReplay"]; }); -/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(471); +/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(472); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); -/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(472); +/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(473); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__["skip"]; }); -/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(473); +/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(474); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); -/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(474); +/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(475); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); -/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(475); +/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(476); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__["skipWhile"]; }); -/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(476); +/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(477); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__["startWith"]; }); -/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(477); +/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(478); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__["subscribeOn"]; }); -/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(479); +/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(480); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__["switchAll"]; }); -/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(480); +/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(481); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); -/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(481); +/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(482); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__["switchMapTo"]; }); -/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(429); +/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(430); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); -/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(442); +/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(443); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); -/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(482); +/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(483); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__["takeUntil"]; }); -/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(483); +/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(484); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__["takeWhile"]; }); -/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(484); +/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(485); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); -/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(485); +/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(486); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__["throttle"]; }); -/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(486); +/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(487); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__["throttleTime"]; }); -/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(428); +/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(429); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__["throwIfEmpty"]; }); -/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(487); +/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(488); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__["timeInterval"]; }); -/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(488); +/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(489); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__["timeout"]; }); -/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(489); +/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(490); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__["timeoutWith"]; }); -/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(490); +/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(491); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); -/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(491); +/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(492); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); -/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(492); +/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(493); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "window", function() { return _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__["window"]; }); -/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(493); +/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(494); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); -/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(494); +/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(495); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__["windowTime"]; }); -/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(495); +/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(496); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); -/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(496); +/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(497); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); -/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(497); +/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(498); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__["withLatestFrom"]; }); -/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(498); +/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(499); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__["zip"]; }); -/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(499); +/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(500); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__["zipAll"]; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ @@ -51937,7 +52132,7 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 403 */ +/* 404 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52016,14 +52211,14 @@ var AuditSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 404 */ +/* 405 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return auditTime; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55); -/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(403); +/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(404); /* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(108); /** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ @@ -52039,7 +52234,7 @@ function auditTime(duration, scheduler) { /***/ }), -/* 405 */ +/* 406 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52086,7 +52281,7 @@ var BufferSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 406 */ +/* 407 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52187,7 +52382,7 @@ var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 407 */ +/* 408 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52348,7 +52543,7 @@ function dispatchBufferClose(arg) { /***/ }), -/* 408 */ +/* 409 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52467,7 +52662,7 @@ var BufferToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 409 */ +/* 410 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52560,7 +52755,7 @@ var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 410 */ +/* 411 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52620,7 +52815,7 @@ var CatchSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 411 */ +/* 412 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52636,7 +52831,7 @@ function combineAll(project) { /***/ }), -/* 412 */ +/* 413 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52668,7 +52863,7 @@ function combineLatest() { /***/ }), -/* 413 */ +/* 414 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52688,7 +52883,7 @@ function concat() { /***/ }), -/* 414 */ +/* 415 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52704,13 +52899,13 @@ function concatMap(project, resultSelector) { /***/ }), -/* 415 */ +/* 416 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return concatMapTo; }); -/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(414); +/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(415); /** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ function concatMapTo(innerObservable, resultSelector) { @@ -52720,7 +52915,7 @@ function concatMapTo(innerObservable, resultSelector) { /***/ }), -/* 416 */ +/* 417 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52785,7 +52980,7 @@ var CountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 417 */ +/* 418 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52870,7 +53065,7 @@ var DebounceSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 418 */ +/* 419 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52946,7 +53141,7 @@ function dispatchNext(subscriber) { /***/ }), -/* 419 */ +/* 420 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52996,7 +53191,7 @@ var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 420 */ +/* 421 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53004,7 +53199,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return delay; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(421); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(422); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); /* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(42); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ @@ -53103,7 +53298,7 @@ var DelayMessage = /*@__PURE__*/ (function () { /***/ }), -/* 421 */ +/* 422 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53117,7 +53312,7 @@ function isDate(value) { /***/ }), -/* 422 */ +/* 423 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53263,7 +53458,7 @@ var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 423 */ +/* 424 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53301,7 +53496,7 @@ var DeMaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 424 */ +/* 425 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53377,7 +53572,7 @@ var DistinctSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 425 */ +/* 426 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53448,13 +53643,13 @@ var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 426 */ +/* 427 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return distinctUntilKeyChanged; }); -/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(425); +/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(426); /** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ function distinctUntilKeyChanged(key, compare) { @@ -53464,7 +53659,7 @@ function distinctUntilKeyChanged(key, compare) { /***/ }), -/* 427 */ +/* 428 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53472,9 +53667,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return elementAt; }); /* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(62); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(428); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(419); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(429); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(429); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(420); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(430); /** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ @@ -53496,7 +53691,7 @@ function elementAt(index, defaultValue) { /***/ }), -/* 428 */ +/* 429 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53562,7 +53757,7 @@ function defaultErrorFactory() { /***/ }), -/* 429 */ +/* 430 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53624,7 +53819,7 @@ var TakeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 430 */ +/* 431 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53646,7 +53841,7 @@ function endWith() { /***/ }), -/* 431 */ +/* 432 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53708,7 +53903,7 @@ var EverySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 432 */ +/* 433 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53762,7 +53957,7 @@ var SwitchFirstSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 433 */ +/* 434 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53856,7 +54051,7 @@ var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 434 */ +/* 435 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53968,7 +54163,7 @@ var ExpandSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 435 */ +/* 436 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54006,7 +54201,7 @@ var FinallySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 436 */ +/* 437 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54078,13 +54273,13 @@ var FindValueSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 437 */ +/* 438 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return findIndex; }); -/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(436); +/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(437); /** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ function findIndex(predicate, thisArg) { @@ -54094,7 +54289,7 @@ function findIndex(predicate, thisArg) { /***/ }), -/* 438 */ +/* 439 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54102,9 +54297,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "first", function() { return first; }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(429); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(419); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(428); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(430); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(420); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(429); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); /** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -54121,7 +54316,7 @@ function first(predicate, defaultValue) { /***/ }), -/* 439 */ +/* 440 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54158,7 +54353,7 @@ var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 440 */ +/* 441 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54202,7 +54397,7 @@ var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 441 */ +/* 442 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54210,9 +54405,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "last", function() { return last; }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(442); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(428); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(419); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(443); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(429); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(420); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); /** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -54229,7 +54424,7 @@ function last(predicate, defaultValue) { /***/ }), -/* 442 */ +/* 443 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54306,7 +54501,7 @@ var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 443 */ +/* 444 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54345,7 +54540,7 @@ var MapToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 444 */ +/* 445 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54395,13 +54590,13 @@ var MaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 445 */ +/* 446 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(446); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(447); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function max(comparer) { @@ -54414,15 +54609,15 @@ function max(comparer) { /***/ }), -/* 446 */ +/* 447 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return reduce; }); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(447); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(442); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(419); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(448); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(443); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(420); /* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(24); /** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ @@ -54443,7 +54638,7 @@ function reduce(accumulator, seed) { /***/ }), -/* 447 */ +/* 448 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54525,7 +54720,7 @@ var ScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 448 */ +/* 449 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54545,7 +54740,7 @@ function merge() { /***/ }), -/* 449 */ +/* 450 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54570,7 +54765,7 @@ function mergeMapTo(innerObservable, resultSelector, concurrent) { /***/ }), -/* 450 */ +/* 451 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54679,13 +54874,13 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 451 */ +/* 452 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(446); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(447); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function min(comparer) { @@ -54698,7 +54893,7 @@ function min(comparer) { /***/ }), -/* 452 */ +/* 453 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54747,7 +54942,7 @@ var MulticastOperator = /*@__PURE__*/ (function () { /***/ }), -/* 453 */ +/* 454 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54837,7 +55032,7 @@ var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 454 */ +/* 455 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54885,7 +55080,7 @@ var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 455 */ +/* 456 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54908,7 +55103,7 @@ function partition(predicate, thisArg) { /***/ }), -/* 456 */ +/* 457 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54948,14 +55143,14 @@ function plucker(props, length) { /***/ }), -/* 457 */ +/* 458 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return publish; }); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(27); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(453); /** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ @@ -54968,14 +55163,14 @@ function publish(selector) { /***/ }), -/* 458 */ +/* 459 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return publishBehavior; }); /* harmony import */ var _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(32); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(453); /** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ @@ -54986,14 +55181,14 @@ function publishBehavior(value) { /***/ }), -/* 459 */ +/* 460 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return publishLast; }); /* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(50); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(453); /** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ @@ -55004,14 +55199,14 @@ function publishLast() { /***/ }), -/* 460 */ +/* 461 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return publishReplay; }); /* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(33); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(453); /** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ @@ -55027,7 +55222,7 @@ function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { /***/ }), -/* 461 */ +/* 462 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55054,7 +55249,7 @@ function race() { /***/ }), -/* 462 */ +/* 463 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55119,7 +55314,7 @@ var RepeatSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 463 */ +/* 464 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55213,7 +55408,7 @@ var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 464 */ +/* 465 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55266,7 +55461,7 @@ var RetrySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 465 */ +/* 466 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55352,7 +55547,7 @@ var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 466 */ +/* 467 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55407,7 +55602,7 @@ var SampleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 467 */ +/* 468 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55467,7 +55662,7 @@ function dispatchNotification(state) { /***/ }), -/* 468 */ +/* 469 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55590,13 +55785,13 @@ var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 469 */ +/* 470 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "share", function() { return share; }); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(452); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(453); /* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(27); /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ @@ -55613,7 +55808,7 @@ function share() { /***/ }), -/* 470 */ +/* 471 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55682,7 +55877,7 @@ function shareReplayOperator(_a) { /***/ }), -/* 471 */ +/* 472 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55762,7 +55957,7 @@ var SingleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 472 */ +/* 473 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55804,7 +55999,7 @@ var SkipSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 473 */ +/* 474 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55866,7 +56061,7 @@ var SkipLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 474 */ +/* 475 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55923,7 +56118,7 @@ var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 475 */ +/* 476 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55979,7 +56174,7 @@ var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 476 */ +/* 477 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56008,13 +56203,13 @@ function startWith() { /***/ }), -/* 477 */ +/* 478 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return subscribeOn; }); -/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(478); +/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(479); /** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ function subscribeOn(scheduler, delay) { @@ -56039,7 +56234,7 @@ var SubscribeOnOperator = /*@__PURE__*/ (function () { /***/ }), -/* 478 */ +/* 479 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56103,13 +56298,13 @@ var SubscribeOnObservable = /*@__PURE__*/ (function (_super) { /***/ }), -/* 479 */ +/* 480 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return switchAll; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(480); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(481); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(25); /** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ @@ -56121,7 +56316,7 @@ function switchAll() { /***/ }), -/* 480 */ +/* 481 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56209,13 +56404,13 @@ var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 481 */ +/* 482 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return switchMapTo; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(480); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(481); /** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ function switchMapTo(innerObservable, resultSelector) { @@ -56225,7 +56420,7 @@ function switchMapTo(innerObservable, resultSelector) { /***/ }), -/* 482 */ +/* 483 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56273,7 +56468,7 @@ var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 483 */ +/* 484 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56341,7 +56536,7 @@ var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 484 */ +/* 485 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56429,7 +56624,7 @@ var TapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 485 */ +/* 486 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56531,7 +56726,7 @@ var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 486 */ +/* 487 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56540,7 +56735,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); -/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(485); +/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(486); /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ @@ -56629,7 +56824,7 @@ function dispatchNext(arg) { /***/ }), -/* 487 */ +/* 488 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56637,7 +56832,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return timeInterval; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimeInterval", function() { return TimeInterval; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(447); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(448); /* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); /* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(66); /** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ @@ -56673,7 +56868,7 @@ var TimeInterval = /*@__PURE__*/ (function () { /***/ }), -/* 488 */ +/* 489 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56681,7 +56876,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return timeout; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55); /* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(64); -/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(489); +/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(490); /* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(49); /** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ @@ -56698,7 +56893,7 @@ function timeout(due, scheduler) { /***/ }), -/* 489 */ +/* 490 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56706,7 +56901,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return timeoutWith; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(421); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(422); /* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(90); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_innerSubscribe PURE_IMPORTS_END */ @@ -56777,7 +56972,7 @@ var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 490 */ +/* 491 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56807,13 +57002,13 @@ var Timestamp = /*@__PURE__*/ (function () { /***/ }), -/* 491 */ +/* 492 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return toArray; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(446); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(447); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function toArrayReducer(arr, item, index) { @@ -56830,7 +57025,7 @@ function toArray() { /***/ }), -/* 492 */ +/* 493 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56908,7 +57103,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 493 */ +/* 494 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56998,7 +57193,7 @@ var WindowCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 494 */ +/* 495 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57168,7 +57363,7 @@ function dispatchWindowClose(state) { /***/ }), -/* 495 */ +/* 496 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57311,7 +57506,7 @@ var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 496 */ +/* 497 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57408,7 +57603,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 497 */ +/* 498 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57503,7 +57698,7 @@ var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 498 */ +/* 499 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57525,7 +57720,7 @@ function zip() { /***/ }), -/* 499 */ +/* 500 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57541,7 +57736,7 @@ function zipAll(project) { /***/ }), -/* 500 */ +/* 501 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57550,7 +57745,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(163); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(144); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(146); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(501); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(280); /* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(502); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } @@ -57632,159 +57827,6 @@ function toArray(value) { return Array.isArray(value) ? value : [value]; } -/***/ }), -/* 501 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "renderProjectsTree", function() { return renderProjectsTree; }); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(113); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); -/* - * 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 projectKey = Symbol('__project'); -function renderProjectsTree(rootPath, projects) { - const projectsTree = buildProjectsTree(rootPath, projects); - return treeToString(createTreeStructure(projectsTree)); -} - -function treeToString(tree) { - return [tree.name].concat(childrenToStrings(tree.children, '')).join('\n'); -} - -function childrenToStrings(tree, treePrefix) { - if (tree === undefined) { - return []; - } - - let strings = []; - tree.forEach((node, index) => { - const isLastNode = tree.length - 1 === index; - const nodePrefix = isLastNode ? '└── ' : '├── '; - const childPrefix = isLastNode ? ' ' : '│ '; - const childrenPrefix = treePrefix + childPrefix; - strings.push(`${treePrefix}${nodePrefix}${node.name}`); - strings = strings.concat(childrenToStrings(node.children, childrenPrefix)); - }); - return strings; -} - -function createTreeStructure(tree) { - let name; - const children = []; - - for (const [dir, project] of tree.entries()) { - // This is a leaf node (aka a project) - if (typeof project === 'string') { - name = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(project); - continue; - } // If there's only one project and the key indicates it's a leaf node, we - // know that we're at a package folder that contains a package.json, so we - // "inline it" so we don't get unnecessary levels, i.e. we'll just see - // `foo` instead of `foo -> foo`. - - - if (project.size === 1 && project.has(projectKey)) { - const projectName = project.get(projectKey); - children.push({ - children: [], - name: dirOrProjectName(dir, projectName) - }); - continue; - } - - const subtree = createTreeStructure(project); // If the name is specified, we know there's a package at the "root" of the - // subtree itself. - - if (subtree.name !== undefined) { - const projectName = subtree.name; - children.push({ - children: subtree.children, - name: dirOrProjectName(dir, projectName) - }); - continue; - } // Special-case whenever we have one child, so we don't get unnecessary - // folders in the output. E.g. instead of `foo -> bar -> baz` we get - // `foo/bar/baz` instead. - - - if (subtree.children && subtree.children.length === 1) { - const child = subtree.children[0]; - const newName = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(path__WEBPACK_IMPORTED_MODULE_1___default.a.join(dir.toString(), child.name)); - children.push({ - children: child.children, - name: newName - }); - continue; - } - - children.push({ - children: subtree.children, - name: chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(dir.toString()) - }); - } - - return { - name, - children - }; -} - -function dirOrProjectName(dir, projectName) { - return dir === projectName ? chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(dir) : chalk__WEBPACK_IMPORTED_MODULE_0___default.a`{dim ${dir.toString()} ({reset.green ${projectName}})}`; -} - -function buildProjectsTree(rootPath, projects) { - const tree = new Map(); - - for (const project of projects.values()) { - if (rootPath === project.path) { - tree.set(projectKey, project.name); - } else { - const relativeProjectPath = path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(rootPath, project.path); - addProjectToTree(tree, relativeProjectPath.split(path__WEBPACK_IMPORTED_MODULE_1___default.a.sep), project); - } - } - - return tree; -} - -function addProjectToTree(tree, pathParts, project) { - if (pathParts.length === 0) { - tree.set(projectKey, project.name); - } else { - const [currentDir, ...rest] = pathParts; - - if (!tree.has(currentDir)) { - tree.set(currentDir, new Map()); - } - - const subtree = tree.get(currentDir); - addProjectToTree(subtree, rest, project); - } -} - /***/ }), /* 502 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { @@ -57796,7 +57838,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(503); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(361); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(362); /* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(275); /* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(146); @@ -58088,7 +58130,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return buildProductionProjects; }); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(509); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(281); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(282); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); @@ -58239,7 +58281,7 @@ const os = __webpack_require__(121); const pAll = __webpack_require__(510); const arrify = __webpack_require__(512); const globby = __webpack_require__(513); -const isGlob = __webpack_require__(294); +const isGlob = __webpack_require__(295); const cpFile = __webpack_require__(713); const junk = __webpack_require__(723); const CpyError = __webpack_require__(724); @@ -58957,7 +58999,7 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); var globParent = __webpack_require__(521); -var isGlob = __webpack_require__(294); +var isGlob = __webpack_require__(295); var micromatch = __webpack_require__(524); var GLOBSTAR = '**'; /** @@ -59145,7 +59187,7 @@ module.exports = function globParent(str) { * Licensed under the MIT License. */ -var isExtglob = __webpack_require__(295); +var isExtglob = __webpack_require__(296); module.exports = function isGlob(str) { if (typeof str !== 'string' || str === '') { @@ -82800,7 +82842,7 @@ exports.flatten = flatten; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var merge2 = __webpack_require__(284); +var merge2 = __webpack_require__(285); /** * Merge multiple streams and propagate their errors into one stream in parallel. */ diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 8ffd86b84bf76..944fcf5998637 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -4,6 +4,9 @@ "version": "1.0.0", "license": "Apache-2.0", "private": true, + "kibana": { + "devOnly": true + }, "scripts": { "build": "webpack", "kbn:watch": "webpack --watch --progress", diff --git a/packages/kbn-pm/src/commands/bootstrap.ts b/packages/kbn-pm/src/commands/bootstrap.ts index 0fa3f355ae9d6..9a8048d6fd106 100644 --- a/packages/kbn-pm/src/commands/bootstrap.ts +++ b/packages/kbn-pm/src/commands/bootstrap.ts @@ -26,7 +26,7 @@ import { ICommand } from './'; import { getAllChecksums } from '../utils/project_checksums'; import { BootstrapCacheFile } from '../utils/bootstrap_cache_file'; import { readYarnLock } from '../utils/yarn_lock'; -import { validateYarnLock } from '../utils/validate_yarn_lock'; +import { validateDependencies } from '../utils/validate_dependencies'; export const BootstrapCommand: ICommand = { description: 'Install dependencies and crosslink projects', @@ -59,7 +59,7 @@ export const BootstrapCommand: ICommand = { const yarnLock = await readYarnLock(kbn); if (options.validate) { - await validateYarnLock(kbn, yarnLock); + await validateDependencies(kbn, yarnLock); } await linkProjectExecutables(projects, projectGraph); diff --git a/packages/kbn-pm/src/utils/project.ts b/packages/kbn-pm/src/utils/project.ts index 8f45df52c7a2f..4e4d76544aa35 100644 --- a/packages/kbn-pm/src/utils/project.ts +++ b/packages/kbn-pm/src/utils/project.ts @@ -153,6 +153,10 @@ export class Project { return (this.json.kibana && this.json.kibana.clean) || {}; } + public isFlaggedAsDevOnly() { + return !!(this.json.kibana && this.json.kibana.devOnly); + } + public hasScript(name: string) { return name in this.scripts; } diff --git a/packages/kbn-pm/src/utils/projects_tree.ts b/packages/kbn-pm/src/utils/projects_tree.ts index c7a13ce2de348..4ba000bc1b158 100644 --- a/packages/kbn-pm/src/utils/projects_tree.ts +++ b/packages/kbn-pm/src/utils/projects_tree.ts @@ -29,7 +29,7 @@ export function renderProjectsTree(rootPath: string, projects: Map {} -function treeToString(tree: ITree) { +export function treeToString(tree: ITree) { return [tree.name].concat(childrenToStrings(tree.children, '')).join('\n'); } diff --git a/packages/kbn-pm/src/utils/validate_yarn_lock.ts b/packages/kbn-pm/src/utils/validate_dependencies.ts similarity index 77% rename from packages/kbn-pm/src/utils/validate_yarn_lock.ts rename to packages/kbn-pm/src/utils/validate_dependencies.ts index ec853a3a958fb..045d6332dcc29 100644 --- a/packages/kbn-pm/src/utils/validate_yarn_lock.ts +++ b/packages/kbn-pm/src/utils/validate_dependencies.ts @@ -20,14 +20,16 @@ // @ts-expect-error published types are useless import { stringify as stringifyLockfile } from '@yarnpkg/lockfile'; import dedent from 'dedent'; +import chalk from 'chalk'; import { writeFile } from './fs'; import { Kibana } from './kibana'; import { YarnLock } from './yarn_lock'; import { log } from './log'; import { Project } from './project'; +import { ITree, treeToString } from './projects_tree'; -export async function validateYarnLock(kbn: Kibana, yarnLock: YarnLock) { +export async function validateDependencies(kbn: Kibana, yarnLock: YarnLock) { // look through all of the packages in the yarn.lock file to see if // we have accidentally installed multiple lodash v4 versions const lodash4Versions = new Set(); @@ -157,5 +159,45 @@ export async function validateYarnLock(kbn: Kibana, yarnLock: YarnLock) { process.exit(1); } + // look for packages that have the the `kibana.devOnly` flag in their package.json + // and make sure they aren't included in the production dependencies of Kibana + const devOnlyProjectsInProduction = getDevOnlyProductionDepsTree(kbn, 'kibana'); + if (devOnlyProjectsInProduction) { + log.error(dedent` + Some of the packages in the production dependency chain for Kibana and X-Pack are + flagged with "kibana.devOnly" in their package.json. Please check changes made to + packages and their dependencies to ensure they don't end up in production. + + The devOnly dependencies that are being dependend on in production are: + + ${treeToString(devOnlyProjectsInProduction).split('\n').join('\n ')} + `); + + process.exit(1); + } + log.success('yarn.lock analysis completed without any issues'); } + +function getDevOnlyProductionDepsTree(kbn: Kibana, projectName: string) { + const project = kbn.getProject(projectName); + const childProjectNames = [ + ...Object.keys(project.productionDependencies).filter((name) => kbn.hasProject(name)), + ...(projectName === 'kibana' ? ['x-pack'] : []), + ]; + + const children = childProjectNames + .map((n) => getDevOnlyProductionDepsTree(kbn, n)) + .filter((t): t is ITree => !!t); + + if (!children.length && !project.isFlaggedAsDevOnly()) { + return; + } + + const tree: ITree = { + name: project.isFlaggedAsDevOnly() ? chalk.red.bold(projectName) : projectName, + children, + }; + + return tree; +} diff --git a/packages/kbn-release-notes/package.json b/packages/kbn-release-notes/package.json index 268530c22399a..e3306b7a54917 100644 --- a/packages/kbn-release-notes/package.json +++ b/packages/kbn-release-notes/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "license": "Apache-2.0", "main": "target/index.js", + "kibana": { + "devOnly": true + }, "scripts": { "kbn:bootstrap": "tsc", "kbn:watch": "tsc --watch" diff --git a/packages/kbn-std/package.json b/packages/kbn-std/package.json index a931dd3f3154d..8a5e885c456cd 100644 --- a/packages/kbn-std/package.json +++ b/packages/kbn-std/package.json @@ -9,12 +9,12 @@ "build": "tsc", "kbn:bootstrap": "yarn build" }, + "dependencies": { + "lodash": "^4.17.20" + }, "devDependencies": { + "@kbn/utility-types": "1.0.0", "typescript": "4.0.2", "tsd": "^0.13.1" - }, - "dependencies": { - "@kbn/utility-types": "1.0.0", - "lodash": "^4.17.20" } } diff --git a/packages/kbn-storybook/lib/default_config.ts b/packages/kbn-storybook/lib/default_config.ts index 1fad9e2a3e087..dc2647b7b5757 100644 --- a/packages/kbn-storybook/lib/default_config.ts +++ b/packages/kbn-storybook/lib/default_config.ts @@ -20,7 +20,12 @@ import { StorybookConfig } from '@storybook/core/types'; export const defaultConfig: StorybookConfig = { - addons: ['@kbn/storybook/preset', '@storybook/addon-knobs', '@storybook/addon-essentials'], + addons: [ + '@kbn/storybook/preset', + '@storybook/addon-a11y', + '@storybook/addon-knobs', + '@storybook/addon-essentials', + ], stories: ['../**/*.stories.tsx'], typescript: { reactDocgen: false, diff --git a/packages/kbn-storybook/package.json b/packages/kbn-storybook/package.json index 58359159e950d..cf0bb1262ea73 100644 --- a/packages/kbn-storybook/package.json +++ b/packages/kbn-storybook/package.json @@ -4,34 +4,13 @@ "private": true, "license": "Apache-2.0", "main": "./target/index.js", - "dependencies": { + "devDependencies": { "@kbn/dev-utils": "1.0.0", - "@storybook/addon-actions": "^6.0.16", - "@storybook/addon-essentials": "^6.0.16", - "@storybook/addon-knobs": "^6.0.16", - "@storybook/addon-storyshots": "^6.0.16", - "@storybook/core": "^6.0.16", - "@storybook/react": "^6.0.16", - "@storybook/theming": "^6.0.16", "@types/loader-utils": "^1.1.3", - "@types/webpack": "^4.41.3", - "@types/webpack-env": "^1.15.2", - "@types/webpack-merge": "^4.1.5", - "@kbn/utils": "1.0.0", - "babel-loader": "^8.0.6", - "copy-webpack-plugin": "^6.0.2", - "fast-glob": "2.2.7", - "glob-watcher": "5.0.3", - "jest-specific-snapshot": "2.0.0", - "jest-styled-components": "^7.0.2", - "mkdirp": "0.5.1", - "mini-css-extract-plugin": "0.8.0", - "normalize-path": "^3.0.0", - "react-docgen-typescript-loader": "^3.1.1", - "rxjs": "^6.5.5", - "serve-static": "1.14.1", - "styled-components": "^5.1.0", - "webpack": "^4.41.5" + "@types/webpack-merge": "^4.1.5" + }, + "kibana": { + "devOnly": true }, "scripts": { "build": "tsc", diff --git a/packages/kbn-storybook/yarn.lock b/packages/kbn-storybook/yarn.lock deleted file mode 120000 index 3f82ebc9cdbae..0000000000000 --- a/packages/kbn-storybook/yarn.lock +++ /dev/null @@ -1 +0,0 @@ -../../yarn.lock \ No newline at end of file diff --git a/packages/kbn-telemetry-tools/package.json b/packages/kbn-telemetry-tools/package.json index 4318cbcf2ec4e..cda2998901d56 100644 --- a/packages/kbn-telemetry-tools/package.json +++ b/packages/kbn-telemetry-tools/package.json @@ -4,6 +4,9 @@ "license": "Apache-2.0", "main": "./target/index.js", "private": true, + "kibana": { + "devOnly": true + }, "scripts": { "build": "babel src --out-dir target --delete-dir-on-start --extensions .ts --source-maps=inline", "kbn:bootstrap": "yarn build", diff --git a/packages/kbn-test-subj-selector/package.json b/packages/kbn-test-subj-selector/package.json index 82a26dc4807be..b823c68f9560b 100755 --- a/packages/kbn-test-subj-selector/package.json +++ b/packages/kbn-test-subj-selector/package.json @@ -5,5 +5,8 @@ "main": "index.js", "keywords": [], "author": "Spencer Alger ", - "license": "Apache-2.0" + "license": "Apache-2.0", + "kibana": { + "devOnly": true + } } diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index 8422c34c9ed08..24096a41a5fdd 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -9,6 +9,9 @@ "kbn:bootstrap": "yarn build", "kbn:watch": "yarn build --watch" }, + "kibana": { + "devOnly": true + }, "devDependencies": { "@babel/cli": "^7.10.5", "@jest/types": "^26.5.2", diff --git a/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/args.test.js.snap b/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/args.test.js.snap index 809b635369a39..cd3174d13c3e6 100644 --- a/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/args.test.js.snap +++ b/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/args.test.js.snap @@ -26,6 +26,7 @@ Object { "debug": true, "esFrom": "snapshot", "extraKbnOpts": undefined, + "useDefaultConfig": true, } `; @@ -35,6 +36,7 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "useDefaultConfig": true, } `; @@ -49,6 +51,7 @@ Object { "extraKbnOpts": Object { "server.foo": "bar", }, + "useDefaultConfig": true, } `; @@ -59,6 +62,7 @@ Object { "esFrom": "snapshot", "extraKbnOpts": undefined, "quiet": true, + "useDefaultConfig": true, } `; @@ -69,6 +73,7 @@ Object { "esFrom": "snapshot", "extraKbnOpts": undefined, "silent": true, + "useDefaultConfig": true, } `; @@ -78,6 +83,7 @@ Object { "createLogger": [Function], "esFrom": "source", "extraKbnOpts": undefined, + "useDefaultConfig": true, } `; @@ -87,6 +93,7 @@ Object { "createLogger": [Function], "esFrom": "source", "extraKbnOpts": undefined, + "useDefaultConfig": true, } `; @@ -97,6 +104,7 @@ Object { "esFrom": "snapshot", "extraKbnOpts": undefined, "installDir": "foo", + "useDefaultConfig": true, } `; @@ -106,6 +114,7 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "useDefaultConfig": true, "verbose": true, } `; @@ -116,5 +125,6 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "useDefaultConfig": true, } `; diff --git a/packages/kbn-test/src/functional_tests/cli/start_servers/args.js b/packages/kbn-test/src/functional_tests/cli/start_servers/args.js index e604e86de8b3a..2b32726557ba3 100644 --- a/packages/kbn-test/src/functional_tests/cli/start_servers/args.js +++ b/packages/kbn-test/src/functional_tests/cli/start_servers/args.js @@ -75,7 +75,8 @@ export function displayHelp() { export function processOptions(userOptions, defaultConfigPath) { validateOptions(userOptions); - const config = userOptions.config || defaultConfigPath; + const useDefaultConfig = !userOptions.config; + const config = useDefaultConfig ? defaultConfigPath : userOptions.config; if (!config) { throw new Error(`functional_tests_server: config is required`); @@ -100,6 +101,7 @@ export function processOptions(userOptions, defaultConfigPath) { return { ...userOptions, config: resolve(config), + useDefaultConfig, createLogger, extraKbnOpts: userOptions._, }; diff --git a/packages/kbn-test/src/functional_tests/tasks.js b/packages/kbn-test/src/functional_tests/tasks.js index 7d4fc84d47bda..c2833cbbda332 100644 --- a/packages/kbn-test/src/functional_tests/tasks.js +++ b/packages/kbn-test/src/functional_tests/tasks.js @@ -36,6 +36,13 @@ import { readConfigFile } from '../functional_test_runner/lib'; const makeSuccessMessage = (options) => { const installDirFlag = options.installDir ? ` --kibana-install-dir=${options.installDir}` : ''; + const configPaths = Array.isArray(options.config) ? options.config : [options.config]; + const pathsMessage = options.useDefaultConfig + ? '' + : configPaths + .map((path) => relative(process.cwd(), path)) + .map((path) => ` --config ${path}`) + .join(''); return ( '\n\n' + @@ -43,7 +50,7 @@ const makeSuccessMessage = (options) => { Elasticsearch and Kibana are ready for functional testing. Start the functional tests in another terminal session by running this command from this directory: - node ${relative(process.cwd(), KIBANA_FTR_SCRIPT)}${installDirFlag} + node ${relative(process.cwd(), KIBANA_FTR_SCRIPT)}${installDirFlag}${pathsMessage} ` + '\n\n' ); diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index 21d25311420ca..d954ae0823caf 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "@babel/core": "^7.11.6", - "@elastic/eui": "29.3.0", + "@elastic/eui": "29.5.0", "@kbn/babel-preset": "1.0.0", "@kbn/optimizer": "1.0.0", "babel-loader": "^8.0.6", diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 0a154c537fec1..980d9d02317b6 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@elastic/charts": "23.2.1", - "@elastic/eui": "29.3.0", + "@elastic/eui": "29.5.0", "@elastic/numeral": "^2.5.0", "@kbn/i18n": "1.0.0", "@kbn/monaco": "1.0.0", diff --git a/packages/kbn-utility-types/package.json b/packages/kbn-utility-types/package.json index d1d7a1c0397cf..6b531efcebace 100644 --- a/packages/kbn-utility-types/package.json +++ b/packages/kbn-utility-types/package.json @@ -5,6 +5,9 @@ "license": "Apache-2.0", "main": "target", "types": "target/index.d.ts", + "kibana": { + "devOnly": true + }, "scripts": { "build": "tsc", "kbn:bootstrap": "tsc", diff --git a/renovate.json5 b/renovate.json5 index 17391c2f83827..84f8da2a72456 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -2,9 +2,12 @@ extends: [ 'config:base', ], - includePaths: [ - 'package.json', - 'x-pack/package.json', + ignorePaths: [ + '**/__fixtures__/**', + '**/fixtures/**', + ], + enabledManagers: [ + 'npm', ], baseBranches: [ 'master', diff --git a/scripts/find_plugin_circular_deps.js b/scripts/find_plugin_circular_deps.js new file mode 100644 index 0000000000000..6b0661cb841b4 --- /dev/null +++ b/scripts/find_plugin_circular_deps.js @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +require('../src/setup_node_env'); +require('../src/dev/run_find_plugin_circular_deps'); diff --git a/scripts/find_plugins_without_ts_refs.js b/scripts/find_plugins_without_ts_refs.js new file mode 100644 index 0000000000000..5f543a045f739 --- /dev/null +++ b/scripts/find_plugins_without_ts_refs.js @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +require('../src/setup_node_env'); +require('../src/dev/run_find_plugins_without_ts_refs'); diff --git a/src/cli_keystore/add.js b/src/cli_keystore/add.js index 232392f34c63b..d88256da1aa59 100644 --- a/src/cli_keystore/add.js +++ b/src/cli_keystore/add.js @@ -59,7 +59,15 @@ export async function add(keystore, key, options = {}) { value = await question(`Enter value for ${key}`, { mask: '*' }); } - keystore.add(key, value.trim()); + const parsedValue = value.trim(); + let parsedJsonValue; + try { + parsedJsonValue = JSON.parse(parsedValue); + } catch { + // noop, only treat value as json if it parses as JSON + } + + keystore.add(key, parsedJsonValue ?? parsedValue); keystore.save(); } diff --git a/src/cli_keystore/add.test.js b/src/cli_keystore/add.test.js index f1adee8879bc2..ba381ca2f3e14 100644 --- a/src/cli_keystore/add.test.js +++ b/src/cli_keystore/add.test.js @@ -129,6 +129,17 @@ describe('Kibana keystore', () => { expect(keystore.data.foo).toEqual('bar'); }); + it('parses JSON values', async () => { + prompt.question.returns(Promise.resolve('["bar"]\n')); + + const keystore = new Keystore('/data/test.keystore'); + sandbox.stub(keystore, 'save'); + + await add(keystore, 'foo'); + + expect(keystore.data.foo).toEqual(['bar']); + }); + it('persists updated keystore', async () => { prompt.question.returns(Promise.resolve('bar\n')); diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index 6a21dcb1b0686..49b962670220c 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -966,7 +966,7 @@ It means that NP plugin artifacts tend to have a bigger size than the legacy pla To understand the current size of your plugin artifact, run `@kbn/optimizer` as ```bash -node scripts/build_kibana_platform_plugins.js --dist --no-examples +node scripts/build_kibana_platform_plugins.js --dist --profile --focus=my_plugin ``` and check the output in the `target` sub-folder of your plugin folder diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx index 0d08f6f3007b0..4d54d4831698b 100644 --- a/src/core/public/application/application_service.tsx +++ b/src/core/public/application/application_service.tsx @@ -242,11 +242,17 @@ export class ApplicationService { appId, { path, state, replace = false }: NavigateToAppOptions = {} ) => { - if (await this.shouldNavigate(overlays)) { + const currentAppId = this.currentAppId$.value; + const navigatingToSameApp = currentAppId === appId; + const shouldNavigate = navigatingToSameApp ? true : await this.shouldNavigate(overlays); + + if (shouldNavigate) { if (path === undefined) { path = applications$.value.get(appId)?.defaultPath; } - this.appInternalStates.delete(this.currentAppId$.value!); + if (!navigatingToSameApp) { + this.appInternalStates.delete(this.currentAppId$.value!); + } this.navigate!(getAppUrl(availableMounters, appId, path), state, replace); this.currentAppId$.next(appId); } 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 d28486928b7e2..82933576bc493 100644 --- a/src/core/public/application/integration_tests/application_service.test.tsx +++ b/src/core/public/application/integration_tests/application_service.test.tsx @@ -258,6 +258,34 @@ describe('ApplicationService', () => { expect(history.entries.length).toEqual(2); expect(history.entries[1].pathname).toEqual('/app/app1'); }); + + it('does not trigger navigation check if navigating to the current app', async () => { + startDeps.overlays.openConfirm.mockResolvedValue(false); + + const { register } = service.setup(setupDeps); + + register(Symbol(), { + id: 'app1', + title: 'App1', + mount: ({ onAppLeave }: AppMountParameters) => { + onAppLeave((actions) => actions.confirm('confirmation-message', 'confirmation-title')); + return () => undefined; + }, + }); + + const { navigateToApp, getComponent } = await service.start(startDeps); + + update = createRenderer(getComponent()); + + await act(async () => { + await navigate('/app/app1'); + await navigateToApp('app1', { path: '/internal-path' }); + }); + + expect(startDeps.overlays.openConfirm).not.toHaveBeenCalled(); + expect(history.entries.length).toEqual(3); + expect(history.entries[2].pathname).toEqual('/app/app1/internal-path'); + }); }); describe('registering action menus', () => { @@ -331,6 +359,48 @@ describe('ApplicationService', () => { expect(await getValue(currentActionMenu$)).toBe(mounter2); }); + it('does not update the observable value when navigating to the current app', async () => { + const { register } = service.setup(setupDeps); + + let initialMount = true; + register(Symbol(), { + id: 'app1', + title: 'App1', + mount: async ({ setHeaderActionMenu }: AppMountParameters) => { + if (initialMount) { + setHeaderActionMenu(mounter1); + initialMount = false; + } + return () => undefined; + }, + }); + + const { navigateToApp, getComponent, currentActionMenu$ } = await service.start(startDeps); + update = createRenderer(getComponent()); + + let mountedMenuCount = 0; + currentActionMenu$.subscribe(() => { + mountedMenuCount++; + }); + + await act(async () => { + await navigateToApp('app1'); + await flushPromises(); + }); + + expect(await getValue(currentActionMenu$)).toBe(mounter1); + + await act(async () => { + await navigateToApp('app1'); + await flushPromises(); + }); + + expect(await getValue(currentActionMenu$)).toBe(mounter1); + + // there is an initial 'undefined' emission + expect(mountedMenuCount).toBe(2); + }); + it('updates the observable value to undefined when switching to an application without action menu', async () => { const { register } = service.setup(setupDeps); diff --git a/src/core/public/application/integration_tests/router.test.tsx b/src/core/public/application/integration_tests/router.test.tsx index d982136422268..8ae559742c0da 100644 --- a/src/core/public/application/integration_tests/router.test.tsx +++ b/src/core/public/application/integration_tests/router.test.tsx @@ -107,7 +107,7 @@ describe('AppRouter', () => { expect(app1.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app1 html: App 1
" @@ -119,7 +119,7 @@ describe('AppRouter', () => { expect(app1Unmount).toHaveBeenCalled(); expect(app2.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app2 html:
App 2
" @@ -133,7 +133,7 @@ describe('AppRouter', () => { expect(standardApp.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app1 html: App 1
" @@ -145,7 +145,7 @@ describe('AppRouter', () => { expect(standardAppUnmount).toHaveBeenCalled(); expect(chromelessApp.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-a/path html:
Chromeless A
" @@ -157,7 +157,7 @@ describe('AppRouter', () => { expect(chromelessAppUnmount).toHaveBeenCalled(); expect(standardApp.mounter.mount).toHaveBeenCalledTimes(2); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app1 html: App 1
" @@ -171,7 +171,7 @@ describe('AppRouter', () => { expect(chromelessAppA.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-a/path html:
Chromeless A
" @@ -183,7 +183,7 @@ describe('AppRouter', () => { expect(chromelessAppAUnmount).toHaveBeenCalled(); expect(chromelessAppB.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-b/path html:
Chromeless B
" @@ -195,7 +195,7 @@ describe('AppRouter', () => { expect(chromelessAppBUnmount).toHaveBeenCalled(); expect(chromelessAppA.mounter.mount).toHaveBeenCalledTimes(2); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-a/path html:
Chromeless A
" diff --git a/src/core/public/chrome/ui/__snapshots__/loading_indicator.test.tsx.snap b/src/core/public/chrome/ui/__snapshots__/loading_indicator.test.tsx.snap index e6bf7e898d8c4..10e6e9befe4f9 100644 --- a/src/core/public/chrome/ui/__snapshots__/loading_indicator.test.tsx.snap +++ b/src/core/public/chrome/ui/__snapshots__/loading_indicator.test.tsx.snap @@ -1,19 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`kbnLoadingIndicator is hidden by default 1`] = ` - `; exports[`kbnLoadingIndicator is visible when loadingCount is > 0 1`] = ` - `; diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap index 47b8aaefaf86a..cf734f33cc3e4 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -585,7 +585,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` className="euiListGroupItem__icon" type="home" > -
@@ -671,7 +671,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` size="m" type="arrowRight" > -
-
-
@@ -1215,7 +1215,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` size="m" type="arrowRight" > -
-
@@ -1466,7 +1466,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` size="m" type="arrowRight" > -
-
@@ -1678,7 +1678,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` size="m" type="arrowRight" > -
-
@@ -1900,7 +1900,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` data-test-subj="collapsible-nav-lock" type="button" > -
@@ -1935,7 +1935,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` className="euiListGroupItem__icon" type="lockOpen" > -
@@ -1994,7 +1994,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` size="m" type="cross" > -
-
@@ -2916,7 +2916,7 @@ exports[`CollapsibleNav renders the default nav 3`] = ` size="m" type="arrowRight" > -
-
@@ -3088,7 +3088,7 @@ exports[`CollapsibleNav renders the default nav 3`] = ` className="euiListGroupItem__icon" type="lock" > -
@@ -3147,7 +3147,7 @@ exports[`CollapsibleNav renders the default nav 3`] = ` size="m" type="cross" > -
, - , ], }, @@ -2821,17 +2818,10 @@ exports[`Header renders 1`] = ` } } href="/" - navLinks$={ + loadingCount$={ BehaviorSubject { "_isScalar": false, - "_value": Array [ - Object { - "baseUrl": "", - "href": "", - "id": "kibana", - "title": "kibana", - }, - ], + "_value": 0, "closed": false, "hasError": false, "isStopped": false, @@ -2873,6 +2863,25 @@ exports[`Header renders 1`] = ` "syncErrorThrown": false, "syncErrorValue": null, }, + ], + "thrownError": null, + } + } + navLinks$={ + BehaviorSubject { + "_isScalar": false, + "_value": Array [ + Object { + "baseUrl": "", + "href": "", + "id": "kibana", + "title": "kibana", + }, + ], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [ Subscriber { "_parentOrParents": null, "_subscriptions": Array [ @@ -2910,66 +2919,6 @@ exports[`Header renders 1`] = ` "syncErrorThrown": false, "syncErrorValue": null, }, - ], - "thrownError": null, - } - } - navigateToApp={[MockFunction]} - > - - - - - - -
- - - + + + + + - - + className="chrHeaderLogo__mark" + > + + + Elastic + + + + + +
@@ -4708,7 +4743,7 @@ exports[`Header renders 1`] = ` size="m" type="help" > -
@@ -4828,7 +4863,7 @@ exports[`Header renders 1`] = ` size="m" type="menu" > -
@@ -5659,7 +5694,7 @@ exports[`Header renders 1`] = ` className="euiListGroupItem__icon" type="home" > -
@@ -5745,7 +5780,7 @@ exports[`Header renders 1`] = ` size="m" type="arrowRight" > -
-
@@ -6002,7 +6037,7 @@ exports[`Header renders 1`] = ` className="euiListGroupItem__icon" type="lock" > -
@@ -6061,7 +6096,7 @@ exports[`Header renders 1`] = ` size="m" type="cross" > -
) => ( + + Elastic + + +); diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index d0b39e362ecb7..7089ec1087271 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -112,8 +112,8 @@ export function Header({ forceNavigation$={observables.forceAppSwitcherNavigation$} navLinks$={observables.navLinks$} navigateToApp={application.navigateToApp} + loadingCount$={observables.loadingCount$} />, - , ], borders: 'none', }, diff --git a/src/core/public/chrome/ui/header/header_logo.scss b/src/core/public/chrome/ui/header/header_logo.scss new file mode 100644 index 0000000000000..f75fd9cfa2466 --- /dev/null +++ b/src/core/public/chrome/ui/header/header_logo.scss @@ -0,0 +1,4 @@ +.chrHeaderLogo__mark { + margin-left: $euiSizeS; + fill: $euiColorGhost; +} diff --git a/src/core/public/chrome/ui/header/header_logo.tsx b/src/core/public/chrome/ui/header/header_logo.tsx index 83e0c52ab3f3a..df961ebb0983f 100644 --- a/src/core/public/chrome/ui/header/header_logo.tsx +++ b/src/core/public/chrome/ui/header/header_logo.tsx @@ -17,13 +17,16 @@ * under the License. */ -import { EuiHeaderLogo } from '@elastic/eui'; +import './header_logo.scss'; import { i18n } from '@kbn/i18n'; import React from 'react'; import useObservable from 'react-use/lib/useObservable'; import { Observable } from 'rxjs'; import Url from 'url'; import { ChromeNavLink } from '../..'; +import { ElasticMark } from './elastic_mark'; +import { HttpStart } from '../../../http'; +import { LoadingIndicator } from '../loading_indicator'; function findClosestAnchor(element: HTMLElement): HTMLAnchorElement | void { let current = element; @@ -90,23 +93,25 @@ interface Props { navLinks$: Observable; forceNavigation$: Observable; navigateToApp: (appId: string) => void; + loadingCount$?: ReturnType; } -export function HeaderLogo({ href, navigateToApp, ...observables }: Props) { +export function HeaderLogo({ href, navigateToApp, loadingCount$, ...observables }: Props) { const forceNavigation = useObservable(observables.forceNavigation$, false); const navLinks = useObservable(observables.navLinks$, []); return ( - onClick(e, forceNavigation, navLinks, navigateToApp)} + className="euiHeaderLogo" href={href} + data-test-subj="logo" aria-label={i18n.translate('core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel', { - defaultMessage: 'Go to home page', + defaultMessage: 'Elastic home', })} > - Elastic - + + + ); } diff --git a/src/core/public/chrome/ui/loading_indicator.test.tsx b/src/core/public/chrome/ui/loading_indicator.test.tsx index ff56ca668ae02..2d45a3d079616 100644 --- a/src/core/public/chrome/ui/loading_indicator.test.tsx +++ b/src/core/public/chrome/ui/loading_indicator.test.tsx @@ -32,7 +32,10 @@ describe('kbnLoadingIndicator', () => { it('is visible when loadingCount is > 0', () => { const wrapper = shallow(); - expect(wrapper.prop('data-test-subj')).toBe('globalLoadingIndicator'); + // Pause the check beyond the 250ms delay that it has + setTimeout(() => { + expect(wrapper.prop('data-test-subj')).toBe('globalLoadingIndicator'); + }, 300); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/src/core/public/chrome/ui/loading_indicator.tsx b/src/core/public/chrome/ui/loading_indicator.tsx index ca3e95f722ec5..25ec52e8dbb58 100644 --- a/src/core/public/chrome/ui/loading_indicator.tsx +++ b/src/core/public/chrome/ui/loading_indicator.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { EuiLoadingSpinner, EuiProgress } from '@elastic/eui'; +import { EuiLoadingSpinner, EuiProgress, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import classNames from 'classnames'; @@ -39,16 +39,26 @@ export class LoadingIndicator extends React.Component { - this.setState({ - visible: count > 0, - }); + if (this.increment > 1) { + clearTimeout(this.timer); + } + this.increment += this.increment; + this.timer = setTimeout(() => { + this.setState({ + visible: count > 0, + }); + }, 250); }); } componentWillUnmount() { if (this.loadingCountSubscription) { + clearTimeout(this.timer); this.loadingCountSubscription.unsubscribe(); this.loadingCountSubscription = undefined; } @@ -67,13 +77,27 @@ export class LoadingIndicator extends React.Component + ) : ( + + ); + + return !this.props.showAsBar ? ( + logo ) : (
Flyout content
"`; +exports[`FlyoutService openFlyout() renders a flyout to the DOM 2`] = `"
Flyout content
"`; exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 1`] = ` Array [ @@ -59,4 +59,4 @@ Array [ ] `; -exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 2`] = `"
Flyout content 2
"`; +exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 2`] = `"
Flyout content 2
"`; diff --git a/src/core/public/overlays/modal/__snapshots__/modal_service.test.tsx.snap b/src/core/public/overlays/modal/__snapshots__/modal_service.test.tsx.snap index aea52eb8e7ab7..7e79725c20307 100644 --- a/src/core/public/overlays/modal/__snapshots__/modal_service.test.tsx.snap +++ b/src/core/public/overlays/modal/__snapshots__/modal_service.test.tsx.snap @@ -31,7 +31,7 @@ Array [ ] `; -exports[`ModalService openConfirm() renders a mountpoint confirm message 2`] = `"
Modal content
"`; +exports[`ModalService openConfirm() renders a mountpoint confirm message 2`] = `"
Modal content
"`; exports[`ModalService openConfirm() renders a string confirm message 1`] = ` Array [ @@ -53,7 +53,7 @@ Array [ ] `; -exports[`ModalService openConfirm() renders a string confirm message 2`] = `"

Some message

"`; +exports[`ModalService openConfirm() renders a string confirm message 2`] = `"

Some message

"`; exports[`ModalService openConfirm() with a currently active confirm replaces the current confirm with the new one 1`] = ` Array [ @@ -145,7 +145,7 @@ Array [ ] `; -exports[`ModalService openModal() renders a modal to the DOM 2`] = `"
Modal content
"`; +exports[`ModalService openModal() renders a modal to the DOM 2`] = `"
Modal content
"`; exports[`ModalService openModal() with a currently active confirm replaces the current confirm with the new one 1`] = ` Array [ diff --git a/src/core/server/audit_trail/audit_trail_service.mock.ts b/src/core/server/audit_trail/audit_trail_service.mock.ts deleted file mode 100644 index 4c9c064840750..0000000000000 --- a/src/core/server/audit_trail/audit_trail_service.mock.ts +++ /dev/null @@ -1,58 +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 type { PublicMethodsOf } from '@kbn/utility-types'; -import { AuditTrailSetup, AuditTrailStart, Auditor } from './types'; -import { AuditTrailService } from './audit_trail_service'; - -const createSetupContractMock = () => { - const mocked: jest.Mocked = { - register: jest.fn(), - }; - return mocked; -}; - -const createAuditorMock = () => { - const mocked: jest.Mocked = { - add: jest.fn(), - withAuditScope: jest.fn(), - }; - return mocked; -}; - -const createStartContractMock = () => { - const mocked: jest.Mocked = { - asScoped: jest.fn(), - }; - mocked.asScoped.mockReturnValue(createAuditorMock()); - return mocked; -}; - -const createServiceMock = (): jest.Mocked> => ({ - setup: jest.fn().mockResolvedValue(createSetupContractMock()), - start: jest.fn().mockResolvedValue(createStartContractMock()), - stop: jest.fn(), -}); - -export const auditTrailServiceMock = { - create: createServiceMock, - createSetupContract: createSetupContractMock, - createStartContract: createStartContractMock, - createAuditorFactory: createStartContractMock, - createAuditor: createAuditorMock, -}; diff --git a/src/core/server/audit_trail/audit_trail_service.test.ts b/src/core/server/audit_trail/audit_trail_service.test.ts deleted file mode 100644 index 63b45b62275b6..0000000000000 --- a/src/core/server/audit_trail/audit_trail_service.test.ts +++ /dev/null @@ -1,99 +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 { AuditTrailService } from './audit_trail_service'; -import { AuditorFactory } from './types'; -import { mockCoreContext } from '../core_context.mock'; -import { httpServerMock } from '../http/http_server.mocks'; - -describe('AuditTrailService', () => { - const coreContext = mockCoreContext.create(); - - describe('#setup', () => { - describe('register', () => { - it('throws if registered the same auditor factory twice', () => { - const auditTrail = new AuditTrailService(coreContext); - const { register } = auditTrail.setup(); - const auditorFactory: AuditorFactory = { - asScoped() { - return { add: () => undefined, withAuditScope: (() => {}) as any }; - }, - }; - register(auditorFactory); - expect(() => register(auditorFactory)).toThrowErrorMatchingInlineSnapshot( - `"An auditor factory has been already registered"` - ); - }); - }); - }); - - describe('#start', () => { - describe('asScoped', () => { - it('initialize every auditor with a request', () => { - const scopedMock = jest.fn(() => ({ add: jest.fn(), withAuditScope: jest.fn() })); - const auditorFactory = { asScoped: scopedMock }; - - const auditTrail = new AuditTrailService(coreContext); - const { register } = auditTrail.setup(); - register(auditorFactory); - - const { asScoped } = auditTrail.start(); - const kibanaRequest = httpServerMock.createKibanaRequest(); - asScoped(kibanaRequest); - - expect(scopedMock).toHaveBeenCalledWith(kibanaRequest); - }); - - it('passes auditable event to an auditor', () => { - const addEventMock = jest.fn(); - const auditorFactory = { - asScoped() { - return { add: addEventMock, withAuditScope: jest.fn() }; - }, - }; - - const auditTrail = new AuditTrailService(coreContext); - const { register } = auditTrail.setup(); - register(auditorFactory); - - const { asScoped } = auditTrail.start(); - const kibanaRequest = httpServerMock.createKibanaRequest(); - const auditor = asScoped(kibanaRequest); - const message = { - type: 'foo', - message: 'bar', - }; - auditor.add(message); - - expect(addEventMock).toHaveBeenLastCalledWith(message); - }); - - describe('return the same auditor instance for the same KibanaRequest', () => { - const auditTrail = new AuditTrailService(coreContext); - auditTrail.setup(); - const { asScoped } = auditTrail.start(); - - const rawRequest1 = httpServerMock.createKibanaRequest(); - const rawRequest2 = httpServerMock.createKibanaRequest(); - expect(asScoped(rawRequest1)).toBe(asScoped(rawRequest1)); - expect(asScoped(rawRequest1)).not.toBe(asScoped(rawRequest2)); - }); - }); - }); -}); diff --git a/src/core/server/audit_trail/audit_trail_service.ts b/src/core/server/audit_trail/audit_trail_service.ts deleted file mode 100644 index f1841858dbc92..0000000000000 --- a/src/core/server/audit_trail/audit_trail_service.ts +++ /dev/null @@ -1,69 +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 { CoreService } from '../../types'; -import { CoreContext } from '../core_context'; -import { Logger } from '../logging'; -import { KibanaRequest, LegacyRequest } from '../http'; -import { ensureRawRequest } from '../http/router'; -import { Auditor, AuditorFactory, AuditTrailSetup, AuditTrailStart } from './types'; - -const defaultAuditorFactory: AuditorFactory = { - asScoped() { - return { - add() {}, - withAuditScope() {}, - }; - }, -}; - -export class AuditTrailService implements CoreService { - private readonly log: Logger; - private auditor: AuditorFactory = defaultAuditorFactory; - private readonly auditors = new WeakMap(); - - constructor(core: CoreContext) { - this.log = core.logger.get('audit_trail'); - } - - setup() { - return { - register: (auditor: AuditorFactory) => { - if (this.auditor !== defaultAuditorFactory) { - throw new Error('An auditor factory has been already registered'); - } - this.auditor = auditor; - this.log.debug('An auditor factory has been registered'); - }, - }; - } - - start() { - return { - asScoped: (request: KibanaRequest) => { - const key = ensureRawRequest(request); - if (!this.auditors.has(key)) { - this.auditors.set(key, this.auditor!.asScoped(request)); - } - return this.auditors.get(key)!; - }, - }; - } - - stop() {} -} diff --git a/src/core/server/audit_trail/types.ts b/src/core/server/audit_trail/types.ts deleted file mode 100644 index b3c1fc3c222fa..0000000000000 --- a/src/core/server/audit_trail/types.ts +++ /dev/null @@ -1,76 +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 { KibanaRequest } from '../http'; - -/** - * Event to audit. - * @public - * - * @remarks - * Not a complete interface. - */ -export interface AuditableEvent { - message: string; - type: string; -} - -/** - * Provides methods to log user actions and access events. - * @public - */ -export interface Auditor { - /** - * Add a record to audit log. - * Service attaches to a log record: - * - metadata about an end-user initiating an operation - * - scope name, if presents - * - * @example - * How to add a record in audit log: - * ```typescript - * router.get({ path: '/my_endpoint', validate: false }, async (context, request, response) => { - * context.core.auditor.withAuditScope('my_plugin_operation'); - * const value = await context.core.elasticsearch.legacy.client.callAsCurrentUser('...'); - * context.core.add({ type: 'operation.type', message: 'perform an operation in ... endpoint' }); - * ``` - */ - add(event: AuditableEvent): void; - /** - * Add a high-level scope name for logged events. - * It helps to identify the root cause of low-level events. - */ - withAuditScope(name: string): void; -} - -/** - * Creates {@link Auditor} instance bound to the current user credentials. - * @public - */ -export interface AuditorFactory { - asScoped(request: KibanaRequest): Auditor; -} - -export interface AuditTrailSetup { - /** - * Register a custom {@link AuditorFactory} implementation. - */ - register(auditor: AuditorFactory): void; -} - -export type AuditTrailStart = AuditorFactory; diff --git a/src/core/server/core_app/integration_tests/default_route_provider_config.test.ts b/src/core/server/core_app/integration_tests/default_route_provider_config.test.ts index 340f45a0a2c18..b7ffefe7005e1 100644 --- a/src/core/server/core_app/integration_tests/default_route_provider_config.test.ts +++ b/src/core/server/core_app/integration_tests/default_route_provider_config.test.ts @@ -24,7 +24,8 @@ const { startES } = kbnTestServer.createTestServers({ }); let esServer: kbnTestServer.TestElasticsearchUtils; -describe('default route provider', () => { +// FLAKY: https://github.com/elastic/kibana/issues/81072 +describe.skip('default route provider', () => { let root: Root; beforeAll(async () => { diff --git a/src/core/server/core_route_handler_context.test.ts b/src/core/server/core_route_handler_context.test.ts index 563e337e6c7e0..d4599d91c1b96 100644 --- a/src/core/server/core_route_handler_context.test.ts +++ b/src/core/server/core_route_handler_context.test.ts @@ -19,41 +19,6 @@ import { CoreRouteHandlerContext } from './core_route_handler_context'; import { coreMock, httpServerMock } from './mocks'; -describe('#auditor', () => { - test('returns the results of coreStart.audiTrail.asScoped', () => { - const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); - const context = new CoreRouteHandlerContext(coreStart, request); - - const auditor = context.auditor; - expect(auditor).toBe(coreStart.auditTrail.asScoped.mock.results[0].value); - }); - - test('lazily created', () => { - const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); - const context = new CoreRouteHandlerContext(coreStart, request); - - expect(coreStart.auditTrail.asScoped).not.toHaveBeenCalled(); - const auditor = context.auditor; - expect(coreStart.auditTrail.asScoped).toHaveBeenCalled(); - expect(auditor).toBeDefined(); - }); - - test('only creates one instance', () => { - const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createInternalStart(); - const context = new CoreRouteHandlerContext(coreStart, request); - - const auditor1 = context.auditor; - const auditor2 = context.auditor; - expect(coreStart.auditTrail.asScoped.mock.calls.length).toBe(1); - const mockResult = coreStart.auditTrail.asScoped.mock.results[0].value; - expect(auditor1).toBe(mockResult); - expect(auditor2).toBe(mockResult); - }); -}); - describe('#elasticsearch', () => { describe('#client', () => { test('returns the results of coreStart.elasticsearch.client.asScoped', () => { diff --git a/src/core/server/core_route_handler_context.ts b/src/core/server/core_route_handler_context.ts index 8a182a523f52c..520c5bd3f685b 100644 --- a/src/core/server/core_route_handler_context.ts +++ b/src/core/server/core_route_handler_context.ts @@ -27,7 +27,6 @@ import { IScopedClusterClient, LegacyScopedClusterClient, } from './elasticsearch'; -import { Auditor } from './audit_trail'; import { InternalUiSettingsServiceStart, IUiSettingsClient } from './ui_settings'; class CoreElasticsearchRouteHandlerContext { @@ -99,8 +98,6 @@ class CoreUiSettingsRouteHandlerContext { } export class CoreRouteHandlerContext { - #auditor?: Auditor; - readonly elasticsearch: CoreElasticsearchRouteHandlerContext; readonly savedObjects: CoreSavedObjectsRouteHandlerContext; readonly uiSettings: CoreUiSettingsRouteHandlerContext; @@ -122,11 +119,4 @@ export class CoreRouteHandlerContext { this.savedObjects ); } - - public get auditor() { - if (this.#auditor == null) { - this.#auditor = this.coreStart.auditTrail.asScoped(this.request); - } - return this.#auditor; - } } diff --git a/src/core/server/elasticsearch/elasticsearch_service.test.ts b/src/core/server/elasticsearch/elasticsearch_service.test.ts index ce82410f6061e..e527fdb915970 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.test.ts @@ -26,7 +26,6 @@ import { configServiceMock, getEnvOptions } from '../config/mocks'; import { CoreContext } from '../core_context'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { httpServiceMock } from '../http/http_service.mock'; -import { auditTrailServiceMock } from '../audit_trail/audit_trail_service.mock'; import { ElasticsearchConfig } from './elasticsearch_config'; import { ElasticsearchService } from './elasticsearch_service'; import { elasticsearchServiceMock } from './elasticsearch_service.mock'; @@ -41,9 +40,6 @@ const configService = configServiceMock.create(); const setupDeps = { http: httpServiceMock.createInternalSetupContract(), }; -const startDeps = { - auditTrail: auditTrailServiceMock.createStartContract(), -}; configService.atPath.mockReturnValue( new BehaviorSubject({ hosts: ['http://1.2.3.4'], @@ -113,7 +109,6 @@ describe('#setup', () => { expect(MockLegacyClusterClient).toHaveBeenCalledWith( expect.objectContaining(customConfig), expect.objectContaining({ context: ['elasticsearch', 'some-custom-type'] }), - expect.any(Function), expect.any(Function) ); }); @@ -260,14 +255,14 @@ describe('#setup', () => { describe('#start', () => { it('throws if called before `setup`', async () => { - expect(() => elasticsearchService.start(startDeps)).rejects.toMatchInlineSnapshot( + expect(() => elasticsearchService.start()).rejects.toMatchInlineSnapshot( `[Error: ElasticsearchService needs to be setup before calling start]` ); }); it('returns elasticsearch client as a part of the contract', async () => { await elasticsearchService.setup(setupDeps); - const startContract = await elasticsearchService.start(startDeps); + const startContract = await elasticsearchService.start(); const client = startContract.client; expect(client.asInternalUser).toBe(mockClusterClientInstance.asInternalUser); @@ -276,7 +271,7 @@ describe('#start', () => { describe('#createClient', () => { it('allows to specify config properties', async () => { await elasticsearchService.setup(setupDeps); - const startContract = await elasticsearchService.start(startDeps); + const startContract = await elasticsearchService.start(); // reset all mocks called during setup phase MockClusterClient.mockClear(); @@ -295,7 +290,7 @@ describe('#start', () => { }); it('creates a new client on each call', async () => { await elasticsearchService.setup(setupDeps); - const startContract = await elasticsearchService.start(startDeps); + const startContract = await elasticsearchService.start(); // reset all mocks called during setup phase MockClusterClient.mockClear(); @@ -310,7 +305,7 @@ describe('#start', () => { it('falls back to elasticsearch default config values if property not specified', async () => { await elasticsearchService.setup(setupDeps); - const startContract = await elasticsearchService.start(startDeps); + const startContract = await elasticsearchService.start(); // reset all mocks called during setup phase MockClusterClient.mockClear(); @@ -347,7 +342,7 @@ describe('#start', () => { describe('#stop', () => { it('stops both legacy and new clients', async () => { await elasticsearchService.setup(setupDeps); - await elasticsearchService.start(startDeps); + await elasticsearchService.start(); await elasticsearchService.stop(); expect(mockLegacyClusterClientInstance.close).toHaveBeenCalledTimes(1); diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index 5d07840e8bda7..a0b9e8c6f2bfb 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.ts @@ -32,7 +32,6 @@ import { import { ClusterClient, ICustomClusterClient, ElasticsearchClientConfig } from './client'; import { ElasticsearchConfig, ElasticsearchConfigType } from './elasticsearch_config'; import { InternalHttpServiceSetup, GetAuthHeaders } from '../http/'; -import { AuditTrailStart, AuditorFactory } from '../audit_trail'; import { InternalElasticsearchServiceSetup, InternalElasticsearchServiceStart } from './types'; import { pollEsNodesVersion } from './version_check/ensure_es_version'; import { calculateStatus$ } from './status'; @@ -41,16 +40,11 @@ interface SetupDeps { http: InternalHttpServiceSetup; } -interface StartDeps { - auditTrail: AuditTrailStart; -} - /** @internal */ export class ElasticsearchService implements CoreService { private readonly log: Logger; private readonly config$: Observable; - private auditorFactory?: AuditorFactory; private stop$ = new Subject(); private kibanaVersion: string; private getAuthHeaders?: GetAuthHeaders; @@ -103,8 +97,7 @@ export class ElasticsearchService status$: calculateStatus$(esNodesCompatibility$), }; } - public async start({ auditTrail }: StartDeps): Promise { - this.auditorFactory = auditTrail; + public async start(): Promise { if (!this.legacyClient || !this.createLegacyCustomClient) { throw new Error('ElasticsearchService needs to be setup before calling start'); } @@ -153,15 +146,7 @@ export class ElasticsearchService return new LegacyClusterClient( config, this.coreContext.logger.get('elasticsearch', type), - this.getAuditorFactory, this.getAuthHeaders ); } - - private getAuditorFactory = () => { - if (!this.auditorFactory) { - throw new Error('auditTrail has not been initialized'); - } - return this.auditorFactory; - }; } diff --git a/src/core/server/elasticsearch/legacy/cluster_client.test.ts b/src/core/server/elasticsearch/legacy/cluster_client.test.ts index 745ef4304d0b1..812f81a1affd6 100644 --- a/src/core/server/elasticsearch/legacy/cluster_client.test.ts +++ b/src/core/server/elasticsearch/legacy/cluster_client.test.ts @@ -27,7 +27,6 @@ import { import { errors } from 'elasticsearch'; import { get } from 'lodash'; -import { auditTrailServiceMock } from '../../audit_trail/audit_trail_service.mock'; import { Logger } from '../../logging'; import { loggingSystemMock } from '../../logging/logging_system.mock'; import { httpServerMock } from '../../http/http_server.mocks'; @@ -43,11 +42,7 @@ test('#constructor creates client with parsed config', () => { const mockEsConfig = { apiVersion: 'es-version' } as any; const mockLogger = logger.get(); - const clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory - ); + const clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger); expect(clusterClient).toBeDefined(); expect(mockParseElasticsearchClientConfig).toHaveBeenCalledTimes(1); @@ -73,11 +68,7 @@ describe('#callAsInternalUser', () => { }; MockClient.mockImplementation(() => mockEsClientInstance); - clusterClient = new LegacyClusterClient( - { apiVersion: 'es-version' } as any, - logger.get(), - auditTrailServiceMock.createAuditorFactory - ); + clusterClient = new LegacyClusterClient({ apiVersion: 'es-version' } as any, logger.get()); }); test('fails if cluster client is closed', async () => { @@ -246,11 +237,7 @@ describe('#asScoped', () => { requestHeadersWhitelist: ['one', 'two'], } as any; - clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory - ); + clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger); jest.clearAllMocks(); }); @@ -285,11 +272,7 @@ describe('#asScoped', () => { test('properly configures `ignoreCertAndKey` for various configurations', () => { // Config without SSL. - clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory - ); + clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger); mockParseElasticsearchClientConfig.mockClear(); clusterClient.asScoped(httpServerMock.createRawRequest({ headers: { one: '1' } })); @@ -302,11 +285,7 @@ describe('#asScoped', () => { // Config ssl.alwaysPresentCertificate === false mockEsConfig = { ...mockEsConfig, ssl: { alwaysPresentCertificate: false } } as any; - clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory - ); + clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger); mockParseElasticsearchClientConfig.mockClear(); clusterClient.asScoped(httpServerMock.createRawRequest({ headers: { one: '1' } })); @@ -319,11 +298,7 @@ describe('#asScoped', () => { // Config ssl.alwaysPresentCertificate === true mockEsConfig = { ...mockEsConfig, ssl: { alwaysPresentCertificate: true } } as any; - clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory - ); + clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger); mockParseElasticsearchClientConfig.mockClear(); clusterClient.asScoped(httpServerMock.createRawRequest({ headers: { one: '1' } })); @@ -344,8 +319,7 @@ describe('#asScoped', () => { expect(MockScopedClusterClient).toHaveBeenCalledWith( expect.any(Function), expect.any(Function), - { one: '1', two: '2' }, - expect.any(Object) + { one: '1', two: '2' } ); }); @@ -360,8 +334,7 @@ describe('#asScoped', () => { expect(MockScopedClusterClient).toHaveBeenCalledWith( expect.any(Function), expect.any(Function), - { 'x-opaque-id': 'alpha' }, - expect.any(Object) + { 'x-opaque-id': 'alpha' } ); }); @@ -383,142 +356,75 @@ describe('#asScoped', () => { }); test('does not fail when scope to not defined request', async () => { - clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory - ); + clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger); clusterClient.asScoped(); expect(MockScopedClusterClient).toHaveBeenCalledTimes(1); expect(MockScopedClusterClient).toHaveBeenCalledWith( expect.any(Function), expect.any(Function), - {}, - undefined + {} ); }); test('does not fail when scope to a request without headers', async () => { - clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory - ); + clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger); clusterClient.asScoped({} as any); expect(MockScopedClusterClient).toHaveBeenCalledTimes(1); expect(MockScopedClusterClient).toHaveBeenCalledWith( expect.any(Function), expect.any(Function), - {}, - undefined + {} ); }); test('calls getAuthHeaders and filters results for a real request', async () => { - clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory, - () => ({ - one: '1', - three: '3', - }) - ); + clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger, () => ({ + one: '1', + three: '3', + })); clusterClient.asScoped(httpServerMock.createRawRequest({ headers: { two: '2' } })); expect(MockScopedClusterClient).toHaveBeenCalledTimes(1); expect(MockScopedClusterClient).toHaveBeenCalledWith( expect.any(Function), expect.any(Function), - { one: '1', two: '2' }, - expect.any(Object) + { one: '1', two: '2' } ); }); test('getAuthHeaders results rewrite extends a request headers', async () => { - clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory, - () => ({ one: 'foo' }) - ); + clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger, () => ({ one: 'foo' })); clusterClient.asScoped(httpServerMock.createRawRequest({ headers: { one: '1', two: '2' } })); expect(MockScopedClusterClient).toHaveBeenCalledTimes(1); expect(MockScopedClusterClient).toHaveBeenCalledWith( expect.any(Function), expect.any(Function), - { one: 'foo', two: '2' }, - expect.any(Object) + { one: 'foo', two: '2' } ); }); test("doesn't call getAuthHeaders for a fake request", async () => { - clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory, - () => ({}) - ); + clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger, () => ({})); clusterClient.asScoped({ headers: { one: 'foo' } }); expect(MockScopedClusterClient).toHaveBeenCalledTimes(1); expect(MockScopedClusterClient).toHaveBeenCalledWith( expect.any(Function), expect.any(Function), - { one: 'foo' }, - undefined + { one: 'foo' } ); }); test('filters a fake request headers', async () => { - clusterClient = new LegacyClusterClient( - mockEsConfig, - mockLogger, - auditTrailServiceMock.createAuditorFactory - ); + clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger); clusterClient.asScoped({ headers: { one: '1', two: '2', three: '3' } }); expect(MockScopedClusterClient).toHaveBeenCalledTimes(1); expect(MockScopedClusterClient).toHaveBeenCalledWith( expect.any(Function), expect.any(Function), - { one: '1', two: '2' }, - undefined + { one: '1', two: '2' } ); }); - - describe('Auditor', () => { - it('creates Auditor for KibanaRequest', async () => { - const auditor = auditTrailServiceMock.createAuditor(); - const auditorFactory = auditTrailServiceMock.createAuditorFactory(); - auditorFactory.asScoped.mockReturnValue(auditor); - clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger, () => auditorFactory); - clusterClient.asScoped(httpServerMock.createKibanaRequest()); - - expect(MockScopedClusterClient).toHaveBeenCalledTimes(1); - expect(MockScopedClusterClient).toHaveBeenCalledWith( - expect.any(Function), - expect.any(Function), - expect.objectContaining({ 'x-opaque-id': expect.any(String) }), - auditor - ); - }); - - it("doesn't create Auditor for a fake request", async () => { - const getAuthHeaders = jest.fn(); - clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger, getAuthHeaders); - clusterClient.asScoped({ headers: { one: '1', two: '2', three: '3' } }); - - expect(getAuthHeaders).not.toHaveBeenCalled(); - }); - - it("doesn't create Auditor when no request passed", async () => { - const getAuthHeaders = jest.fn(); - clusterClient = new LegacyClusterClient(mockEsConfig, mockLogger, getAuthHeaders); - clusterClient.asScoped(); - - expect(getAuthHeaders).not.toHaveBeenCalled(); - }); - }); }); describe('#close', () => { @@ -536,8 +442,7 @@ describe('#close', () => { clusterClient = new LegacyClusterClient( { apiVersion: 'es-version', requestHeadersWhitelist: [] } as any, - logger.get(), - auditTrailServiceMock.createAuditorFactory + logger.get() ); }); diff --git a/src/core/server/elasticsearch/legacy/cluster_client.ts b/src/core/server/elasticsearch/legacy/cluster_client.ts index 81cbb5a10d7c6..00417e3bef4f4 100644 --- a/src/core/server/elasticsearch/legacy/cluster_client.ts +++ b/src/core/server/elasticsearch/legacy/cluster_client.ts @@ -20,8 +20,7 @@ import { Client } from 'elasticsearch'; import { get } from 'lodash'; import { LegacyElasticsearchErrorHelpers } from './errors'; -import { GetAuthHeaders, KibanaRequest, isKibanaRequest, isRealRequest } from '../../http'; -import { AuditorFactory } from '../../audit_trail'; +import { GetAuthHeaders, isKibanaRequest, isRealRequest } from '../../http'; import { filterHeaders, ensureRawRequest } from '../../http/router'; import { Logger } from '../../logging'; import { ScopeableRequest } from '../types'; @@ -132,7 +131,6 @@ export class LegacyClusterClient implements ILegacyClusterClient { constructor( private readonly config: LegacyElasticsearchClientConfig, private readonly log: Logger, - private readonly getAuditorFactory: () => AuditorFactory, private readonly getAuthHeaders: GetAuthHeaders = noop ) { this.client = new Client(parseElasticsearchClientConfig(config, log)); @@ -210,20 +208,10 @@ export class LegacyClusterClient implements ILegacyClusterClient { filterHeaders(this.getHeaders(request), [ 'x-opaque-id', ...this.config.requestHeadersWhitelist, - ]), - this.getScopedAuditor(request) + ]) ); } - private getScopedAuditor(request?: ScopeableRequest) { - // TODO: support alternative credential owners from outside of Request context in #39430 - if (request && isRealRequest(request)) { - const kibanaRequest = isKibanaRequest(request) ? request : KibanaRequest.from(request); - const auditorFactory = this.getAuditorFactory(); - return auditorFactory.asScoped(kibanaRequest); - } - } - /** * Calls specified endpoint with provided clientParams on behalf of the * user initiated request to the Kibana server (via HTTP request headers). diff --git a/src/core/server/elasticsearch/legacy/scoped_cluster_client.test.ts b/src/core/server/elasticsearch/legacy/scoped_cluster_client.test.ts index f1096d5d602f4..2eb8cefb564ae 100644 --- a/src/core/server/elasticsearch/legacy/scoped_cluster_client.test.ts +++ b/src/core/server/elasticsearch/legacy/scoped_cluster_client.test.ts @@ -18,7 +18,6 @@ */ import { LegacyScopedClusterClient } from './scoped_cluster_client'; -import { auditTrailServiceMock } from '../../audit_trail/audit_trail_service.mock'; let internalAPICaller: jest.Mock; let scopedAPICaller: jest.Mock; @@ -84,28 +83,6 @@ describe('#callAsInternalUser', () => { expect(scopedAPICaller).not.toHaveBeenCalled(); }); - - describe('Auditor', () => { - it('does not fail when no auditor provided', () => { - const clusterClientWithoutAuditor = new LegacyScopedClusterClient(jest.fn(), jest.fn()); - expect(() => clusterClientWithoutAuditor.callAsInternalUser('endpoint')).not.toThrow(); - }); - it('creates an audit record if auditor provided', () => { - const auditor = auditTrailServiceMock.createAuditor(); - const clusterClientWithoutAuditor = new LegacyScopedClusterClient( - jest.fn(), - jest.fn(), - {}, - auditor - ); - clusterClientWithoutAuditor.callAsInternalUser('endpoint'); - expect(auditor.add).toHaveBeenCalledTimes(1); - expect(auditor.add).toHaveBeenLastCalledWith({ - message: 'endpoint', - type: 'elasticsearch.call.internalUser', - }); - }); - }); }); describe('#callAsCurrentUser', () => { @@ -229,26 +206,4 @@ describe('#callAsCurrentUser', () => { expect(internalAPICaller).not.toHaveBeenCalled(); }); - - describe('Auditor', () => { - it('does not fail when no auditor provided', () => { - const clusterClientWithoutAuditor = new LegacyScopedClusterClient(jest.fn(), jest.fn()); - expect(() => clusterClientWithoutAuditor.callAsCurrentUser('endpoint')).not.toThrow(); - }); - it('creates an audit record if auditor provided', () => { - const auditor = auditTrailServiceMock.createAuditor(); - const clusterClientWithoutAuditor = new LegacyScopedClusterClient( - jest.fn(), - jest.fn(), - {}, - auditor - ); - clusterClientWithoutAuditor.callAsCurrentUser('endpoint'); - expect(auditor.add).toHaveBeenCalledTimes(1); - expect(auditor.add).toHaveBeenLastCalledWith({ - message: 'endpoint', - type: 'elasticsearch.call.currentUser', - }); - }); - }); }); diff --git a/src/core/server/elasticsearch/legacy/scoped_cluster_client.ts b/src/core/server/elasticsearch/legacy/scoped_cluster_client.ts index aee7a1daa8166..65484f0927c9e 100644 --- a/src/core/server/elasticsearch/legacy/scoped_cluster_client.ts +++ b/src/core/server/elasticsearch/legacy/scoped_cluster_client.ts @@ -18,7 +18,6 @@ */ import { intersection, isObject } from 'lodash'; -import { Auditor } from '../../audit_trail'; import { Headers } from '../../http/router'; import { LegacyAPICaller, LegacyCallAPIOptions } from './api_types'; @@ -47,8 +46,7 @@ export class LegacyScopedClusterClient implements ILegacyScopedClusterClient { constructor( private readonly internalAPICaller: LegacyAPICaller, private readonly scopedAPICaller: LegacyAPICaller, - private readonly headers?: Headers, - private readonly auditor?: Auditor + private readonly headers?: Headers ) { this.callAsCurrentUser = this.callAsCurrentUser.bind(this); this.callAsInternalUser = this.callAsInternalUser.bind(this); @@ -68,13 +66,6 @@ export class LegacyScopedClusterClient implements ILegacyScopedClusterClient { clientParams: Record = {}, options?: LegacyCallAPIOptions ) { - if (this.auditor) { - this.auditor.add({ - message: endpoint, - type: 'elasticsearch.call.internalUser', - }); - } - return this.internalAPICaller(endpoint, clientParams, options); } @@ -107,13 +98,6 @@ export class LegacyScopedClusterClient implements ILegacyScopedClusterClient { clientParams.headers = Object.assign({}, clientParams.headers, this.headers); } - if (this.auditor) { - this.auditor.add({ - message: endpoint, - type: 'elasticsearch.call.currentUser', - }); - } - return this.scopedAPICaller(endpoint, clientParams, options); } } diff --git a/src/core/server/http/integration_tests/lifecycle.test.ts b/src/core/server/http/integration_tests/lifecycle.test.ts index 59090d101acbc..01817b29de8ac 100644 --- a/src/core/server/http/integration_tests/lifecycle.test.ts +++ b/src/core/server/http/integration_tests/lifecycle.test.ts @@ -117,6 +117,62 @@ describe('OnPreRouting', () => { expect(urlAfterForwarding).toBe('/redirectUrl'); }); + it('provides original request url', async () => { + const { registerOnPreRouting, server: innerServer, createRouter } = await server.setup( + setupDeps + ); + const router = createRouter('/'); + + router.get({ path: '/login', validate: false }, (context, req, res) => { + return res.ok({ body: { rewrittenUrl: req.rewrittenUrl?.path } }); + }); + + registerOnPreRouting((req, res, t) => t.rewriteUrl('/login')); + + await server.start(); + + await supertest(innerServer.listener) + .get('/initial?name=foo') + .expect(200, { rewrittenUrl: '/initial?name=foo' }); + }); + + it('provides original request url if rewritten several times', async () => { + const { registerOnPreRouting, server: innerServer, createRouter } = await server.setup( + setupDeps + ); + const router = createRouter('/'); + + router.get({ path: '/reroute-2', validate: false }, (context, req, res) => { + return res.ok({ body: { rewrittenUrl: req.rewrittenUrl?.path } }); + }); + + registerOnPreRouting((req, res, t) => t.rewriteUrl('/reroute-1')); + registerOnPreRouting((req, res, t) => t.rewriteUrl('/reroute-2')); + + await server.start(); + + await supertest(innerServer.listener) + .get('/initial?name=foo') + .expect(200, { rewrittenUrl: '/initial?name=foo' }); + }); + + it('does not provide request url if interceptor does not rewrite url', async () => { + const { registerOnPreRouting, server: innerServer, createRouter } = await server.setup( + setupDeps + ); + const router = createRouter('/'); + + router.get({ path: '/login', validate: false }, (context, req, res) => { + return res.ok({ body: { rewrittenUrl: req.rewrittenUrl?.path } }); + }); + + registerOnPreRouting((req, res, t) => t.next()); + + await server.start(); + + await supertest(innerServer.listener).get('/login').expect(200, {}); + }); + it('supports redirection from the interceptor', async () => { const { registerOnPreRouting, server: innerServer, createRouter } = await server.setup( setupDeps diff --git a/src/core/server/http/lifecycle/on_pre_routing.ts b/src/core/server/http/lifecycle/on_pre_routing.ts index e62eb54f2398f..92ae1f0b7bbdf 100644 --- a/src/core/server/http/lifecycle/on_pre_routing.ts +++ b/src/core/server/http/lifecycle/on_pre_routing.ts @@ -25,6 +25,7 @@ import { KibanaResponse, lifecycleResponseFactory, LifecycleResponseFactory, + KibanaRequestState, } from '../router'; enum ResultType { @@ -108,6 +109,9 @@ export function adoptToHapiOnRequest(fn: OnPreRoutingHandler, log: Logger) { } if (preRoutingResult.isRewriteUrl(result)) { + const appState = request.app as KibanaRequestState; + appState.rewrittenUrl = appState.rewrittenUrl ?? request.url; + const { url } = result; request.setUrl(url); // We should update raw request as well since it can be proxied to the old platform diff --git a/src/core/server/http/router/request.ts b/src/core/server/http/router/request.ts index 903eb75022df3..2d0e8d6c1a6ad 100644 --- a/src/core/server/http/router/request.ts +++ b/src/core/server/http/router/request.ts @@ -45,6 +45,7 @@ export interface KibanaRouteOptions extends RouteOptionsApp { export interface KibanaRequestState extends ApplicationState { requestId: string; requestUuid: string; + rewrittenUrl?: Url; } /** @@ -186,6 +187,11 @@ export class KibanaRequest< isAuthenticated: boolean; }; + /** + * URL rewritten in onPreRouting request interceptor. + */ + public readonly rewrittenUrl?: Url; + /** @internal */ protected readonly [requestSymbol]: Request; @@ -199,10 +205,12 @@ export class KibanaRequest< private readonly withoutSecretHeaders: boolean ) { // The `requestId` and `requestUuid` properties will not be populated for requests that are 'faked' by internal systems that leverage - // KibanaRequest in conjunction with scoped Elaticcsearch and SavedObjectsClient in order to pass credentials. + // KibanaRequest in conjunction with scoped Elasticsearch and SavedObjectsClient in order to pass credentials. // In these cases, the ids default to a newly generated UUID. - this.id = (request.app as KibanaRequestState | undefined)?.requestId ?? uuid.v4(); - this.uuid = (request.app as KibanaRequestState | undefined)?.requestUuid ?? uuid.v4(); + const appState = request.app as KibanaRequestState | undefined; + this.id = appState?.requestId ?? uuid.v4(); + this.uuid = appState?.requestUuid ?? uuid.v4(); + this.rewrittenUrl = appState?.rewrittenUrl; this.url = request.url; this.headers = deepFreeze({ ...request.headers }); diff --git a/src/core/server/index.ts b/src/core/server/index.ts index fc091bd17bdf4..efb196590ea97 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -62,7 +62,6 @@ import { import { CapabilitiesSetup, CapabilitiesStart } from './capabilities'; import { MetricsServiceSetup, MetricsServiceStart } from './metrics'; import { StatusServiceSetup } from './status'; -import { Auditor, AuditTrailSetup, AuditTrailStart } from './audit_trail'; import { AppenderConfigType, appendersSchema, LoggingServiceSetup } from './logging'; import { CoreUsageDataStart } from './core_usage_data'; @@ -77,7 +76,6 @@ import { export { CoreUsageData, CoreConfigUsageData, CoreEnvironmentUsageData, CoreServicesUsageData }; -export { AuditableEvent, Auditor, AuditorFactory, AuditTrailSetup } from './audit_trail'; export { bootstrap } from './bootstrap'; export { Capabilities, CapabilitiesProvider, CapabilitiesSwitcher } from './capabilities'; export { @@ -378,7 +376,6 @@ export { CoreUsageDataStart } from './core_usage_data'; * data client which uses the credentials of the incoming request * - {@link IUiSettingsClient | uiSettings.client} - uiSettings client * which uses the credentials of the incoming request - * - {@link Auditor | uiSettings.auditor} - AuditTrail client scoped to the incoming request * * @public */ @@ -397,7 +394,6 @@ export interface RequestHandlerContext { uiSettings: { client: IUiSettingsClient; }; - auditor: Auditor; }; } @@ -434,8 +430,6 @@ export interface CoreSetup; - /** {@link AuditTrailSetup} */ - auditTrail: AuditTrailSetup; } /** @@ -469,8 +463,6 @@ export interface CoreStart { savedObjects: SavedObjectsServiceStart; /** {@link UiSettingsServiceStart} */ uiSettings: UiSettingsServiceStart; - /** {@link AuditTrailSetup} */ - auditTrail: AuditTrailStart; /** @internal {@link CoreUsageDataStart} */ coreUsageData: CoreUsageDataStart; } @@ -483,7 +475,6 @@ export { PluginsServiceSetup, PluginsServiceStart, PluginOpaqueId, - AuditTrailStart, }; /** diff --git a/src/core/server/internal_types.ts b/src/core/server/internal_types.ts index ce58348a14153..294af5ec34c3e 100644 --- a/src/core/server/internal_types.ts +++ b/src/core/server/internal_types.ts @@ -37,7 +37,6 @@ import { InternalMetricsServiceSetup, InternalMetricsServiceStart } from './metr import { InternalRenderingServiceSetup } from './rendering'; import { InternalHttpResourcesSetup } from './http_resources'; import { InternalStatusServiceSetup } from './status'; -import { AuditTrailSetup, AuditTrailStart } from './audit_trail'; import { InternalLoggingServiceSetup } from './logging'; import { CoreUsageDataStart } from './core_usage_data'; @@ -53,7 +52,6 @@ export interface InternalCoreSetup { environment: InternalEnvironmentServiceSetup; rendering: InternalRenderingServiceSetup; httpResources: InternalHttpResourcesSetup; - auditTrail: AuditTrailSetup; logging: InternalLoggingServiceSetup; metrics: InternalMetricsServiceSetup; } @@ -68,7 +66,6 @@ export interface InternalCoreStart { metrics: InternalMetricsServiceStart; savedObjects: InternalSavedObjectsServiceStart; uiSettings: InternalUiSettingsServiceStart; - auditTrail: AuditTrailStart; coreUsageData: CoreUsageDataStart; } diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index 57009f0d35c16..b8f5757f0b67f 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -44,7 +44,6 @@ import { LegacyServiceSetupDeps, LegacyServiceStartDeps } from './types'; import { LegacyService } from './legacy_service'; import { coreMock } from '../mocks'; import { statusServiceMock } from '../status/status_service.mock'; -import { auditTrailServiceMock } from '../audit_trail/audit_trail_service.mock'; import { loggingServiceMock } from '../logging/logging_service.mock'; import { metricsServiceMock } from '../metrics/metrics_service.mock'; @@ -92,7 +91,6 @@ beforeEach(() => { rendering: renderingServiceMock, environment: environmentSetup, status: statusServiceMock.createInternalSetupContract(), - auditTrail: auditTrailServiceMock.createSetupContract(), logging: loggingServiceMock.createInternalSetupContract(), metrics: metricsServiceMock.createInternalSetupContract(), }, diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 75e8ae6524920..c42771179aba2 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -216,7 +216,6 @@ export class LegacyService implements CoreService { getOpsMetrics$: startDeps.core.metrics.getOpsMetrics$, }, uiSettings: { asScopedToClient: startDeps.core.uiSettings.asScopedToClient }, - auditTrail: startDeps.core.auditTrail, coreUsageData: { getCoreUsageData: () => { throw new Error('core.start.coreUsageData.getCoreUsageData is unsupported in legacy'); @@ -284,7 +283,6 @@ export class LegacyService implements CoreService { uiSettings: { register: setupDeps.core.uiSettings.register, }, - auditTrail: setupDeps.core.auditTrail, getStartServices: () => Promise.resolve([coreStart, startDeps.plugins, {}]), }; diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 34e85920efb24..e47d06409894e 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -36,7 +36,6 @@ import { capabilitiesServiceMock } from './capabilities/capabilities_service.moc import { metricsServiceMock } from './metrics/metrics_service.mock'; import { environmentServiceMock } from './environment/environment_service.mock'; import { statusServiceMock } from './status/status_service.mock'; -import { auditTrailServiceMock } from './audit_trail/audit_trail_service.mock'; import { coreUsageDataServiceMock } from './core_usage_data/core_usage_data_service.mock'; export { configServiceMock } from './config/mocks'; @@ -139,7 +138,6 @@ function createCoreSetupMock({ savedObjects: savedObjectsServiceMock.createInternalSetupContract(), status: statusServiceMock.createSetupContract(), uiSettings: uiSettingsMock, - auditTrail: auditTrailServiceMock.createSetupContract(), logging: loggingServiceMock.createSetupContract(), metrics: metricsServiceMock.createSetupContract(), getStartServices: jest @@ -152,7 +150,6 @@ function createCoreSetupMock({ function createCoreStartMock() { const mock: MockedKeys = { - auditTrail: auditTrailServiceMock.createStartContract(), capabilities: capabilitiesServiceMock.createStartContract(), elasticsearch: elasticsearchServiceMock.createStart(), http: httpServiceMock.createStartContract(), @@ -177,7 +174,6 @@ function createInternalCoreSetupMock() { httpResources: httpResourcesMock.createSetupContract(), rendering: renderingMock.createSetupContract(), uiSettings: uiSettingsServiceMock.createSetupContract(), - auditTrail: auditTrailServiceMock.createSetupContract(), logging: loggingServiceMock.createInternalSetupContract(), metrics: metricsServiceMock.createInternalSetupContract(), }; @@ -192,7 +188,6 @@ function createInternalCoreStartMock() { metrics: metricsServiceMock.createInternalStartContract(), savedObjects: savedObjectsServiceMock.createInternalStartContract(), uiSettings: uiSettingsServiceMock.createStartContract(), - auditTrail: auditTrailServiceMock.createStartContract(), coreUsageData: coreUsageDataServiceMock.createStartContract(), }; return startDeps; @@ -213,7 +208,6 @@ function createCoreRequestHandlerContextMock() { uiSettings: { client: uiSettingsServiceMock.createClient(), }, - auditor: auditTrailServiceMock.createAuditor(), }; } diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index a8249ed7e3218..22e79741e854c 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -201,7 +201,6 @@ export function createPluginSetupContext( register: deps.uiSettings.register, }, getStartServices: () => plugin.startDependencies, - auditTrail: deps.auditTrail, }; } @@ -250,7 +249,6 @@ export function createPluginStartContext( uiSettings: { asScopedToClient: deps.uiSettings.asScopedToClient, }, - auditTrail: deps.auditTrail, coreUsageData: deps.coreUsageData, }; } diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 20bd102e6f507..d9dc46d2cad99 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -198,38 +198,6 @@ export interface AssistantAPIClientParams extends GenericParams { path: '/_migration/assistance'; } -// @public -export interface AuditableEvent { - // (undocumented) - message: string; - // (undocumented) - type: string; -} - -// @public -export interface Auditor { - add(event: AuditableEvent): void; - withAuditScope(name: string): void; -} - -// @public -export interface AuditorFactory { - // (undocumented) - asScoped(request: KibanaRequest): Auditor; -} - -// Warning: (ae-missing-release-tag) "AuditTrailSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface AuditTrailSetup { - register(auditor: AuditorFactory): void; -} - -// Warning: (ae-missing-release-tag) "AuditTrailStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type AuditTrailStart = AuditorFactory; - // @public (undocumented) export interface Authenticated extends AuthResultParams { // (undocumented) @@ -499,8 +467,6 @@ export interface CoreServicesUsageData { // @public export interface CoreSetup { - // (undocumented) - auditTrail: AuditTrailSetup; // (undocumented) capabilities: CapabilitiesSetup; // (undocumented) @@ -527,8 +493,6 @@ export interface CoreSetup>; // (undocumented) readonly socket: IKibanaSocket; @@ -1348,7 +1313,7 @@ export interface LegacyCallAPIOptions { // @public @deprecated export class LegacyClusterClient implements ILegacyClusterClient { - constructor(config: LegacyElasticsearchClientConfig, log: Logger, getAuditorFactory: () => AuditorFactory, getAuthHeaders?: GetAuthHeaders); + constructor(config: LegacyElasticsearchClientConfig, log: Logger, getAuthHeaders?: GetAuthHeaders); asScoped(request?: ScopeableRequest): ILegacyScopedClusterClient; callAsInternalUser: LegacyAPICaller; close(): void; @@ -1396,7 +1361,7 @@ export interface LegacyRequest extends Request { // @public @deprecated export class LegacyScopedClusterClient implements ILegacyScopedClusterClient { - constructor(internalAPICaller: LegacyAPICaller, scopedAPICaller: LegacyAPICaller, headers?: Headers | undefined, auditor?: Auditor | undefined); + constructor(internalAPICaller: LegacyAPICaller, scopedAPICaller: LegacyAPICaller, headers?: Headers | undefined); callAsCurrentUser(endpoint: string, clientParams?: Record, options?: LegacyCallAPIOptions): Promise; callAsInternalUser(endpoint: string, clientParams?: Record, options?: LegacyCallAPIOptions): Promise; } @@ -1738,7 +1703,6 @@ export interface RequestHandlerContext { uiSettings: { client: IUiSettingsClient; }; - auditor: Auditor; }; } diff --git a/src/core/server/server.test.mocks.ts b/src/core/server/server.test.mocks.ts index 77f2787b75412..fe299c6d11675 100644 --- a/src/core/server/server.test.mocks.ts +++ b/src/core/server/server.test.mocks.ts @@ -100,9 +100,3 @@ export const mockLoggingService = loggingServiceMock.create(); jest.doMock('./logging/logging_service', () => ({ LoggingService: jest.fn(() => mockLoggingService), })); - -import { auditTrailServiceMock } from './audit_trail/audit_trail_service.mock'; -export const mockAuditTrailService = auditTrailServiceMock.create(); -jest.doMock('./audit_trail/audit_trail_service', () => ({ - AuditTrailService: jest.fn(() => mockAuditTrailService), -})); diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts index 51defb7d0392e..78703ceeec7ae 100644 --- a/src/core/server/server.test.ts +++ b/src/core/server/server.test.ts @@ -31,7 +31,6 @@ import { mockMetricsService, mockStatusService, mockLoggingService, - mockAuditTrailService, } from './server.test.mocks'; import { BehaviorSubject } from 'rxjs'; @@ -71,7 +70,6 @@ test('sets up services on "setup"', async () => { expect(mockMetricsService.setup).not.toHaveBeenCalled(); expect(mockStatusService.setup).not.toHaveBeenCalled(); expect(mockLoggingService.setup).not.toHaveBeenCalled(); - expect(mockAuditTrailService.setup).not.toHaveBeenCalled(); await server.setup(); @@ -85,7 +83,6 @@ test('sets up services on "setup"', async () => { expect(mockMetricsService.setup).toHaveBeenCalledTimes(1); expect(mockStatusService.setup).toHaveBeenCalledTimes(1); expect(mockLoggingService.setup).toHaveBeenCalledTimes(1); - expect(mockAuditTrailService.setup).toHaveBeenCalledTimes(1); }); test('injects legacy dependency to context#setup()', async () => { @@ -126,7 +123,6 @@ test('runs services on "start"', async () => { expect(mockSavedObjectsService.start).not.toHaveBeenCalled(); expect(mockUiSettingsService.start).not.toHaveBeenCalled(); expect(mockMetricsService.start).not.toHaveBeenCalled(); - expect(mockAuditTrailService.start).not.toHaveBeenCalled(); await server.start(); @@ -135,7 +131,6 @@ test('runs services on "start"', async () => { expect(mockSavedObjectsService.start).toHaveBeenCalledTimes(1); expect(mockUiSettingsService.start).toHaveBeenCalledTimes(1); expect(mockMetricsService.start).toHaveBeenCalledTimes(1); - expect(mockAuditTrailService.start).toHaveBeenCalledTimes(1); }); test('does not fail on "setup" if there are unused paths detected', async () => { @@ -160,7 +155,6 @@ test('stops services on "stop"', async () => { expect(mockMetricsService.stop).not.toHaveBeenCalled(); expect(mockStatusService.stop).not.toHaveBeenCalled(); expect(mockLoggingService.stop).not.toHaveBeenCalled(); - expect(mockAuditTrailService.stop).not.toHaveBeenCalled(); await server.stop(); @@ -173,7 +167,6 @@ test('stops services on "stop"', async () => { expect(mockMetricsService.stop).toHaveBeenCalledTimes(1); expect(mockStatusService.stop).toHaveBeenCalledTimes(1); expect(mockLoggingService.stop).toHaveBeenCalledTimes(1); - expect(mockAuditTrailService.stop).toHaveBeenCalledTimes(1); }); test(`doesn't setup core services if config validation fails`, async () => { @@ -227,7 +220,6 @@ test(`doesn't validate config if env.isDevClusterMaster is true`, async () => { expect(mockEnsureValidConfiguration).not.toHaveBeenCalled(); expect(mockContextService.setup).toHaveBeenCalled(); - expect(mockAuditTrailService.setup).toHaveBeenCalled(); expect(mockHttpService.setup).toHaveBeenCalled(); expect(mockElasticsearchService.setup).toHaveBeenCalled(); expect(mockSavedObjectsService.setup).toHaveBeenCalled(); diff --git a/src/core/server/server.ts b/src/core/server/server.ts index f38cac4f43768..eaa03d11cab98 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -21,7 +21,6 @@ import { config as pathConfig } from '@kbn/utils'; import { mapToObject } from '@kbn/std'; import { ConfigService, Env, RawConfigurationProvider, coreDeprecationProvider } from './config'; import { CoreApp } from './core_app'; -import { AuditTrailService } from './audit_trail'; import { ElasticsearchService } from './elasticsearch'; import { HttpService } from './http'; import { HttpResourcesService } from './http_resources'; @@ -72,7 +71,6 @@ export class Server { private readonly status: StatusService; private readonly logging: LoggingService; private readonly coreApp: CoreApp; - private readonly auditTrail: AuditTrailService; private readonly coreUsageData: CoreUsageDataService; #pluginsInitialized?: boolean; @@ -103,7 +101,6 @@ export class Server { this.status = new StatusService(core); this.coreApp = new CoreApp(core); this.httpResources = new HttpResourcesService(core); - this.auditTrail = new AuditTrailService(core); this.logging = new LoggingService(core); this.coreUsageData = new CoreUsageDataService(core); } @@ -139,8 +136,6 @@ export class Server { ]), }); - const auditTrailSetup = this.auditTrail.setup(); - const httpSetup = await this.http.setup({ context: contextServiceSetup, }); @@ -200,7 +195,6 @@ export class Server { uiSettings: uiSettingsSetup, rendering: renderingSetup, httpResources: httpResourcesSetup, - auditTrail: auditTrailSetup, logging: loggingSetup, metrics: metricsSetup, }; @@ -225,11 +219,7 @@ export class Server { this.log.debug('starting server'); const startTransaction = apm.startTransaction('server_start', 'kibana_platform'); - const auditTrailStart = this.auditTrail.start(); - - const elasticsearchStart = await this.elasticsearch.start({ - auditTrail: auditTrailStart, - }); + const elasticsearchStart = await this.elasticsearch.start(); const soStartSpan = startTransaction?.startSpan('saved_objects.migration', 'migration'); const savedObjectsStart = await this.savedObjects.start({ elasticsearch: elasticsearchStart, @@ -252,7 +242,6 @@ export class Server { metrics: metricsStart, savedObjects: savedObjectsStart, uiSettings: uiSettingsStart, - auditTrail: auditTrailStart, coreUsageData: coreUsageDataStart, }; @@ -285,7 +274,6 @@ export class Server { await this.metrics.stop(); await this.status.stop(); await this.logging.stop(); - await this.auditTrail.stop(); } private registerCoreContext(coreSetup: InternalCoreSetup) { diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker index 0039debe383bd..f5cf6c85fcbef 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker @@ -47,6 +47,10 @@ kibana_vars=( elasticsearch.ssl.truststore.password elasticsearch.ssl.verificationMode elasticsearch.username + enterpriseSearch.accessCheckTimeout + enterpriseSearch.accessCheckTimeoutWarning + enterpriseSearch.enabled + enterpriseSearch.host i18n.locale interpreter.enableInVisualize kibana.autocompleteTerminateAfter diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile index e7523c1bf6032..fba3dcee0a7cb 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile @@ -33,7 +33,6 @@ RUN tar --strip-components=1 -zxf /opt/{{artifactTarball}} # OpenShift does this, for example. # REF: https://docs.openshift.org/latest/creating_images/guidelines.html RUN chmod -R g=u /usr/share/kibana -RUN find /usr/share/kibana -type d -exec chmod g+s {} \; ################################################################################ # Build stage 1 (the actual Kibana image): diff --git a/src/dev/plugin_discovery/find_plugins.ts b/src/dev/plugin_discovery/find_plugins.ts new file mode 100644 index 0000000000000..4e7c34698c964 --- /dev/null +++ b/src/dev/plugin_discovery/find_plugins.ts @@ -0,0 +1,50 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import Path from 'path'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import { getPluginSearchPaths } from '@kbn/config'; +import { KibanaPlatformPlugin, simpleKibanaPlatformPluginDiscovery } from '@kbn/dev-utils'; + +export interface SearchOptions { + oss: boolean; + examples: boolean; + extraPluginScanDirs: string[]; +} + +export function findPlugins({ + oss, + examples, + extraPluginScanDirs, +}: SearchOptions): Map { + const pluginSearchPaths = getPluginSearchPaths({ + rootDir: REPO_ROOT, + oss, + examples, + }); + + for (const extraScanDir of extraPluginScanDirs) { + if (!Path.isAbsolute(extraScanDir)) { + throw new TypeError('extraPluginScanDirs must all be absolute paths'); + } + pluginSearchPaths.push(extraScanDir); + } + + const plugins = simpleKibanaPlatformPluginDiscovery(pluginSearchPaths, []); + return new Map(plugins.map((p) => [p.manifest.id, p])); +} diff --git a/src/dev/plugin_discovery/get_plugin_deps.ts b/src/dev/plugin_discovery/get_plugin_deps.ts new file mode 100644 index 0000000000000..498feefd97094 --- /dev/null +++ b/src/dev/plugin_discovery/get_plugin_deps.ts @@ -0,0 +1,89 @@ +/* + * 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 { KibanaPlatformPlugin } from '@kbn/dev-utils'; + +interface AllOptions { + id: string; + pluginMap: Map; +} + +interface CircularRefsError { + from: string; + to: string; + stack: string[]; +} + +export type SearchErrors = CircularRefsError; + +interface State { + deps: Set; + stack: string[]; + errors: Map; +} + +function traverse(pluginMap: Map, state: State, id: string) { + const plugin = pluginMap.get(id); + if (plugin === undefined) { + throw new Error(`Unknown plugin id: ${id}`); + } + + const prevIndex = state.stack.indexOf(id); + const isVisited = prevIndex > -1; + if (isVisited) { + const from = state.stack[state.stack.length - 1]; + const to = id; + const key = `circular-${[from, to].sort().join('-')}`; + + if (!state.errors.has(key)) { + const error: CircularRefsError = { + from, + to, + // provide sub-stack with circular refs only + stack: state.stack.slice(prevIndex), + }; + state.errors.set(key, error); + } + + return; + } + + state.stack.push(id); + new Set([ + ...plugin.manifest.requiredPlugins, + ...plugin.manifest.optionalPlugins, + ...plugin.manifest.requiredBundles, + ]).forEach((depId) => { + state.deps.add(pluginMap.get(depId)!); + traverse(pluginMap, state, depId); + }); + + state.stack.pop(); +} + +export function getPluginDeps({ pluginMap, id }: AllOptions): State { + const state: State = { + deps: new Set(), + errors: new Map(), + stack: [], + }; + + traverse(pluginMap, state, id); + + return state; +} diff --git a/src/dev/plugin_discovery/index.ts b/src/dev/plugin_discovery/index.ts new file mode 100644 index 0000000000000..4a4be65dfaef0 --- /dev/null +++ b/src/dev/plugin_discovery/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './find_plugins'; +export * from './get_plugin_deps'; diff --git a/src/dev/run_find_plugin_circular_deps.ts b/src/dev/run_find_plugin_circular_deps.ts new file mode 100644 index 0000000000000..501e2c4fed048 --- /dev/null +++ b/src/dev/run_find_plugin_circular_deps.ts @@ -0,0 +1,73 @@ +/* + * 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 { run } from '@kbn/dev-utils'; +import { findPlugins, getPluginDeps, SearchErrors } from './plugin_discovery'; + +interface AllOptions { + examples?: boolean; + extraPluginScanDirs?: string[]; +} + +run( + async ({ flags, log }) => { + const { examples = false, extraPluginScanDirs = [] } = flags as AllOptions; + + const pluginMap = findPlugins({ + oss: false, + examples, + extraPluginScanDirs, + }); + + const allErrors = new Map(); + for (const pluginId of pluginMap.keys()) { + const { errors } = getPluginDeps({ + pluginMap, + id: pluginId, + }); + + for (const [errorId, error] of errors) { + if (!allErrors.has(errorId)) { + allErrors.set(errorId, error); + } + } + } + + if (allErrors.size > 0) { + allErrors.forEach((error) => { + log.warning( + `Circular refs detected: ${[...error.stack, error.to].map((p) => `[${p}]`).join(' --> ')}` + ); + }); + } + }, + { + flags: { + boolean: ['examples'], + default: { + examples: false, + }, + allowUnexpected: false, + help: ` + --examples Include examples folder + --extraPluginScanDirs Include extra scan folder + `, + }, + } +); diff --git a/src/dev/run_find_plugins_without_ts_refs.ts b/src/dev/run_find_plugins_without_ts_refs.ts new file mode 100644 index 0000000000000..ad63884671e24 --- /dev/null +++ b/src/dev/run_find_plugins_without_ts_refs.ts @@ -0,0 +1,95 @@ +/* + * 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 Path from 'path'; +import Fs from 'fs'; +import { get } from 'lodash'; +import { run } from '@kbn/dev-utils'; +import { getPluginDeps, findPlugins } from './plugin_discovery'; + +interface AllOptions { + id?: string; + examples?: boolean; + extraPluginScanDirs?: string[]; +} + +run( + async ({ flags, log }) => { + const { examples = false, extraPluginScanDirs = [], id } = flags as AllOptions; + + if (!id) { + throw new Error('Plugin id required'); + } + + const pluginMap = findPlugins({ + oss: false, + examples, + extraPluginScanDirs, + }); + + const result = getPluginDeps({ + pluginMap, + id, + }); + + if (result.errors.size > 0) { + result.errors.forEach((error) => { + log.warning( + `Circular refs detected: ${[...error.stack, error.to].map((p) => `[${p}]`).join(' --> ')}` + ); + }); + } + + const notMigratedPlugins = [...result.deps].filter( + (plugin) => !isMigratedToTsProjectRefs(plugin.directory) + ); + if (notMigratedPlugins.length > 0) { + log.info( + `Dependencies haven't been migrated to TS project refs yet:\n${notMigratedPlugins + .map((p) => p.manifest.id) + .join('\n')}` + ); + } + }, + { + flags: { + boolean: ['examples'], + string: ['id'], + default: { + examples: false, + }, + allowUnexpected: false, + help: ` + --id Plugin id to perform deps search for + --examples Include examples folder + --extraPluginScanDirs Include extra scan folder + `, + }, + } +); + +function isMigratedToTsProjectRefs(dir: string): boolean { + try { + const path = Path.join(dir, 'tsconfig.json'); + const content = Fs.readFileSync(path, { encoding: 'utf8' }); + return get(JSON.parse(content), 'compilerOptions.composite', false); + } catch (e) { + return false; + } +} diff --git a/src/legacy/server/i18n/index.ts b/src/legacy/server/i18n/index.ts index cb86c3220bec1..61caefb2fb599 100644 --- a/src/legacy/server/i18n/index.ts +++ b/src/legacy/server/i18n/index.ts @@ -33,7 +33,7 @@ export async function i18nMixin(kbnServer: KbnServer, server: Server, config: Ki const translationPaths = await Promise.all([ getTranslationPaths({ cwd: fromRoot('.'), - glob: I18N_RC, + glob: `*/${I18N_RC}`, }), ...(config.get('plugins.paths') as string[]).map((cwd) => getTranslationPaths({ cwd, glob: I18N_RC }) diff --git a/src/legacy/server/keystore/keystore.test.js b/src/legacy/server/keystore/keystore.test.js index 0897ce55d086b..e35edd1859484 100644 --- a/src/legacy/server/keystore/keystore.test.js +++ b/src/legacy/server/keystore/keystore.test.js @@ -157,11 +157,13 @@ describe('Keystore', () => { it('adds a key/value pair', () => { const keystore = new Keystore('/data/unprotected.keystore'); keystore.add('a3', 'baz'); + keystore.add('a4', [1, 'a', 2, 'b']); expect(keystore.data).toEqual({ 'a1.b2.c3': 'foo', a2: 'bar', a3: 'baz', + a4: [1, 'a', 2, 'b'], }); }); }); diff --git a/src/plugins/charts/public/services/colors/color_palette.ts b/src/plugins/charts/public/services/colors/color_palette.ts index e1c32fe68da12..df76edb1e30ed 100644 --- a/src/plugins/charts/public/services/colors/color_palette.ts +++ b/src/plugins/charts/public/services/colors/color_palette.ts @@ -58,7 +58,7 @@ export function createColorPalette(num: number): string[] { const seedLength = seedColors.length; _.times(num - seedLength, function (i) { - colors.push(hsl((fraction(i + seedLength + 1) * 360 + offset) % 360, 0.5, 0.5).hex()); + colors.push(hsl((fraction(i + seedLength + 1) * 360 + offset) % 360, 50, 50).hex()); }); return colors; diff --git a/src/plugins/charts/public/services/colors/colors_palette.test.ts b/src/plugins/charts/public/services/colors/colors_palette.test.ts index 02ff5a6056d54..273a36f6a43a6 100644 --- a/src/plugins/charts/public/services/colors/colors_palette.test.ts +++ b/src/plugins/charts/public/services/colors/colors_palette.test.ts @@ -90,4 +90,8 @@ describe('Color Palette', () => { it('should create new darker colors when input is greater than 72', () => { expect(createColorPalette(num3)[72]).not.toEqual(seedColors[0]); }); + + it('should create new colors and convert them correctly', () => { + expect(createColorPalette(num3)[72]).toEqual('#404ABF'); + }); }); 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 201c6e83a4a44..d523bd2ea5b52 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 @@ -704,7 +704,7 @@ exports[`DashboardEmptyScreen renders correctly with visualize paragraph 1`] = ` size="m" type="plusInCircle" > -
`#${DashboardConstants.LANDING_PAGE_PATH}`; const getDashTitle = () => diff --git a/src/plugins/data/common/field_formats/field_format.ts b/src/plugins/data/common/field_formats/field_format.ts index 9e4308d6fd559..15ecf6e4fc3ef 100644 --- a/src/plugins/data/common/field_formats/field_format.ts +++ b/src/plugins/data/common/field_formats/field_format.ts @@ -185,7 +185,8 @@ export abstract class FieldFormat { const params = transform( this._params, - (uniqParams: any, val, param) => { + (uniqParams: any, val, param: string) => { + if (param === 'parsedUrl') return; if (param && val !== get(defaultsParams, param)) { uniqParams[param] = val; } @@ -195,7 +196,7 @@ export abstract class FieldFormat { return { id, - params: size(params) ? params : undefined, + params: size(params) ? (params as any) : undefined, }; } diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap index ed84aceb60e5a..dc4da2456b47b 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap @@ -2,6 +2,7 @@ exports[`IndexPattern toSpec should match snapshot 1`] = ` Object { + "fieldFormats": Object {}, "fields": Object { "@tags": Object { "aggregatable": true, diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap index 752fdcf11991c..a3d19f311b765 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap @@ -2,6 +2,9 @@ exports[`IndexPatterns savedObjectToSpec 1`] = ` Object { + "fieldFormats": Object { + "field": Object {}, + }, "fields": Object {}, "id": "id", "intervalName": undefined, diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts index 6e11bc8f1d508..9fd43be8dc5b3 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts @@ -183,6 +183,20 @@ describe('IndexPattern', () => { }); }); + describe('setFieldFormat and deleteFieldFormaat', () => { + test('should persist changes', () => { + const formatter = { + toJSON: () => ({ id: 'bytes' }), + } as FieldFormat; + indexPattern.getFormatterForField = () => formatter; + indexPattern.setFieldFormat('bytes', { id: 'bytes' }); + expect(indexPattern.toSpec().fieldFormats).toEqual({ bytes: { id: 'bytes' } }); + + indexPattern.deleteFieldFormat('bytes'); + expect(indexPattern.toSpec().fieldFormats).toEqual({}); + }); + }); + describe('toSpec', () => { test('should match snapshot', () => { const formatter = { @@ -209,7 +223,6 @@ describe('IndexPattern', () => { expect(restoredPattern.title).toEqual(indexPattern.title); expect(restoredPattern.timeFieldName).toEqual(indexPattern.timeFieldName); expect(restoredPattern.fields.length).toEqual(indexPattern.fields.length); - expect(restoredPattern.fieldFormatMap.bytes instanceof MockFieldFormatter).toEqual(true); }); }); }); diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 5fc6344c935d5..d38df68e9f428 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -21,13 +21,7 @@ import _, { each, reject } from 'lodash'; import { SavedObjectsClientCommon } from '../..'; import { DuplicateField } from '../../../../kibana_utils/common'; -import { - ES_FIELD_TYPES, - KBN_FIELD_TYPES, - IIndexPattern, - FieldFormatNotFoundError, - IFieldType, -} from '../../../common'; +import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../../../common'; import { IndexPatternField, IIndexPatternFieldList, fieldList } from '../fields'; import { formatHitProvider } from './format_hit'; import { flattenHitWrapper } from './flatten_hit'; @@ -102,7 +96,7 @@ export class IndexPattern implements IIndexPattern { // set values this.id = spec.id; - const fieldFormatMap = this.fieldSpecsToFieldFormatMap(spec.fields); + this.fieldFormatMap = spec.fieldFormats || {}; this.version = spec.version; @@ -113,12 +107,16 @@ export class IndexPattern implements IIndexPattern { this.fields.replaceAll(Object.values(spec.fields || {})); this.type = spec.type; this.typeMeta = spec.typeMeta; - - this.fieldFormatMap = _.mapValues(fieldFormatMap, (mapping) => { - return this.deserializeFieldFormatMap(mapping); - }); } + setFieldFormat = (fieldName: string, format: SerializedFieldFormat) => { + this.fieldFormatMap[fieldName] = format; + }; + + deleteFieldFormat = (fieldName: string) => { + delete this.fieldFormatMap[fieldName]; + }; + /** * Get last saved saved object fields */ @@ -131,34 +129,6 @@ export class IndexPattern implements IIndexPattern { this.originalSavedObjectBody = this.getAsSavedObjectBody(); }; - /** - * Converts field format spec to field format instance - * @param mapping - */ - private deserializeFieldFormatMap(mapping: SerializedFieldFormat>) { - try { - return this.fieldFormats.getInstance(mapping.id as string, mapping.params); - } catch (err) { - if (err instanceof FieldFormatNotFoundError) { - return undefined; - } else { - throw err; - } - } - } - - /** - * Extracts FieldFormatMap from FieldSpec map - * @param fldList FieldSpec map - */ - private fieldSpecsToFieldFormatMap = (fldList: IndexPatternSpec['fields'] = {}) => - Object.values(fldList).reduce>((col, fieldSpec) => { - if (fieldSpec.format) { - col[fieldSpec.name] = { ...fieldSpec.format }; - } - return col; - }, {}); - getComputedFields() { const scriptFields: any = {}; if (!this.fields) { @@ -211,6 +181,7 @@ export class IndexPattern implements IIndexPattern { fields: this.fields.toSpec({ getFormatterForField: this.getFormatterForField.bind(this) }), typeMeta: this.typeMeta, type: this.type, + fieldFormats: this.fieldFormatMap, }; } @@ -299,17 +270,9 @@ export class IndexPattern implements IIndexPattern { * Returns index pattern as saved object body for saving */ getAsSavedObjectBody() { - const serializeFieldFormatMap = ( - flat: any, - format: FieldFormat | undefined, - field: string | undefined - ) => { - if (format && field) { - flat[field] = format; - } - }; - const serialized = _.transform(this.fieldFormatMap, serializeFieldFormatMap); - const fieldFormatMap = _.isEmpty(serialized) ? undefined : JSON.stringify(serialized); + const fieldFormatMap = _.isEmpty(this.fieldFormatMap) + ? undefined + : JSON.stringify(this.fieldFormatMap); return { title: this.title, @@ -330,12 +293,25 @@ export class IndexPattern implements IIndexPattern { getFormatterForField( field: IndexPatternField | IndexPatternField['spec'] | IFieldType ): FieldFormat { - return ( - this.fieldFormatMap[field.name] || - this.fieldFormats.getDefaultInstance( + const formatSpec = this.fieldFormatMap[field.name]; + if (formatSpec) { + return this.fieldFormats.getInstance(formatSpec.id, formatSpec.params); + } else { + return this.fieldFormats.getDefaultInstance( field.type as KBN_FIELD_TYPES, field.esTypes as ES_FIELD_TYPES[] - ) - ); + ); + } + } + + /** + * Get formatter for a given field name. Return undefined if none exists + * @param field + */ + getFormatterForFieldNoDefault(fieldname: string) { + const formatSpec = this.fieldFormatMap[fieldname]; + if (formatSpec) { + return this.fieldFormats.getInstance(formatSpec.id, formatSpec.params); + } } } diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 9a86541376cd8..bfd0dc9d946c2 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -35,7 +35,6 @@ import { IndexPatternSpec, IndexPatternAttributes, FieldSpec, - FieldFormatMap, IndexPatternFieldMap, } from '../types'; import { FieldFormatsStartCommon } from '../../field_formats'; @@ -296,20 +295,6 @@ export class IndexPatternsService { return fields; }; - /** - * Applies a set of formats to a set of fields - * @param fieldSpecs - * @param fieldFormatMap - */ - private addFormatsToFields = (fieldSpecs: FieldSpec[], fieldFormatMap: FieldFormatMap) => { - Object.entries(fieldFormatMap).forEach(([fieldName, value]) => { - const field = fieldSpecs.find((fld: FieldSpec) => fld.name === fieldName); - if (field) { - field.format = value; - } - }); - }; - /** * Converts field array to map * @param fields @@ -346,7 +331,6 @@ export class IndexPatternsService { const parsedFieldFormatMap = fieldFormatMap ? JSON.parse(fieldFormatMap) : {}; const parsedFields: FieldSpec[] = fields ? JSON.parse(fields) : []; - this.addFormatsToFields(parsedFields, parsedFieldFormatMap); return { id, version, @@ -357,6 +341,7 @@ export class IndexPatternsService { fields: this.fieldArrayToMap(parsedFields), typeMeta: parsedTypeMeta, type, + fieldFormats: parsedFieldFormatMap, }; }; @@ -382,9 +367,6 @@ export class IndexPatternsService { const spec = this.savedObjectToSpec(savedObject); const { title, type, typeMeta } = spec; - const parsedFieldFormats: FieldFormatMap = savedObject.attributes.fieldFormatMap - ? JSON.parse(savedObject.attributes.fieldFormatMap) - : {}; const isFieldRefreshRequired = this.isFieldRefreshRequired(spec.fields); let isSaveRequired = isFieldRefreshRequired; @@ -415,12 +397,9 @@ export class IndexPatternsService { } } - Object.entries(parsedFieldFormats).forEach(([fieldName, value]) => { - const field = spec.fields?.[fieldName]; - if (field) { - field.format = value; - } - }); + spec.fieldFormats = savedObject.attributes.fieldFormatMap + ? JSON.parse(savedObject.attributes.fieldFormatMap) + : {}; const indexPattern = await this.create(spec, true); indexPatternCache.set(id, indexPattern); diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index cb0c3aa0de38e..3387bc3b3c19e 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -172,6 +172,7 @@ export interface IndexPatternSpec { fields?: IndexPatternFieldMap; typeMeta?: TypeMeta; type?: string; + fieldFormats?: Record; } export interface SourceFilter { diff --git a/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts b/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts index 6a2d6edd04692..dd1a9a7f689a9 100644 --- a/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts +++ b/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts @@ -66,6 +66,7 @@ describe('utils/kbn_field_types', () => { test('returns the kbnFieldType name that matches the esType', () => { expect(castEsToKbnFieldTypeName(ES_FIELD_TYPES.KEYWORD)).toBe('string'); expect(castEsToKbnFieldTypeName(ES_FIELD_TYPES.FLOAT)).toBe('number'); + expect(castEsToKbnFieldTypeName(ES_FIELD_TYPES.UNSIGNED_LONG)).toBe('number'); }); test('returns unknown for unknown es types', () => { diff --git a/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts b/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts index b93ebcbbca9c8..373cdfda30607 100644 --- a/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts +++ b/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts @@ -48,6 +48,7 @@ export const createKbnFieldTypes = (): KbnFieldType[] => [ ES_FIELD_TYPES.DOUBLE, ES_FIELD_TYPES.INTEGER, ES_FIELD_TYPES.LONG, + ES_FIELD_TYPES.UNSIGNED_LONG, ES_FIELD_TYPES.SHORT, ES_FIELD_TYPES.BYTE, ES_FIELD_TYPES.TOKEN_COUNT, diff --git a/src/plugins/data/common/kbn_field_types/types.ts b/src/plugins/data/common/kbn_field_types/types.ts index acd7a36b01fb3..ba9fd3e70b315 100644 --- a/src/plugins/data/common/kbn_field_types/types.ts +++ b/src/plugins/data/common/kbn_field_types/types.ts @@ -52,6 +52,7 @@ export enum ES_FIELD_TYPES { INTEGER = 'integer', LONG = 'long', SHORT = 'short', + UNSIGNED_LONG = 'unsigned_long', NESTED = 'nested', BYTE = 'byte', diff --git a/src/plugins/data/common/mocks.ts b/src/plugins/data/common/mocks.ts new file mode 100644 index 0000000000000..dde70b1d07443 --- /dev/null +++ b/src/plugins/data/common/mocks.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { getSessionServiceMock } from './search/session/mocks'; diff --git a/src/plugins/data/common/search/es_search/types.ts b/src/plugins/data/common/search/es_search/types.ts index b1c3e5cdd3960..4d3bc088749a9 100644 --- a/src/plugins/data/common/search/es_search/types.ts +++ b/src/plugins/data/common/search/es_search/types.ts @@ -31,6 +31,11 @@ export interface ISearchOptions { * Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. */ strategy?: string; + + /** + * A session ID, grouping multiple search requests into a single session. + */ + sessionId?: string; } export type ISearchRequestParams> = { diff --git a/src/plugins/data/common/search/expressions/esaggs.ts b/src/plugins/data/common/search/expressions/esaggs.ts index 2957512886b4d..4f65babdcd360 100644 --- a/src/plugins/data/common/search/expressions/esaggs.ts +++ b/src/plugins/data/common/search/expressions/esaggs.ts @@ -19,12 +19,12 @@ import { KibanaContext, - KibanaDatatable, + Datatable, ExpressionFunctionDefinition, } from '../../../../../plugins/expressions/common'; type Input = KibanaContext | null; -type Output = Promise; +type Output = Promise; interface Arguments { index: string; diff --git a/src/plugins/data/common/search/index.ts b/src/plugins/data/common/search/index.ts index 2ee0db384cf06..e650cf10db87c 100644 --- a/src/plugins/data/common/search/index.ts +++ b/src/plugins/data/common/search/index.ts @@ -23,3 +23,4 @@ export * from './expressions'; export * from './search_source'; export * from './tabify'; export * from './types'; +export * from './session'; diff --git a/src/plugins/data/public/search/expressions/utils/index.ts b/src/plugins/data/common/search/session/index.ts similarity index 95% rename from src/plugins/data/public/search/expressions/utils/index.ts rename to src/plugins/data/common/search/session/index.ts index 094536fc18437..d8f7b5091eb8f 100644 --- a/src/plugins/data/public/search/expressions/utils/index.ts +++ b/src/plugins/data/common/search/session/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export * from './serialize_agg_config'; +export * from './types'; diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/lib/get_field_format.ts b/src/plugins/data/common/search/session/mocks.ts similarity index 72% rename from src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/lib/get_field_format.ts rename to src/plugins/data/common/search/session/mocks.ts index 861017d99962e..7d5cd75b57534 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/lib/get_field_format.ts +++ b/src/plugins/data/common/search/session/mocks.ts @@ -17,11 +17,13 @@ * under the License. */ -import { get } from 'lodash'; -import { IIndexPattern } from '../../../../../../data/public'; +import { ISessionService } from './types'; -export function getFieldFormat(indexPattern?: IIndexPattern, fieldName?: string): string { - return indexPattern && fieldName - ? get(indexPattern, ['fieldFormatMap', fieldName, 'type', 'title']) - : ''; +export function getSessionServiceMock(): jest.Mocked { + return { + clear: jest.fn(), + start: jest.fn(), + getSessionId: jest.fn(), + getSession$: jest.fn(), + }; } diff --git a/src/plugins/data/common/search/session/types.ts b/src/plugins/data/common/search/session/types.ts new file mode 100644 index 0000000000000..80ab74f1aa14d --- /dev/null +++ b/src/plugins/data/common/search/session/types.ts @@ -0,0 +1,41 @@ +/* + * 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 { Observable } from 'rxjs'; + +export interface ISessionService { + /** + * Returns the active session ID + * @returns The active session ID + */ + getSessionId: () => string | undefined; + /** + * Returns the observable that emits an update every time the session ID changes + * @returns `Observable` + */ + getSession$: () => Observable; + /** + * Starts a new session + */ + start: () => string; + /** + * Clears the active session. + */ + clear: () => void; +} diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts index aa96d77d873d3..6dcfa4d02bcb2 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts @@ -21,7 +21,12 @@ import moment from 'moment'; import { createFiltersFromRangeSelectAction } from './create_filters_from_range_select'; -import { IndexPatternsContract, RangeFilter } from '../../../public'; +import { + fieldFormats, + FieldFormatsGetConfigFn, + IndexPatternsContract, + RangeFilter, +} from '../../../public'; import { dataPluginMock } from '../../../public/mocks'; import { setIndexPatterns, setSearchService } from '../../../public/services'; import { TriggerContextMapping } from '../../../../ui_actions/public'; @@ -31,23 +36,30 @@ describe('brushEvent', () => { const JAN_01_2014 = 1388559600000; let baseEvent: TriggerContextMapping['SELECT_RANGE_TRIGGER']['data']; + const mockField = { + name: 'time', + indexPattern: { + id: 'logstash-*', + }, + filterable: true, + format: new fieldFormats.DateFormat({}, (() => {}) as FieldFormatsGetConfigFn), + }; + const indexPattern = { id: 'indexPatternId', timeFieldName: 'time', fields: { - getByName: () => undefined, - filter: () => [], + getByName: () => mockField, + filter: () => [mockField], }, }; - const aggConfigs = [ - { - params: { - field: {}, - }, - getIndexPattern: () => indexPattern, + const serializedAggConfig = { + type: 'date_histogram', + params: { + field: {}, }, - ]; + }; beforeEach(() => { const dataStart = dataPluginMock.createStartContract(); @@ -60,15 +72,18 @@ describe('brushEvent', () => { baseEvent = { column: 0, table: { - type: 'kibana_datatable', + type: 'datatable', columns: [ { id: '1', name: '1', meta: { - type: 'histogram', - indexPatternId: 'indexPatternId', - aggConfigParams: aggConfigs[0].params, + type: 'date', + sourceParams: { + indexPatternId: 'indexPatternId', + ...serializedAggConfig, + }, + source: 'esaggs', }, }, ], @@ -90,7 +105,7 @@ describe('brushEvent', () => { describe('handles an event when the x-axis field is a date field', () => { describe('date field is index pattern timefield', () => { beforeEach(() => { - aggConfigs[0].params.field = { + serializedAggConfig.params.field = { name: 'time', type: 'date', }; @@ -98,7 +113,7 @@ describe('brushEvent', () => { afterAll(() => { baseEvent.range = []; - aggConfigs[0].params.field = {}; + serializedAggConfig.params.field = {}; }); test('by ignoring the event when range spans zero time', async () => { @@ -123,7 +138,7 @@ describe('brushEvent', () => { describe('date field is not index pattern timefield', () => { beforeEach(() => { - aggConfigs[0].params.field = { + serializedAggConfig.params.field = { name: 'anotherTimeField', type: 'date', }; @@ -131,7 +146,7 @@ describe('brushEvent', () => { afterAll(() => { baseEvent.range = []; - aggConfigs[0].params.field = {}; + serializedAggConfig.params.field = {}; }); test('creates a new range filter', async () => { @@ -157,7 +172,7 @@ describe('brushEvent', () => { describe('handles an event when the x-axis field is a number', () => { beforeAll(() => { - aggConfigs[0].params.field = { + serializedAggConfig.params.field = { name: 'numberField', type: 'number', }; diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts index d9aa1b8ec8048..2d7aeff79a689 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts @@ -20,9 +20,9 @@ import { last } from 'lodash'; import moment from 'moment'; import { esFilters, IFieldType, RangeFilterParams } from '../../../public'; -import { getIndexPatterns } from '../../../public/services'; -import { deserializeAggConfig } from '../../search/expressions/utils'; -import type { RangeSelectContext } from '../../../../embeddable/public'; +import { getIndexPatterns, getSearchService } from '../../../public/services'; +import { RangeSelectContext } from '../../../../embeddable/public'; +import { AggConfigSerialized } from '../../../common/search/aggs'; export async function createFiltersFromRangeSelectAction(event: RangeSelectContext['data']) { const column: Record = event.table.columns[event.column]; @@ -31,11 +31,12 @@ export async function createFiltersFromRangeSelectAction(event: RangeSelectConte return []; } - const indexPattern = await getIndexPatterns().get(column.meta.indexPatternId); - const aggConfig = deserializeAggConfig({ - ...column.meta, - indexPattern, - }); + const { indexPatternId, ...aggConfigs } = column.meta.sourceParams; + const indexPattern = await getIndexPatterns().get(indexPatternId); + const aggConfigsInstance = getSearchService().aggs.createAggConfigs(indexPattern, [ + aggConfigs as AggConfigSerialized, + ]); + const aggConfig = aggConfigsInstance.aggs[0]; const field: IFieldType = aggConfig.params.field; if (!field || event.range.length <= 1) { diff --git a/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts index 2ad20c3807819..23d2ab080d75e 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts @@ -45,12 +45,16 @@ describe('createFiltersFromValueClick', () => { name: 'test', id: '1-1', meta: { - type: 'histogram', - indexPatternId: 'logstash-*', - aggConfigParams: { - field: 'bytes', - interval: 30, - otherBucket: true, + type: 'date', + source: 'esaggs', + sourceParams: { + indexPatternId: 'logstash-*', + type: 'histogram', + params: { + field: 'bytes', + interval: 30, + otherBucket: true, + }, }, }, }, @@ -91,9 +95,7 @@ describe('createFiltersFromValueClick', () => { }); test('handles an event when aggregations type is a terms', async () => { - if (dataPoints[0].table.columns[0].meta) { - dataPoints[0].table.columns[0].meta.type = 'terms'; - } + (dataPoints[0].table.columns[0].meta.sourceParams as any).type = 'terms'; const filters = await createFiltersFromValueClickAction({ data: dataPoints }); expect(filters.length).toEqual(1); diff --git a/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts b/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts index 9429df91f693c..ce7ecf434056a 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts @@ -17,11 +17,11 @@ * under the License. */ -import { KibanaDatatable } from '../../../../../plugins/expressions/public'; -import { deserializeAggConfig } from '../../search/expressions'; +import { Datatable } from '../../../../../plugins/expressions/public'; import { esFilters, Filter } from '../../../public'; -import { getIndexPatterns } from '../../../public/services'; -import type { ValueClickContext } from '../../../../embeddable/public'; +import { getIndexPatterns, getSearchService } from '../../../public/services'; +import { ValueClickContext } from '../../../../embeddable/public'; +import { AggConfigSerialized } from '../../../common/search/aggs'; /** * For terms aggregations on `__other__` buckets, this assembles a list of applicable filter @@ -33,7 +33,7 @@ import type { ValueClickContext } from '../../../../embeddable/public'; * @return {array} - array of terms to filter against */ const getOtherBucketFilterTerms = ( - table: Pick, + table: Pick, columnIndex: number, rowIndex: number ) => { @@ -71,22 +71,28 @@ const getOtherBucketFilterTerms = ( * @return {Filter[]|undefined} - list of filters to provide to queryFilter.addFilters() */ const createFilter = async ( - table: Pick, + table: Pick, columnIndex: number, rowIndex: number ) => { - if (!table || !table.columns || !table.columns[columnIndex]) { + if ( + !table || + !table.columns || + !table.columns[columnIndex] || + !table.columns[columnIndex].meta || + table.columns[columnIndex].meta.source !== 'esaggs' || + !table.columns[columnIndex].meta.sourceParams?.indexPatternId + ) { return; } const column = table.columns[columnIndex]; - if (!column.meta || !column.meta.indexPatternId) { - return; - } - const aggConfig = deserializeAggConfig({ - type: column.meta.type, - aggConfigParams: column.meta.aggConfigParams ? column.meta.aggConfigParams : {}, - indexPattern: await getIndexPatterns().get(column.meta.indexPatternId), - }); + const { indexPatternId, ...aggConfigParams } = table.columns[columnIndex].meta + .sourceParams as any; + const aggConfigsInstance = getSearchService().aggs.createAggConfigs( + await getIndexPatterns().get(indexPatternId), + [aggConfigParams as AggConfigSerialized] + ); + const aggConfig = aggConfigsInstance.aggs[0]; let filter: Filter[] = []; const value: any = rowIndex > -1 ? table.rows[rowIndex][column.id] : null; if (value === null || value === undefined || !aggConfig.isFilterable()) { diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 2ed3e440040de..1390b28ec830d 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -514,7 +514,9 @@ export enum ES_FIELD_TYPES { // (undocumented) TOKEN_COUNT = "token_count", // (undocumented) - _TYPE = "_type" + _TYPE = "_type", + // (undocumented) + UNSIGNED_LONG = "unsigned_long" } // Warning: (ae-missing-release-tag) "ES_SEARCH_STRATEGY" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1063,6 +1065,8 @@ export class IndexPattern implements IIndexPattern { constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); addScriptedField(name: string, script: string, fieldType?: string): Promise; // (undocumented) + deleteFieldFormat: (fieldName: string) => void; + // (undocumented) fieldFormatMap: Record; // (undocumented) fields: IIndexPatternFieldList & { @@ -1108,6 +1112,7 @@ export class IndexPattern implements IIndexPattern { // (undocumented) getFieldByName(name: string): IndexPatternField | undefined; getFormatterForField(field: IndexPatternField | IndexPatternField['spec'] | IFieldType): FieldFormat; + getFormatterForFieldNoDefault(fieldname: string): FieldFormat | undefined; // (undocumented) getNonScriptedFields(): IndexPatternField[]; getOriginalSavedObjectBody: () => { @@ -1139,6 +1144,8 @@ export class IndexPattern implements IIndexPattern { metaFields: string[]; removeScriptedField(fieldName: string): void; resetOriginalSavedObjectBody: () => void; + // (undocumented) + setFieldFormat: (fieldName: string, format: SerializedFieldFormat) => void; // Warning: (ae-forgotten-export) The symbol "SourceFilter" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -1287,6 +1294,8 @@ export type IndexPatternSelectProps = Required, 'isLo // // @public (undocumented) export interface IndexPatternSpec { + // (undocumented) + fieldFormats?: Record; // (undocumented) fields?: IndexPatternFieldMap; // (undocumented) @@ -1389,6 +1398,7 @@ export type ISearchGeneric = void; } @@ -1987,7 +2002,7 @@ export class SearchInterceptor { // (undocumented) protected getTimeoutMode(): TimeoutErrorMode; // (undocumented) - protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; + protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, options?: ISearchOptions): Error; // @internal protected pendingCount$: BehaviorSubject; // @internal (undocumented) @@ -1998,8 +2013,8 @@ export class SearchInterceptor { abortSignal?: AbortSignal; timeout?: number; }): { - combinedSignal: AbortSignal; timeoutSignal: AbortSignal; + combinedSignal: AbortSignal; cleanup: () => void; }; // (undocumented) @@ -2013,6 +2028,8 @@ export interface SearchInterceptorDeps { // (undocumented) http: CoreSetup_2['http']; // (undocumented) + session: ISessionService; + // (undocumented) startServices: Promise<[CoreStart, any, unknown]>; // (undocumented) toasts: ToastsSetup; @@ -2266,7 +2283,7 @@ export const UI_SETTINGS: { // src/plugins/data/common/es_query/filters/meta_filter.ts:54:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrase_filter.ts:33:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrases_filter.ts:31:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:70:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:64:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/aggs/types.ts:98:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts // src/plugins/data/public/field_formats/field_formats_service.ts:67:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/query/query_string/query_string_manager.test.ts b/src/plugins/data/public/query/query_string/query_string_manager.test.ts new file mode 100644 index 0000000000000..aa1556480452a --- /dev/null +++ b/src/plugins/data/public/query/query_string/query_string_manager.test.ts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { QueryStringManager } from './query_string_manager'; +import { Storage } from '../../../../kibana_utils/public/storage'; +import { StubBrowserStorage } from 'test_utils/stub_browser_storage'; +import { coreMock } from '../../../../../core/public/mocks'; +import { Query } from '../../../common/query'; + +describe('QueryStringManager', () => { + let service: QueryStringManager; + + beforeEach(() => { + service = new QueryStringManager( + new Storage(new StubBrowserStorage()), + coreMock.createSetup().uiSettings + ); + }); + + test('getUpdates$ is a cold emits only after query changes', () => { + const obs$ = service.getUpdates$(); + const emittedValues: Query[] = []; + obs$.subscribe((v) => { + emittedValues.push(v); + }); + expect(emittedValues).toHaveLength(0); + + const newQuery = { query: 'new query', language: 'kquery' }; + service.setQuery(newQuery); + expect(emittedValues).toHaveLength(1); + expect(emittedValues[0]).toEqual(newQuery); + + service.setQuery({ ...newQuery }); + expect(emittedValues).toHaveLength(1); + }); +}); diff --git a/src/plugins/data/public/query/query_string/query_string_manager.ts b/src/plugins/data/public/query/query_string/query_string_manager.ts index bd02830f4aed8..50732c99a62d9 100644 --- a/src/plugins/data/public/query/query_string/query_string_manager.ts +++ b/src/plugins/data/public/query/query_string/query_string_manager.ts @@ -17,8 +17,8 @@ * under the License. */ -import _ from 'lodash'; import { BehaviorSubject } from 'rxjs'; +import { skip } from 'rxjs/operators'; import { CoreStart } from 'kibana/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { Query, UI_SETTINGS } from '../../../common'; @@ -61,7 +61,7 @@ export class QueryStringManager { } public getUpdates$ = () => { - return this.query$.asObservable(); + return this.query$.asObservable().pipe(skip(1)); }; public getQuery = (): Query => { diff --git a/src/plugins/data/public/search/expressions/esaggs.ts b/src/plugins/data/public/search/expressions/esaggs.ts index 1021ef0f91d52..de7a4ffce8bd4 100644 --- a/src/plugins/data/public/search/expressions/esaggs.ts +++ b/src/plugins/data/public/search/expressions/esaggs.ts @@ -19,7 +19,7 @@ import { get, hasIn } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { KibanaDatatable, KibanaDatatableColumn } from 'src/plugins/expressions/public'; +import { Datatable, DatatableColumn } from 'src/plugins/expressions/public'; import { PersistedState } from '../../../../../plugins/visualizations/public'; import { Adapters } from '../../../../../plugins/inspector/public'; @@ -49,7 +49,6 @@ import { getSearchService, } from '../../services'; import { buildTabularInspectorData } from './build_tabular_inspector_data'; -import { serializeAggConfig } from './utils'; export interface RequestHandlerParams { searchSource: ISearchSource; @@ -149,7 +148,9 @@ const handleCourierRequest = async ({ request.stats(getRequestInspectorStats(requestSearchSource)); try { - const response = await requestSearchSource.fetch({ abortSignal }); + const response = await requestSearchSource.fetch({ + abortSignal, + }); request.stats(getResponseInspectorStats(response, searchSource)).ok({ json: response }); @@ -193,11 +194,9 @@ const handleCourierRequest = async ({ : undefined, }; - (searchSource as any).tabifiedResponse = tabifyAggResponse( - aggs, - (searchSource as any).finalResponse, - tabifyParams - ); + const response = tabifyAggResponse(aggs, (searchSource as any).finalResponse, tabifyParams); + + (searchSource as any).tabifiedResponse = response; inspectorAdapters.data.setTabularLoader( () => @@ -208,12 +207,12 @@ const handleCourierRequest = async ({ { returnsFormattedValues: true } ); - return (searchSource as any).tabifiedResponse; + return response; }; export const esaggs = (): EsaggsExpressionFunctionDefinition => ({ name, - type: 'kibana_datatable', + type: 'datatable', inputTypes: ['kibana_context', 'null'], help: i18n.translate('data.functions.esaggs.help', { defaultMessage: 'Run AggConfig aggregation', @@ -279,18 +278,25 @@ export const esaggs = (): EsaggsExpressionFunctionDefinition => ({ abortSignal: (abortSignal as unknown) as AbortSignal, }); - const table: KibanaDatatable = { - type: 'kibana_datatable', + const table: Datatable = { + type: 'datatable', rows: response.rows, - columns: response.columns.map((column: any) => { - const cleanedColumn: KibanaDatatableColumn = { + columns: response.columns.map((column) => { + const cleanedColumn: DatatableColumn = { id: column.id, name: column.name, - meta: serializeAggConfig(column.aggConfig), + meta: { + type: column.aggConfig.params.field?.type || 'number', + field: column.aggConfig.params.field?.name, + index: indexPattern.title, + params: column.aggConfig.toSerializedFieldFormat(), + source: 'esaggs', + sourceParams: { + indexPatternId: indexPattern.id, + ...column.aggConfig.serialize(), + }, + }, }; - if (args.includeFormatHints) { - cleanedColumn.formatHint = column.aggConfig.toSerializedFieldFormat(); - } return cleanedColumn; }), }; diff --git a/src/plugins/data/public/search/expressions/index.ts b/src/plugins/data/public/search/expressions/index.ts index 02df7986479ad..98ed1d08af8ad 100644 --- a/src/plugins/data/public/search/expressions/index.ts +++ b/src/plugins/data/public/search/expressions/index.ts @@ -20,4 +20,3 @@ export * from './esaggs'; export * from './es_raw_response'; export * from './esdsl'; -export * from './utils'; diff --git a/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts b/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts deleted file mode 100644 index 6ba323b65783f..0000000000000 --- a/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts +++ /dev/null @@ -1,54 +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 { KibanaDatatableColumnMeta } from '../../../../../../plugins/expressions/public'; -import { IAggConfig } from '../../../../common'; -import { IndexPattern } from '../../../index_patterns'; -import { getSearchService } from '../../../../public/services'; - -/** @internal */ -export const serializeAggConfig = (aggConfig: IAggConfig): KibanaDatatableColumnMeta => { - return { - type: aggConfig.type.name, - indexPatternId: aggConfig.getIndexPattern().id, - aggConfigParams: aggConfig.serialize().params, - }; -}; - -interface DeserializeAggConfigParams { - type: string; - aggConfigParams: Record; - indexPattern: IndexPattern; -} - -/** @internal */ -export const deserializeAggConfig = ({ - type, - aggConfigParams, - indexPattern, -}: DeserializeAggConfigParams) => { - const { aggs } = getSearchService(); - const aggConfigs = aggs.createAggConfigs(indexPattern); - const aggConfig = aggConfigs.createAggConfig({ - enabled: true, - type, - params: aggConfigParams, - }); - return aggConfig; -}; diff --git a/src/plugins/data/public/search/mocks.ts b/src/plugins/data/public/search/mocks.ts index 8bad4cd269b3f..836ddb618e746 100644 --- a/src/plugins/data/public/search/mocks.ts +++ b/src/plugins/data/public/search/mocks.ts @@ -20,11 +20,13 @@ import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks'; import { searchSourceMock } from './search_source/mocks'; import { ISearchSetup, ISearchStart } from './types'; +import { getSessionServiceMock } from '../../common/mocks'; function createSetupContract(): jest.Mocked { return { aggs: searchAggsSetupMock(), __enhance: jest.fn(), + session: getSessionServiceMock(), }; } @@ -33,6 +35,7 @@ function createStartContract(): jest.Mocked { aggs: searchAggsStartMock(), search: jest.fn(), showError: jest.fn(), + session: getSessionServiceMock(), searchSource: searchSourceMock.createStartContract(), }; } diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index ade15adc1c3a3..e8a728bb9cec3 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -17,12 +17,14 @@ * under the License. */ -import { CoreSetup } from '../../../../core/public'; +import { CoreSetup, CoreStart } from '../../../../core/public'; import { coreMock } from '../../../../core/public/mocks'; import { IEsSearchRequest } from '../../common/search'; import { SearchInterceptor } from './search_interceptor'; import { AbortError } from '../../common'; -import { SearchTimeoutError, PainlessError } from './errors'; +import { SearchTimeoutError, PainlessError, TimeoutErrorMode } from './errors'; +import { searchServiceMock } from './mocks'; +import { ISearchStart } from '.'; let searchInterceptor: SearchInterceptor; let mockCoreSetup: MockedKeys; @@ -31,13 +33,61 @@ const flushPromises = () => new Promise((resolve) => setImmediate(resolve)); jest.useFakeTimers(); describe('SearchInterceptor', () => { + let searchMock: jest.Mocked; + let mockCoreStart: MockedKeys; beforeEach(() => { mockCoreSetup = coreMock.createSetup(); + mockCoreStart = coreMock.createStart(); + searchMock = searchServiceMock.createStartContract(); searchInterceptor = new SearchInterceptor({ toasts: mockCoreSetup.notifications.toasts, - startServices: mockCoreSetup.getStartServices(), + startServices: new Promise((resolve) => { + resolve([mockCoreStart, {}, {}]); + }), uiSettings: mockCoreSetup.uiSettings, http: mockCoreSetup.http, + session: searchMock.session, + }); + }); + + describe('showError', () => { + test('Ignores an AbortError', async () => { + searchInterceptor.showError(new AbortError()); + expect(mockCoreSetup.notifications.toasts.addDanger).not.toBeCalled(); + expect(mockCoreSetup.notifications.toasts.addError).not.toBeCalled(); + }); + + test('Ignores a SearchTimeoutError', async () => { + searchInterceptor.showError(new SearchTimeoutError(new Error(), TimeoutErrorMode.UPGRADE)); + expect(mockCoreSetup.notifications.toasts.addDanger).not.toBeCalled(); + expect(mockCoreSetup.notifications.toasts.addError).not.toBeCalled(); + }); + + test('Renders a PainlessError', async () => { + searchInterceptor.showError( + new PainlessError( + { + body: { + attributes: { + error: { + failed_shards: { + reason: 'bananas', + }, + }, + }, + } as any, + }, + {} as any + ) + ); + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); + expect(mockCoreSetup.notifications.toasts.addError).not.toBeCalled(); + }); + + test('Renders a general error', async () => { + searchInterceptor.showError(new Error('Oopsy')); + expect(mockCoreSetup.notifications.toasts.addDanger).not.toBeCalled(); + expect(mockCoreSetup.notifications.toasts.addError).toBeCalledTimes(1); }); }); @@ -49,149 +99,172 @@ describe('SearchInterceptor', () => { params: {}, }; const response = searchInterceptor.search(mockRequest); - - const result = await response.toPromise(); - expect(result).toBe(mockResponse); + expect(response.toPromise()).resolves.toBe(mockResponse); }); - test('Observable should fail if fetch has an internal error', async () => { - const mockResponse: any = { result: 500, message: 'Internal Error' }; - mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest); + describe('Should throw typed errors', () => { + test('Observable should fail if fetch has an internal error', async () => { + const mockResponse: any = new Error('Internal Error'); + mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest); + await expect(response.toPromise()).rejects.toThrow('Internal Error'); + }); - try { - await response.toPromise(); - } catch (e) { - expect(e).toBe(mockResponse); - } - }); + describe('Should handle Timeout errors', () => { + test('Should throw SearchTimeoutError on server timeout AND show toast', async () => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest); + await expect(response.toPromise()).rejects.toThrow(SearchTimeoutError); + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); + }); - test('Should throw SearchTimeoutError on server timeout AND show toast', async (done) => { - const mockResponse: any = { - result: 500, - body: { - message: 'Request timed out', - }, - }; - mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest); + test('Timeout error should show multiple times if not in a session', async () => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; - try { - await response.toPromise(); - } catch (e) { - expect(e).toBeInstanceOf(SearchTimeoutError); - expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); - done(); - } - }); + await expect(searchInterceptor.search(mockRequest).toPromise()).rejects.toThrow( + SearchTimeoutError + ); + await expect(searchInterceptor.search(mockRequest).toPromise()).rejects.toThrow( + SearchTimeoutError + ); + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(2); + }); - test('Search error should be debounced', async (done) => { - const mockResponse: any = { - result: 500, - body: { - message: 'Request timed out', - }, - }; - mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - try { - await searchInterceptor.search(mockRequest).toPromise(); - } catch (e) { - expect(e).toBeInstanceOf(SearchTimeoutError); - try { - await searchInterceptor.search(mockRequest).toPromise(); - } catch (e2) { + test('Timeout error should show once per each session', async () => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + + await expect( + searchInterceptor.search(mockRequest, { sessionId: 'abc' }).toPromise() + ).rejects.toThrow(SearchTimeoutError); + await expect( + searchInterceptor.search(mockRequest, { sessionId: 'def' }).toPromise() + ).rejects.toThrow(SearchTimeoutError); + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(2); + }); + + test('Timeout error should show once in a single session', async () => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + await expect( + searchInterceptor.search(mockRequest, { sessionId: 'abc' }).toPromise() + ).rejects.toThrow(SearchTimeoutError); + await expect( + searchInterceptor.search(mockRequest, { sessionId: 'abc' }).toPromise() + ).rejects.toThrow(SearchTimeoutError); expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); - done(); - } - } - }); + }); + }); - test('Should throw Painless error on server error with OSS format', async (done) => { - const mockResponse: any = { - result: 500, - body: { - attributes: { - error: { - failed_shards: [ - { - reason: { - lang: 'painless', - script_stack: ['a', 'b'], - reason: 'banana', + test('Should throw Painless error on server error with OSS format', async () => { + const mockResponse: any = { + result: 500, + body: { + attributes: { + error: { + failed_shards: [ + { + reason: { + lang: 'painless', + script_stack: ['a', 'b'], + reason: 'banana', + }, }, - }, - ], + ], + }, }, }, - }, - }; - mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest); + }; + mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest); + await expect(response.toPromise()).rejects.toThrow(PainlessError); + }); - try { - await response.toPromise(); - } catch (e) { - expect(e).toBeInstanceOf(PainlessError); - done(); - } - }); + test('Observable should fail if user aborts (test merged signal)', async () => { + const abortController = new AbortController(); + mockCoreSetup.http.fetch.mockImplementationOnce((options: any) => { + return new Promise((resolve, reject) => { + options.signal.addEventListener('abort', () => { + reject(new AbortError()); + }); - test('Observable should fail if user aborts (test merged signal)', async () => { - const abortController = new AbortController(); - mockCoreSetup.http.fetch.mockImplementationOnce((options: any) => { - return new Promise((resolve, reject) => { - options.signal.addEventListener('abort', () => { - reject(new AbortError()); + setTimeout(resolve, 500); }); - - setTimeout(resolve, 500); }); - }); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest, { - abortSignal: abortController.signal, - }); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest, { + abortSignal: abortController.signal, + }); - const next = jest.fn(); - const error = (e: any) => { - expect(next).not.toBeCalled(); - expect(e).toBeInstanceOf(AbortError); - }; - response.subscribe({ next, error }); - setTimeout(() => abortController.abort(), 200); - jest.advanceTimersByTime(5000); + const next = jest.fn(); + const error = (e: any) => { + expect(next).not.toBeCalled(); + expect(e).toBeInstanceOf(AbortError); + }; + response.subscribe({ next, error }); + setTimeout(() => abortController.abort(), 200); + jest.advanceTimersByTime(5000); - await flushPromises(); - }); + await flushPromises(); + }); - test('Immediately aborts if passed an aborted abort signal', async (done) => { - const abort = new AbortController(); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest, { abortSignal: abort.signal }); - abort.abort(); + test('Immediately aborts if passed an aborted abort signal', async (done) => { + const abort = new AbortController(); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest, { abortSignal: abort.signal }); + abort.abort(); - const error = (e: any) => { - expect(e).toBeInstanceOf(AbortError); - expect(mockCoreSetup.http.fetch).not.toBeCalled(); - done(); - }; - response.subscribe({ error }); + const error = (e: any) => { + expect(e).toBeInstanceOf(AbortError); + expect(mockCoreSetup.http.fetch).not.toBeCalled(); + done(); + }; + response.subscribe({ error }); + }); }); }); }); diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 2e42635a7f811..e3c6dd3e287d4 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -17,7 +17,7 @@ * under the License. */ -import { get, trimEnd, debounce } from 'lodash'; +import { get, memoize, trimEnd } from 'lodash'; import { BehaviorSubject, throwError, timer, defer, from, Observable, NEVER } from 'rxjs'; import { catchError, finalize } from 'rxjs/operators'; import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public'; @@ -28,6 +28,7 @@ import { IKibanaSearchResponse, ISearchOptions, ES_SEARCH_STRATEGY, + ISessionService, } from '../../common'; import { SearchUsageCollector } from './collectors'; import { SearchTimeoutError, PainlessError, isPainlessError, TimeoutErrorMode } from './errors'; @@ -39,6 +40,7 @@ export interface SearchInterceptorDeps { startServices: Promise<[CoreStart, any, unknown]>; toasts: ToastsSetup; usageCollector?: SearchUsageCollector; + session: ISessionService; } export class SearchInterceptor { @@ -86,16 +88,17 @@ export class SearchInterceptor { e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, - appAbortSignal?: AbortSignal + options?: ISearchOptions ): Error { if (timeoutSignal.aborted || get(e, 'body.message') === 'Request timed out') { // Handle a client or a server side timeout const err = new SearchTimeoutError(e, this.getTimeoutMode()); // Show the timeout error here, so that it's shown regardless of how an application chooses to handle errors. - this.showTimeoutError(err); + // The timeout error is shown any time a request times out, or once per session, if the request is part of a session. + this.showTimeoutError(err, options?.sessionId); return err; - } else if (appAbortSignal?.aborted) { + } else if (options?.abortSignal?.aborted) { // In the case an application initiated abort, throw the existing AbortError. return e; } else if (isPainlessError(e)) { @@ -162,27 +165,37 @@ export class SearchInterceptor { combinedSignal.addEventListener('abort', cleanup); return { - combinedSignal, timeoutSignal, + combinedSignal, cleanup, }; } + private showTimeoutErrorToast = (e: SearchTimeoutError, sessionId?: string) => { + this.deps.toasts.addDanger({ + title: 'Timed out', + text: toMountPoint(e.getErrorMessage(this.application)), + }); + }; + + private showTimeoutErrorMemoized = memoize( + this.showTimeoutErrorToast, + (_: SearchTimeoutError, sessionId: string) => { + return sessionId; + } + ); + /** - * Right now we are throttling but we will hook this up with background sessions to show only one - * error notification per session. + * Show one error notification per session. * @internal */ - private showTimeoutError = debounce( - (e: SearchTimeoutError) => { - this.deps.toasts.addDanger({ - title: 'Timed out', - text: toMountPoint(e.getErrorMessage(this.application)), - }); - }, - 30000, - { leading: true, trailing: false } - ); + private showTimeoutError = (e: SearchTimeoutError, sessionId?: string) => { + if (sessionId) { + this.showTimeoutErrorMemoized(e, sessionId); + } else { + this.showTimeoutErrorToast(e, sessionId); + } + }; /** * Searches using the given `search` method. Overrides the `AbortSignal` with one that will abort @@ -207,12 +220,9 @@ export class SearchInterceptor { abortSignal: options?.abortSignal, }); this.pendingCount$.next(this.pendingCount$.getValue() + 1); - return this.runSearch(request, combinedSignal, options?.strategy).pipe( - catchError((e: any) => { - return throwError( - this.handleSearchError(e, request, timeoutSignal, options?.abortSignal) - ); + catchError((e: Error) => { + return throwError(this.handleSearchError(e, request, timeoutSignal, options)); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 734e88e085661..f955dc5b6ebd5 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -31,6 +31,7 @@ import { ISearchOptions, SearchSourceService, SearchSourceDependencies, + ISessionService, } from '../../common/search'; import { getCallMsearch } from './legacy'; import { AggsService, AggsStartDependencies } from './aggs'; @@ -40,6 +41,7 @@ import { SearchUsageCollector, createUsageCollector } from './collectors'; import { UsageCollectionSetup } from '../../../usage_collection/public'; import { esdsl, esRawResponse } from './expressions'; import { ExpressionsSetup } from '../../../expressions/public'; +import { SessionService } from './session_service'; import { ConfigSchema } from '../../config'; import { SHARD_DELAY_AGG_NAME, @@ -64,6 +66,7 @@ export class SearchService implements Plugin { private readonly searchSourceService = new SearchSourceService(); private searchInterceptor!: ISearchInterceptor; private usageCollector?: SearchUsageCollector; + private sessionService!: ISessionService; constructor(private initializerContext: PluginInitializerContext) {} @@ -73,6 +76,7 @@ export class SearchService implements Plugin { ): ISearchSetup { this.usageCollector = createUsageCollector(getStartServices, usageCollection); + this.sessionService = new SessionService(this.initializerContext, getStartServices); /** * A global object that intercepts all searches and provides convenience methods for cancelling * all pending search requests, as well as getting the number of pending search requests. @@ -83,6 +87,7 @@ export class SearchService implements Plugin { uiSettings, startServices: getStartServices(), usageCollector: this.usageCollector!, + session: this.sessionService, }); expressions.registerFunction(esdsl); @@ -104,6 +109,7 @@ export class SearchService implements Plugin { __enhance: (enhancements: SearchEnhancements) => { this.searchInterceptor = enhancements.searchInterceptor; }, + session: this.sessionService, }; } @@ -142,6 +148,7 @@ export class SearchService implements Plugin { showError: (e: Error) => { this.searchInterceptor.showError(e); }, + session: this.sessionService, searchSource: this.searchSourceService.start(indexPatterns, searchSourceDependencies), }; } diff --git a/src/plugins/data/public/search/session_service.test.ts b/src/plugins/data/public/search/session_service.test.ts new file mode 100644 index 0000000000000..dd64d187f47d6 --- /dev/null +++ b/src/plugins/data/public/search/session_service.test.ts @@ -0,0 +1,43 @@ +/* + * 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 { SessionService } from './session_service'; +import { ISessionService } from '../../common'; +import { coreMock } from '../../../../core/public/mocks'; + +describe('Session service', () => { + let sessionService: ISessionService; + + beforeEach(() => { + const initializerContext = coreMock.createPluginInitializerContext(); + sessionService = new SessionService( + initializerContext, + coreMock.createSetup().getStartServices + ); + }); + + describe('Session management', () => { + it('Creates and clears a session', async () => { + sessionService.start(); + expect(sessionService.getSessionId()).not.toBeUndefined(); + sessionService.clear(); + expect(sessionService.getSessionId()).toBeUndefined(); + }); + }); +}); diff --git a/src/plugins/data/public/search/session_service.ts b/src/plugins/data/public/search/session_service.ts new file mode 100644 index 0000000000000..31524434af302 --- /dev/null +++ b/src/plugins/data/public/search/session_service.ts @@ -0,0 +1,80 @@ +/* + * 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 uuid from 'uuid'; +import { Subject, Subscription } from 'rxjs'; +import { PluginInitializerContext, StartServicesAccessor } from 'kibana/public'; +import { ISessionService } from '../../common/search'; +import { ConfigSchema } from '../../config'; + +export class SessionService implements ISessionService { + private sessionId?: string; + private session$: Subject = new Subject(); + private appChangeSubscription$?: Subscription; + private curApp?: string; + + constructor( + initializerContext: PluginInitializerContext, + getStartServices: StartServicesAccessor + ) { + /* + Make sure that apps don't leave sessions open. + */ + getStartServices().then(([coreStart]) => { + this.appChangeSubscription$ = coreStart.application.currentAppId$.subscribe((appName) => { + if (this.sessionId) { + const message = `Application '${this.curApp}' had an open session while navigating`; + if (initializerContext.env.mode.dev) { + // TODO: This setTimeout is necessary due to a race condition while navigating. + setTimeout(() => { + coreStart.fatalErrors.add(message); + }, 100); + } else { + // eslint-disable-next-line no-console + console.warn(message); + } + } + this.curApp = appName; + }); + }); + } + + public destroy() { + this.appChangeSubscription$?.unsubscribe(); + } + + public getSessionId() { + return this.sessionId; + } + + public getSession$() { + return this.session$.asObservable(); + } + + public start() { + this.sessionId = uuid.v4(); + this.session$.next(this.sessionId); + return this.sessionId; + } + + public clear() { + this.sessionId = undefined; + this.session$.next(this.sessionId); + } +} diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts index 85ef7aa4d97cb..c08d9f4c7be3f 100644 --- a/src/plugins/data/public/search/types.ts +++ b/src/plugins/data/public/search/types.ts @@ -21,7 +21,7 @@ import { PackageInfo } from 'kibana/server'; import { ISearchInterceptor } from './search_interceptor'; import { SearchUsageCollector } from './collectors'; import { AggsSetup, AggsSetupDependencies, AggsStartDependencies, AggsStart } from './aggs'; -import { ISearchGeneric, ISearchStartSearchSource } from '../../common/search'; +import { ISearchGeneric, ISessionService, ISearchStartSearchSource } from '../../common/search'; import { IndexPatternsContract } from '../../common/index_patterns/index_patterns'; import { UsageCollectionSetup } from '../../../usage_collection/public'; @@ -38,6 +38,11 @@ export interface SearchEnhancements { export interface ISearchSetup { aggs: AggsSetup; usageCollector?: SearchUsageCollector; + /** + * session management + * {@link ISessionService} + */ + session: ISessionService; /** * @internal */ @@ -67,6 +72,11 @@ export interface ISearchStart { * {@link ISearchStartSearchSource} */ searchSource: ISearchStartSearchSource; + /** + * session management + * {@link ISessionService} + */ + session: ISessionService; } export { SEARCH_EVENT_TYPE } from './collectors'; diff --git a/src/plugins/data/public/ui/apply_filters/apply_filters_popover.tsx b/src/plugins/data/public/ui/apply_filters/apply_filters_popover.tsx index 80e1a26163b72..19606cafc5c8a 100644 --- a/src/plugins/data/public/ui/apply_filters/apply_filters_popover.tsx +++ b/src/plugins/data/public/ui/apply_filters/apply_filters_popover.tsx @@ -18,17 +18,12 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import { IIndexPattern, Filter } from '../..'; type CancelFnType = () => void; type SubmitFnType = (filters: Filter[]) => void; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazyApplyFiltersPopoverContent = React.lazy(() => import('./apply_filter_popover_content')); diff --git a/src/plugins/data/public/ui/filter_bar/index.tsx b/src/plugins/data/public/ui/filter_bar/index.tsx index b4296bb6615d4..4d9ba69afd48e 100644 --- a/src/plugins/data/public/ui/filter_bar/index.tsx +++ b/src/plugins/data/public/ui/filter_bar/index.tsx @@ -18,14 +18,9 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import type { FilterLabelProps } from './filter_editor/lib/filter_label'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazyFilterLabel = React.lazy(() => import('./filter_editor/lib/filter_label')); export const FilterLabel = (props: FilterLabelProps) => ( diff --git a/src/plugins/data/public/ui/index_pattern_select/index.tsx b/src/plugins/data/public/ui/index_pattern_select/index.tsx index f0db37eb963fd..c909b202a4094 100644 --- a/src/plugins/data/public/ui/index_pattern_select/index.tsx +++ b/src/plugins/data/public/ui/index_pattern_select/index.tsx @@ -18,14 +18,9 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import type { IndexPatternSelectInternalProps } from './index_pattern_select'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazyIndexPatternSelect = React.lazy(() => import('./index_pattern_select')); export const IndexPatternSelect = (props: IndexPatternSelectInternalProps) => ( diff --git a/src/plugins/data/public/ui/query_string_input/index.tsx b/src/plugins/data/public/ui/query_string_input/index.tsx index 5bc5bd5097969..eb6641bf3661e 100644 --- a/src/plugins/data/public/ui/query_string_input/index.tsx +++ b/src/plugins/data/public/ui/query_string_input/index.tsx @@ -18,16 +18,11 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import { withKibana } from '../../../../kibana_react/public'; import type { QueryBarTopRowProps } from './query_bar_top_row'; import type { QueryStringInputProps } from './query_string_input'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazyQueryBarTopRow = React.lazy(() => import('./query_bar_top_row')); export const QueryBarTopRow = (props: QueryBarTopRowProps) => ( diff --git a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx index e01fbedbe38de..7a44b924870f0 100644 --- a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx @@ -36,12 +36,14 @@ import { EuiSuperUpdateButton, OnRefreshProps } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { Toast } from 'src/core/public'; import { IDataPluginServices, IIndexPattern, TimeRange, TimeHistoryContract, Query } from '../..'; -import { useKibana, toMountPoint } from '../../../../kibana_react/public'; -import { QueryStringInput } from './'; +import { useKibana, toMountPoint, withKibana } from '../../../../kibana_react/public'; +import QueryStringInputUI from './query_string_input'; import { doesKueryExpressionHaveLuceneSyntaxError, UI_SETTINGS } from '../../../common'; import { PersistedLog, getQueryLog } from '../../query'; import { NoDataPopover } from './no_data_popover'; +const QueryStringInput = withKibana(QueryStringInputUI); + // @internal export interface QueryBarTopRowProps { query?: Query; diff --git a/src/plugins/data/public/ui/search_bar/index.tsx b/src/plugins/data/public/ui/search_bar/index.tsx index d81ed7333655d..310542f4b12bd 100644 --- a/src/plugins/data/public/ui/search_bar/index.tsx +++ b/src/plugins/data/public/ui/search_bar/index.tsx @@ -19,15 +19,10 @@ import React from 'react'; import { injectI18n } from '@kbn/i18n/react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import { withKibana } from '../../../../kibana_react/public'; import type { SearchBarProps } from './search_bar'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazySearchBar = React.lazy(() => import('./search_bar')); const WrappedSearchBar = (props: SearchBarProps) => ( diff --git a/src/plugins/data/public/ui/search_bar/search_bar.test.tsx b/src/plugins/data/public/ui/search_bar/search_bar.test.tsx index a89b9bb7f91ef..74992f35fffc8 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.test.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.test.tsx @@ -18,10 +18,7 @@ */ import React from 'react'; -import { waitFor } from '@testing-library/dom'; -import { render } from '@testing-library/react'; - -import { SearchBar } from './'; +import SearchBar from './search_bar'; import { KibanaContextProvider } from 'src/plugins/kibana_react/public'; import { I18nProvider } from '@kbn/i18n/react'; @@ -29,6 +26,7 @@ import { I18nProvider } from '@kbn/i18n/react'; import { coreMock } from '../../../../../core/public/mocks'; const startMock = coreMock.createStart(); +import { mount } from 'enzyme'; import { IIndexPattern } from '../..'; const mockTimeHistory = { @@ -37,16 +35,14 @@ const mockTimeHistory = { }, }; -jest.mock('..', () => { +jest.mock('../filter_bar/filter_bar', () => { return { FilterBar: () =>
, }; }); -jest.mock('../query_string_input', () => { - return { - QueryBarTopRow: () =>
, - }; +jest.mock('../query_string_input/query_bar_top_row', () => { + return () =>
; }); const noop = jest.fn(); @@ -117,48 +113,42 @@ function wrapSearchBarInContext(testProps: any) { ); } -// FLAKY: https://github.com/elastic/kibana/issues/79910 -describe.skip('SearchBar', () => { - const SEARCH_BAR_TEST_ID = 'globalQueryBar'; +describe('SearchBar', () => { const SEARCH_BAR_ROOT = '.globalQueryBar'; - const FILTER_BAR = '.globalFilterBar'; + const FILTER_BAR = '.filterBar'; const QUERY_BAR = '.queryBar'; beforeEach(() => { jest.clearAllMocks(); }); - it('Should render query bar when no options provided (in reality - timepicker)', async () => { - const { container, getByTestId } = render( + it('Should render query bar when no options provided (in reality - timepicker)', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(0); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(1); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(0); + expect(component.find(QUERY_BAR).length).toBe(1); }); - it('Should render empty when timepicker is off and no options provided', async () => { - const { container, getByTestId } = render( + it('Should render empty when timepicker is off and no options provided', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], showDatePicker: false, }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(0); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(0); + expect(component.find(QUERY_BAR).length).toBe(0); }); - it('Should render filter bar, when required fields are provided', async () => { - const { container, getByTestId } = render( + it('Should render filter bar, when required fields are provided', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], showDatePicker: false, @@ -167,15 +157,13 @@ describe.skip('SearchBar', () => { }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(1); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(1); + expect(component.find(QUERY_BAR).length).toBe(0); }); - it('Should NOT render filter bar, if disabled', async () => { - const { container, getByTestId } = render( + it('Should NOT render filter bar, if disabled', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], showFilterBar: false, @@ -185,15 +173,13 @@ describe.skip('SearchBar', () => { }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(0); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(0); + expect(component.find(QUERY_BAR).length).toBe(0); }); - it('Should render query bar, when required fields are provided', async () => { - const { container, getByTestId } = render( + it('Should render query bar, when required fields are provided', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], screenTitle: 'test screen', @@ -202,15 +188,13 @@ describe.skip('SearchBar', () => { }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(0); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(1); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(0); + expect(component.find(QUERY_BAR).length).toBe(1); }); - it('Should NOT render query bar, if disabled', async () => { - const { container, getByTestId } = render( + it('Should NOT render query bar, if disabled', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], screenTitle: 'test screen', @@ -220,15 +204,13 @@ describe.skip('SearchBar', () => { }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(0); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(0); + expect(component.find(QUERY_BAR).length).toBe(0); }); - it('Should render query bar and filter bar', async () => { - const { container, getByTestId } = render( + it('Should render query bar and filter bar', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], screenTitle: 'test screen', @@ -239,10 +221,8 @@ describe.skip('SearchBar', () => { }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(1); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(1); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(1); + expect(component.find(QUERY_BAR).length).toBe(1); }); }); diff --git a/src/plugins/data/public/ui/search_bar/search_bar.tsx b/src/plugins/data/public/ui/search_bar/search_bar.tsx index 95651ac9ed8b3..daa6fa0dd80ab 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -26,7 +26,7 @@ import { get, isEqual } from 'lodash'; import { withKibana, KibanaReactContextValue } from '../../../../kibana_react/public'; -import { QueryBarTopRow } from '../query_string_input'; +import QueryBarTopRow from '../query_string_input/query_bar_top_row'; import { SavedQueryAttributes, TimeHistoryContract, SavedQuery } from '../../query'; import { IDataPluginServices } from '../../types'; import { TimeRange, Query, Filter, IIndexPattern } from '../../../common'; diff --git a/src/plugins/data/public/ui/shard_failure_modal/index.tsx b/src/plugins/data/public/ui/shard_failure_modal/index.tsx index cea882deff365..2ac470573c422 100644 --- a/src/plugins/data/public/ui/shard_failure_modal/index.tsx +++ b/src/plugins/data/public/ui/shard_failure_modal/index.tsx @@ -18,14 +18,9 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import type { ShardFailureOpenModalButtonProps } from './shard_failure_open_modal_button'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazyShardFailureOpenModalButton = React.lazy( () => import('./shard_failure_open_modal_button') diff --git a/src/plugins/data/public/ui/typeahead/index.tsx b/src/plugins/data/public/ui/typeahead/index.tsx index aa3c2d71300df..58547cd2ccbec 100644 --- a/src/plugins/data/public/ui/typeahead/index.tsx +++ b/src/plugins/data/public/ui/typeahead/index.tsx @@ -18,14 +18,9 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import type { SuggestionsComponentProps } from './suggestions_component'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazySuggestionsComponent = React.lazy(() => import('./suggestions_component')); export const SuggestionsComponent = (props: SuggestionsComponentProps) => ( diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 0828460830f2c..65313adfc0e0f 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -211,7 +211,9 @@ export enum ES_FIELD_TYPES { // (undocumented) TOKEN_COUNT = "token_count", // (undocumented) - _TYPE = "_type" + _TYPE = "_type", + // (undocumented) + UNSIGNED_LONG = "unsigned_long" } // Warning: (ae-missing-release-tag) "ES_SEARCH_STRATEGY" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -513,6 +515,8 @@ export class IndexPattern implements IIndexPattern { constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); addScriptedField(name: string, script: string, fieldType?: string): Promise; // (undocumented) + deleteFieldFormat: (fieldName: string) => void; + // (undocumented) fieldFormatMap: Record; // Warning: (ae-forgotten-export) The symbol "IIndexPatternFieldList" needs to be exported by the entry point index.d.ts // @@ -560,6 +564,7 @@ export class IndexPattern implements IIndexPattern { // (undocumented) getFieldByName(name: string): IndexPatternField | undefined; getFormatterForField(field: IndexPatternField | IndexPatternField['spec'] | IFieldType): FieldFormat; + getFormatterForFieldNoDefault(fieldname: string): FieldFormat | undefined; // Warning: (ae-forgotten-export) The symbol "IndexPatternField" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -593,6 +598,10 @@ export class IndexPattern implements IIndexPattern { metaFields: string[]; removeScriptedField(fieldName: string): void; resetOriginalSavedObjectBody: () => void; + // Warning: (ae-forgotten-export) The symbol "SerializedFieldFormat" needs to be exported by the entry point index.d.ts + // + // (undocumented) + setFieldFormat: (fieldName: string, format: SerializedFieldFormat) => void; // Warning: (ae-forgotten-export) The symbol "SourceFilter" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -685,6 +694,7 @@ export class IndexPatternsService implements Plugin_3 - @@ -35,39 +35,17 @@ type="'predecessors'" > - - -
-
-
- -
-
- -
-
+ , -
-
-
-
- -
-
-
-

- Refine your query -

-

- The search bar at the top uses Elasticsearch’s support for Lucene - - Query String syntax - - . Here are some examples of how you can search for web server logs that have been parsed into a few fields. -

-
-
-
-
-
- - Find requests that contain the number 200, in any field - -
-
-
- - - 200 - - -
-
-
- - Find 200 in the status field - -
-
-
- - - status:200 - - -
-
-
- - Find all status codes between 400-499 - -
-
-
- - - status:[400 TO 499] - - -
-
-
- - Find status codes 400-499 with the extension php - -
-
-
- - - status:[400 TO 499] AND extension:PHP - - -
-
-
- - Find status codes 400-499 with the extension php or html - -
-
-
- - - status:[400 TO 499] AND (extension:php OR extension:html) - - -
-
-
-
-
, -] -`; - -exports[`DiscoverNoResults props timeFieldName renders time range feedback 1`] = ` -Array [ -
, -
-
-
-
- -
-
-
-

- Expand your time range -

-

- One or more of the indices you’re looking at contains a date field. Your query may not match anything in the current time range, or there may not be any data at all in the currently selected time range. You can try changing the time range to one which contains data. -

-
-
-
, -] -`; diff --git a/src/plugins/discover/public/application/angular/directives/_index.scss b/src/plugins/discover/public/application/angular/directives/_index.scss index d4b365547b40c..dfacdf45c9d7b 100644 --- a/src/plugins/discover/public/application/angular/directives/_index.scss +++ b/src/plugins/discover/public/application/angular/directives/_index.scss @@ -1,2 +1 @@ -@import 'no_results'; @import 'histogram'; diff --git a/src/plugins/discover/public/application/angular/directives/index.ts b/src/plugins/discover/public/application/angular/directives/index.ts new file mode 100644 index 0000000000000..2e120995cce07 --- /dev/null +++ b/src/plugins/discover/public/application/angular/directives/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { DiscoverUninitialized } from './uninitialized'; +export { DiscoverHistogram } from './histogram'; diff --git a/src/plugins/discover/public/application/angular/directives/no_results.js b/src/plugins/discover/public/application/angular/directives/no_results.js deleted file mode 100644 index d8a39d9178e93..0000000000000 --- a/src/plugins/discover/public/application/angular/directives/no_results.js +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { Component, Fragment } from 'react'; -import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; -import PropTypes from 'prop-types'; - -import { - EuiCallOut, - EuiCode, - EuiDescriptionList, - EuiFlexGroup, - EuiFlexItem, - EuiLink, - EuiSpacer, - EuiText, -} from '@elastic/eui'; -import { getServices } from '../../../kibana_services'; - -// eslint-disable-next-line react/prefer-stateless-function -export class DiscoverNoResults extends Component { - static propTypes = { - timeFieldName: PropTypes.string, - queryLanguage: PropTypes.string, - }; - - render() { - const { timeFieldName, queryLanguage } = this.props; - - let timeFieldMessage; - - if (timeFieldName) { - timeFieldMessage = ( - - - - -

- -

- -

- -

-
-
- ); - } - - let luceneQueryMessage; - - if (queryLanguage === 'lucene') { - const searchExamples = [ - { - description: 200, - title: ( - - - - - - ), - }, - { - description: status:200, - title: ( - - - - - - ), - }, - { - description: status:[400 TO 499], - title: ( - - - - - - ), - }, - { - description: status:[400 TO 499] AND extension:PHP, - title: ( - - - - - - ), - }, - { - description: status:[400 TO 499] AND (extension:php OR extension:html), - title: ( - - - - - - ), - }, - ]; - - luceneQueryMessage = ( - - - - -

- -

- -

- - - - ), - }} - /> -

-
- - - - - - -
- ); - } - - return ( - - - - - - - - } - color="warning" - iconType="help" - data-test-subj="discoverNoResults" - /> - {timeFieldMessage} - {luceneQueryMessage} - - - - - ); - } -} diff --git a/src/plugins/discover/public/application/angular/directives/no_results.test.js b/src/plugins/discover/public/application/angular/directives/no_results.test.js deleted file mode 100644 index 60c50048a39ef..0000000000000 --- a/src/plugins/discover/public/application/angular/directives/no_results.test.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { renderWithIntl } from 'test_utils/enzyme_helpers'; - -import { DiscoverNoResults } from './no_results'; - -jest.mock('../../../kibana_services', () => { - return { - getServices: () => ({ - docLinks: { - links: { - query: { - luceneQuerySyntax: 'documentation-link', - }, - }, - }, - }), - }; -}); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -describe('DiscoverNoResults', () => { - describe('props', () => { - describe('timeFieldName', () => { - test('renders time range feedback', () => { - const component = renderWithIntl(); - - expect(component).toMatchSnapshot(); - }); - }); - - describe('queryLanguage', () => { - test('supports lucene and renders doc link', () => { - const component = renderWithIntl( - 'documentation-link'} /> - ); - - expect(component).toMatchSnapshot(); - }); - }); - }); -}); diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 078a047324113..ebd086dd1e38a 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -87,6 +87,7 @@ const fetchStatuses = { UNINITIALIZED: 'uninitialized', LOADING: 'loading', COMPLETE: 'complete', + ERROR: 'error', }; const app = getAngularModule(); @@ -193,7 +194,7 @@ app.directive('discoverApp', function () { function discoverController($element, $route, $scope, $timeout, $window, Promise, uiCapabilities) { const { isDefault: isDefaultType } = indexPatternsUtils; const subscriptions = new Subscription(); - const $fetchObservable = new Subject(); + const refetch$ = new Subject(); let inspectorRequest; const savedSearch = $route.current.locals.savedObjects.savedSearch; $scope.searchSource = savedSearch.searchSource; @@ -267,7 +268,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise ); if (changes.length) { - $fetchObservable.next(); + refetch$.next(); } }); } @@ -332,6 +333,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise if (abortController) abortController.abort(); savedSearch.destroy(); subscriptions.unsubscribe(); + data.search.session.clear(); appStateUnsubscribe(); stopStateSync(); stopSyncingGlobalStateWithUrl(); @@ -619,6 +621,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise config: config, fixedScroll: createFixedScroll($scope, $timeout), setHeaderActionMenu: getHeaderActionMenuMounter(), + data, }; const shouldSearchOnPageLoad = () => { @@ -633,12 +636,18 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise const init = _.once(() => { $scope.updateDataSource().then(async () => { - const searchBarChanges = merge(data.query.state$, $fetchObservable).pipe(debounceTime(100)); + const fetch$ = merge( + refetch$, + filterManager.getFetches$(), + timefilter.getFetch$(), + timefilter.getAutoRefreshFetch$(), + data.query.queryString.getUpdates$() + ).pipe(debounceTime(100)); subscriptions.add( subscribeWithScope( $scope, - searchBarChanges, + fetch$, { next: $scope.fetch, }, @@ -678,7 +687,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise function pick(rows, oldRows, fetchStatus) { // initial state, pretend we're already loading if we're about to execute a search so // that the uninitilized message doesn't flash on screen - if (rows == null && oldRows == null && shouldSearchOnPageLoad()) { + if (!$scope.fetchError && rows == null && oldRows == null && shouldSearchOnPageLoad()) { return status.LOADING; } @@ -717,7 +726,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise init.complete = true; if (shouldSearchOnPageLoad()) { - $fetchObservable.next(); + refetch$.next(); } }); }); @@ -788,6 +797,8 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise if (abortController) abortController.abort(); abortController = new AbortController(); + const sessionId = data.search.session.start(); + $scope .updateDataSource() .then(setupVisualization) @@ -796,6 +807,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise logInspectorRequest(); return $scope.searchSource.fetch({ abortSignal: abortController.signal, + sessionId, }); }) .then(onResults) @@ -804,7 +816,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise if (error instanceof Error && error.name === 'AbortError') return; $scope.fetchStatus = fetchStatuses.NO_RESULTS; - $scope.rows = []; + $scope.fetchError = error; data.search.showError(error); }); @@ -812,7 +824,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise $scope.handleRefresh = function (_payload, isUpdate) { if (isUpdate === false) { - $fetchObservable.next(); + refetch$.next(); } }; diff --git a/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx b/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx index ad2b674af014c..f191fa2dc89e8 100644 --- a/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx @@ -68,6 +68,7 @@ export function convertDirectiveToRenderFn( let rejected = false; const cleanupFnPromise = injectAngularElement(domNode, directive.template, props, getInjector); + cleanupFnPromise.catch(() => { rejected = true; render(
error
, domNode); @@ -91,10 +92,10 @@ export interface DocTableLegacyProps { rows: Array>; indexPattern: IIndexPattern; minimumVisibleRows: number; - onAddColumn: (column: string) => void; - onSort: (sort: string[][]) => void; - onMoveColumn: (columns: string, newIdx: number) => void; - onRemoveColumn: (column: string) => void; + onAddColumn?: (column: string) => void; + onSort?: (sort: string[][]) => void; + onMoveColumn?: (columns: string, newIdx: number) => void; + onRemoveColumn?: (column: string) => void; sort?: string[][]; } diff --git a/src/plugins/discover/public/application/components/context_app/__snapshots__/context_app_legacy.test.tsx.snap b/src/plugins/discover/public/application/components/context_app/__snapshots__/context_app_legacy.test.tsx.snap new file mode 100644 index 0000000000000..58305ee23cb21 --- /dev/null +++ b/src/plugins/discover/public/application/components/context_app/__snapshots__/context_app_legacy.test.tsx.snap @@ -0,0 +1,741 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ContextAppLegacy test renders correctly 1`] = ` + + + + + +
+
+ +
+ +
+
+ + + + + +`; + +exports[`ContextAppLegacy test renders loading indicator 1`] = ` + + + + + +
+ +
+ +
+ + Loading... + +
+
+
+
+
+
+
+
+
+
+`; diff --git a/src/plugins/discover/public/application/angular/context/query/constants.js b/src/plugins/discover/public/application/components/context_app/constants.ts similarity index 100% rename from src/plugins/discover/public/application/angular/context/query/constants.js rename to src/plugins/discover/public/application/components/context_app/constants.ts diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx b/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx new file mode 100644 index 0000000000000..16d8cd78004f9 --- /dev/null +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy.test.tsx @@ -0,0 +1,75 @@ +/* + * 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 { ContextAppLegacy } from './context_app_legacy'; +import { IIndexPattern } from '../../../../../data/common/index_patterns'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { DocTableLegacy } from '../../angular/doc_table/create_doc_table_react'; +import { findTestSubject } from '@elastic/eui/lib/test'; + +describe('ContextAppLegacy test', () => { + const hit = { + _id: '123', + _index: 'test_index', + _score: null, + _version: 1, + _source: { + category: ["Men's Clothing"], + currency: 'EUR', + customer_first_name: 'Walker', + customer_full_name: 'Walker Texas Ranger', + customer_gender: 'MALE', + customer_last_name: 'Ranger', + }, + fields: [{ order_date: ['2020-10-19T13:35:02.000Z'] }], + sort: [1603114502000, 2092], + }; + const indexPattern = { + id: 'test_index_pattern', + } as IIndexPattern; + const defaultProps = { + columns: ['_source'], + filter: () => {}, + hits: [hit], + infiniteScroll: true, + sorting: ['order_date', 'desc'], + minimumVisibleRows: 5, + indexPattern, + status: 'loaded', + }; + + it('renders correctly', () => { + const component = mountWithIntl(); + expect(component).toMatchSnapshot(); + expect(component.find(DocTableLegacy).length).toBe(1); + const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator'); + expect(loadingIndicator.length).toBe(0); + }); + + it('renders loading indicator', () => { + const props = { ...defaultProps }; + props.status = 'loading'; + const component = mountWithIntl(); + expect(component).toMatchSnapshot(); + expect(component.find('DocTableLegacy').length).toBe(0); + const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator'); + expect(loadingIndicator.length).toBe(1); + }); +}); diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx new file mode 100644 index 0000000000000..ee8b2f590f71c --- /dev/null +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx @@ -0,0 +1,76 @@ +/* + * 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 { FormattedMessage } from '@kbn/i18n/react'; +import { EuiPanel, EuiText } from '@elastic/eui'; +import { I18nProvider } from '@kbn/i18n/react'; +import { + DocTableLegacy, + DocTableLegacyProps, +} from '../../angular/doc_table/create_doc_table_react'; +import { IIndexPattern, IndexPatternField } from '../../../../../data/common/index_patterns'; +import { LOADING_STATUS } from './constants'; + +export interface ContextAppProps { + columns: string[]; + hits: Array>; + indexPattern: IIndexPattern; + filter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; + minimumVisibleRows: number; + sorting: string[]; + status: string; +} + +export function ContextAppLegacy(renderProps: ContextAppProps) { + const { hits, filter, sorting, status } = renderProps; + const props = ({ ...renderProps } as unknown) as DocTableLegacyProps; + props.rows = hits; + props.onFilter = filter; + props.sort = sorting.map((el) => [el]); + const isLoaded = status === LOADING_STATUS.LOADED; + const loadingFeedback = () => { + if (status === LOADING_STATUS.UNINITIALIZED || status === LOADING_STATUS.LOADING) { + return ( + + + + + + ); + } + return null; + }; + return ( + + + {loadingFeedback()} + {isLoaded ? ( + +
+ +
+
+ ) : null} +
+
+ ); +} diff --git a/src/plugins/discover/public/application/angular/directives/index.js b/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts similarity index 58% rename from src/plugins/discover/public/application/angular/directives/index.js rename to src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts index 5d8969a78f018..af94c5537da28 100644 --- a/src/plugins/discover/public/application/angular/directives/index.js +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy_directive.ts @@ -16,16 +16,17 @@ * specific language governing permissions and limitations * under the License. */ +import { ContextAppLegacy } from './context_app_legacy'; -import { DiscoverNoResults } from './no_results'; -import { DiscoverUninitialized } from './uninitialized'; -import { DiscoverHistogram } from './histogram'; -import { getAngularModule } from '../../../kibana_services'; - -const app = getAngularModule(); - -app.directive('discoverNoResults', (reactDirective) => reactDirective(DiscoverNoResults)); - -app.directive('discoverUninitialized', (reactDirective) => reactDirective(DiscoverUninitialized)); - -app.directive('discoverHistogram', (reactDirective) => reactDirective(DiscoverHistogram)); +export function createContextAppLegacy(reactDirective: any) { + return reactDirective(ContextAppLegacy, [ + ['filter', { watchDepth: 'reference' }], + ['hits', { watchDepth: 'reference' }], + ['indexPattern', { watchDepth: 'reference' }], + ['sorting', { watchDepth: 'reference' }], + ['columns', { watchDepth: 'collection' }], + ['infiniteScroll', { watchDepth: 'reference' }], + ['minimumVisibleRows', { watchDepth: 'reference' }], + ['status', { watchDepth: 'reference' }], + ]); +} diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx index 139b2ca69d9e4..3ca421f809640 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -26,10 +26,8 @@ import { HitsCounter } from './hits_counter'; import { TimechartHeader } from './timechart_header'; import { DiscoverSidebar } from './sidebar'; import { getServices, IndexPattern } from '../../kibana_services'; -// @ts-ignore -import { DiscoverNoResults } from '../angular/directives/no_results'; -import { DiscoverUninitialized } from '../angular/directives/uninitialized'; -import { DiscoverHistogram } from '../angular/directives/histogram'; +import { DiscoverUninitialized, DiscoverHistogram } from '../angular/directives'; +import { DiscoverNoResults } from './no_results'; import { LoadingSpinner } from './loading_spinner/loading_spinner'; import { DocTableLegacy } from '../angular/doc_table/create_doc_table_react'; import { SkipBottomButton } from './skip_bottom_button'; @@ -40,6 +38,7 @@ import { TimeRange, Query, IndexPatternAttributes, + DataPublicPluginStart, } from '../../../../data/public'; import { Chart } from '../angular/helpers/point_series'; import { AppState } from '../angular/discover_state'; @@ -53,6 +52,7 @@ export interface DiscoverLegacyProps { addColumn: (column: string) => void; fetch: () => void; fetchCounter: number; + fetchError: Error; fieldCounts: Record; histogramData: Chart; hits: number; @@ -73,6 +73,7 @@ export interface DiscoverLegacyProps { sampleSize: number; fixedScroll: (el: HTMLElement) => void; setHeaderActionMenu: (menuMount: MountPoint | undefined) => void; + data: DataPublicPluginStart; }; resetQuery: () => void; resultState: string; @@ -94,6 +95,7 @@ export function DiscoverLegacy({ fetch, fetchCounter, fieldCounts, + fetchError, histogramData, hits, indexPattern, @@ -208,6 +210,8 @@ export function DiscoverLegacy({ )} {resultState === 'uninitialized' && } diff --git a/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap b/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap index afb541253d994..d00a956b7c73d 100644 --- a/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap +++ b/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap @@ -10,7 +10,7 @@ exports[`FieldName renders a geo field, useShortDots is set to true 1`] = ` -
-
-
{ + return { + getServices: () => ({ + docLinks: { + links: { + query: { + luceneQuerySyntax: 'documentation-link', + }, + }, + }, + }), + }; +}); + +beforeEach(() => { + jest.clearAllMocks(); +}); + +function mountAndFindSubjects(props: DiscoverNoResultsProps) { + const component = mountWithIntl(); + return { + mainMsg: findTestSubject(component, 'discoverNoResults').length > 0, + timeFieldMsg: findTestSubject(component, 'discoverNoResultsTimefilter').length > 0, + luceneMsg: findTestSubject(component, 'discoverNoResultsLucene').length > 0, + errorMsg: findTestSubject(component, 'discoverNoResultsError').length > 0, + }; +} + +describe('DiscoverNoResults', () => { + describe('props', () => { + describe('no props', () => { + test('renders default feedback', () => { + const result = mountAndFindSubjects({}); + expect(result).toMatchInlineSnapshot(` + Object { + "errorMsg": false, + "luceneMsg": false, + "mainMsg": true, + "timeFieldMsg": false, + } + `); + }); + }); + describe('timeFieldName', () => { + test('renders time range feedback', () => { + const result = mountAndFindSubjects({ + timeFieldName: 'awesome_time_field', + }); + expect(result).toMatchInlineSnapshot(` + Object { + "errorMsg": false, + "luceneMsg": false, + "mainMsg": true, + "timeFieldMsg": true, + } + `); + }); + }); + + describe('queryLanguage', () => { + test('supports lucene and renders doc link', () => { + const result = mountAndFindSubjects({ queryLanguage: 'lucene' }); + expect(result).toMatchInlineSnapshot(` + Object { + "errorMsg": false, + "luceneMsg": true, + "mainMsg": true, + "timeFieldMsg": false, + } + `); + }); + }); + + describe('error message', () => { + test('renders error message', () => { + const error = new Error('Fatal error'); + const result = mountAndFindSubjects({ + timeFieldName: 'awesome_time_field', + error, + queryLanguage: 'lucene', + }); + expect(result).toMatchInlineSnapshot(` + Object { + "errorMsg": true, + "luceneMsg": false, + "mainMsg": false, + "timeFieldMsg": false, + } + `); + }); + }); + }); +}); diff --git a/src/plugins/discover/public/application/components/no_results/no_results.tsx b/src/plugins/discover/public/application/components/no_results/no_results.tsx new file mode 100644 index 0000000000000..fcc2912d16dd5 --- /dev/null +++ b/src/plugins/discover/public/application/components/no_results/no_results.tsx @@ -0,0 +1,92 @@ +/* + * 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, { Fragment } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiButton, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { getServices } from '../../../kibana_services'; +import { DataPublicPluginStart } from '../../../../../data/public'; +import { getLuceneQueryMessage, getTimeFieldMessage } from './no_results_helper'; +import './_no_results.scss'; + +export interface DiscoverNoResultsProps { + timeFieldName?: string; + queryLanguage?: string; + error?: Error; + data?: DataPublicPluginStart; +} + +export function DiscoverNoResults({ + timeFieldName, + queryLanguage, + error, + data, +}: DiscoverNoResultsProps) { + const callOut = !error ? ( + + + } + color="warning" + iconType="help" + data-test-subj="discoverNoResults" + /> + {timeFieldName ? getTimeFieldMessage() : null} + {queryLanguage === 'lucene' + ? getLuceneQueryMessage(getServices().docLinks.links.query.luceneQuerySyntax) + : null} + + ) : ( + + + } + color="danger" + iconType="alert" + data-test-subj="discoverNoResultsError" + > + (data ? data.search.showError(error) : void 0)} + > + + + + + ); + + return ( + + + {callOut} + + ); +} diff --git a/src/plugins/discover/public/application/components/no_results/no_results_helper.tsx b/src/plugins/discover/public/application/components/no_results/no_results_helper.tsx new file mode 100644 index 0000000000000..fbc655e01bdf5 --- /dev/null +++ b/src/plugins/discover/public/application/components/no_results/no_results_helper.tsx @@ -0,0 +1,149 @@ +/* + * 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, { Fragment } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiCode, EuiDescriptionList, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; + +export function getTimeFieldMessage() { + return ( + + + +

+ +

+

+ +

+
+
+ ); +} + +export function getLuceneQueryMessage(link: string) { + const searchExamples = [ + { + description: 200, + title: ( + + + + + + ), + }, + { + description: status:200, + title: ( + + + + + + ), + }, + { + description: status:[400 TO 499], + title: ( + + + + + + ), + }, + { + description: status:[400 TO 499] AND extension:PHP, + title: ( + + + + + + ), + }, + { + description: status:[400 TO 499] AND (extension:php OR extension:html), + title: ( + + + + + + ), + }, + ]; + return ( + + + +

+ +

+

+ + + + ), + }} + /> +

+
+ + + +
+ ); +} diff --git a/src/plugins/discover/public/get_inner_angular.ts b/src/plugins/discover/public/get_inner_angular.ts index 1ca0bb20e8723..55a75240909bf 100644 --- a/src/plugins/discover/public/get_inner_angular.ts +++ b/src/plugins/discover/public/get_inner_angular.ts @@ -36,6 +36,7 @@ import { createToolBarPagerButtonsDirective, createToolBarPagerTextDirective, } from './application/angular/doc_table/components/pager'; +import { createContextAppLegacy } from './application/components/context_app/context_app_legacy_directive'; import { createTableRowDirective } from './application/angular/doc_table/components/table_row'; import { createPagerFactory } from './application/angular/doc_table/lib/pager/pager_factory'; import { createInfiniteScrollDirective } from './application/angular/doc_table/infinite_scroll'; @@ -55,7 +56,6 @@ import { createContextErrorMessageDirective } from './application/components/con import { DiscoverStartPlugins } from './plugin'; import { getScopedHistory } from './kibana_services'; import { createDiscoverLegacyDirective } from './application/components/create_discover_legacy_directive'; - /** * returns the main inner angular module, it contains all the parts of Angular Discover * needs to render, so in the end the current 'kibana' angular module is no longer necessary @@ -190,5 +190,6 @@ function createDocTableModule() { .directive('kbnTableRow', createTableRowDirective) .directive('toolBarPagerButtons', createToolBarPagerButtonsDirective) .directive('kbnInfiniteScroll', createInfiniteScrollDirective) - .directive('docViewer', createDocViewerDirective); + .directive('docViewer', createDocViewerDirective) + .directive('contextAppLegacy', createContextAppLegacy); } diff --git a/src/plugins/embeddable/public/lib/triggers/triggers.ts b/src/plugins/embeddable/public/lib/triggers/triggers.ts index ccba5cf771088..54c7a2ecc129d 100644 --- a/src/plugins/embeddable/public/lib/triggers/triggers.ts +++ b/src/plugins/embeddable/public/lib/triggers/triggers.ts @@ -17,7 +17,7 @@ * under the License. */ -import { KibanaDatatable } from '../../../../expressions'; +import { Datatable } from '../../../../expressions'; import { Trigger } from '../../../../ui_actions/public'; import { IEmbeddable } from '..'; @@ -29,7 +29,7 @@ export interface ValueClickContext { embeddable?: T; data: { data: Array<{ - table: Pick; + table: Pick; column: number; row: number; value: any; @@ -42,7 +42,7 @@ export interface ValueClickContext { export interface RangeSelectContext { embeddable?: T; data: { - table: KibanaDatatable; + table: Datatable; column: number; range: number[]; timeFieldName?: string; diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md index 6280d3a2e4a50..a6d90f2766c18 100644 --- a/src/plugins/embeddable/public/public.api.md +++ b/src/plugins/embeddable/public/public.api.md @@ -326,7 +326,7 @@ export abstract class Embeddable { +export class EmbeddableChildPanel extends React.Component { constructor(props: EmbeddableChildPanelProps); // (undocumented) [panel: string]: any; @@ -477,7 +477,7 @@ export interface EmbeddablePackageState { // Warning: (ae-missing-release-tag) "EmbeddablePanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export class EmbeddablePanel extends React.Component { +export class EmbeddablePanel extends React.Component { constructor(props: Props); // (undocumented) closeMyContextMenuPanel: () => void; @@ -810,7 +810,7 @@ export interface PropertySpec { export interface RangeSelectContext { // (undocumented) data: { - table: KibanaDatatable; + table: Datatable; column: number; range: number[]; timeFieldName?: string; @@ -841,7 +841,7 @@ export interface ValueClickContext { // (undocumented) data: { data: Array<{ - table: Pick; + table: Pick; column: number; row: number; value: any; @@ -881,7 +881,7 @@ export const withEmbeddableSubscription: { + // error.name is slightly better in terms of performance, since all errors now have name property + if (error.name === 'ResponseError') { + const { statusCode, body } = error as ResponseError; + return response.customError({ + statusCode, + body: { message: body.error?.reason }, + }); + } + // Case: default + return response.internalError({ body: error }); +}; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/index.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/index.ts index 0d025442f4a92..484dc17868ab0 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/index.ts +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/index.ts @@ -18,3 +18,4 @@ */ export { isEsError } from './is_es_error'; +export { handleEsError } from './handle_es_error'; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/is_es_error.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/is_es_error.ts index 1e212307ca1cc..80a53aac328a4 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/errors/is_es_error.ts +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/errors/is_es_error.ts @@ -25,6 +25,10 @@ interface RequestError extends Error { statusCode?: number; } +/* + * @deprecated + * Only works with legacy elasticsearch js client errors and will be removed after 7.x last + */ export function isEsError(err: RequestError) { const isInstanceOfEsError = err instanceof esErrorsParent; const hasStatusCode = Boolean(err.statusCode); diff --git a/src/plugins/es_ui_shared/server/errors/index.ts b/src/plugins/es_ui_shared/server/errors/index.ts index c18374cd9ec31..532e02774ff50 100644 --- a/src/plugins/es_ui_shared/server/errors/index.ts +++ b/src/plugins/es_ui_shared/server/errors/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { isEsError } from '../../__packages_do_not_import__/errors'; +export { isEsError, handleEsError } from '../../__packages_do_not_import__/errors'; diff --git a/src/plugins/es_ui_shared/server/index.ts b/src/plugins/es_ui_shared/server/index.ts index 0118bbda53262..b2c9c85d956ba 100644 --- a/src/plugins/es_ui_shared/server/index.ts +++ b/src/plugins/es_ui_shared/server/index.ts @@ -17,7 +17,7 @@ * under the License. */ -export { isEsError } from './errors'; +export { isEsError, handleEsError } from './errors'; /** dummy plugin*/ export function plugin() { diff --git a/src/plugins/es_ui_shared/static/forms/components/fields/checkbox_field.tsx b/src/plugins/es_ui_shared/static/forms/components/fields/checkbox_field.tsx index c8ba9f5ac4102..b80d6caf54f4f 100644 --- a/src/plugins/es_ui_shared/static/forms/components/fields/checkbox_field.tsx +++ b/src/plugins/es_ui_shared/static/forms/components/fields/checkbox_field.tsx @@ -39,8 +39,8 @@ export const CheckBoxField = ({ field, euiFieldProps = {}, ...rest }: Props) => error={errorMessage} isInvalid={isInvalid} fullWidth - data-test-subj={rest['data-test-subj']} describedByIds={rest.idAria ? [rest.idAria] : undefined} + {...rest} > error={errorMessage} isInvalid={isInvalid} fullWidth - data-test-subj={rest['data-test-subj']} describedByIds={rest.idAria ? [rest.idAria] : undefined} + {...rest} > { error={errorMessage} isInvalid={isInvalid} fullWidth - data-test-subj={rest['data-test-subj']} describedByIds={rest.idAria ? [rest.idAria] : undefined} + {...rest} > { error={errorMessage} isInvalid={isInvalid} fullWidth - data-test-subj={rest['data-test-subj']} describedByIds={rest.idAria ? [rest.idAria] : undefined} + {...rest} > { error={errorMessage} isInvalid={isInvalid} fullWidth - data-test-subj={rest['data-test-subj']} describedByIds={rest.idAria ? [rest.idAria] : undefined} + {...rest} > error={errorMessage} isInvalid={isInvalid} fullWidth - data-test-subj={rest['data-test-subj']} describedByIds={rest.idAria ? [rest.idAria] : undefined} + {...rest} > { error={errorMessage} isInvalid={isInvalid} fullWidth - data-test-subj={rest['data-test-subj']} describedByIds={rest.idAria ? [rest.idAria] : undefined} + {...rest} > { error={errorMessage} isInvalid={isInvalid} fullWidth - data-test-subj={rest['data-test-subj']} describedByIds={rest.idAria ? [rest.idAria] : undefined} + {...rest} > ', () => { find('btn').simulate('click').update(); }); - expect(onFormData.mock.calls.length).toBe(1); + expect(onFormData.mock.calls.length).toBe(2); const [formDataUpdated] = onFormData.mock.calls[onFormData.mock.calls.length - 1] as Parameters< OnUpdateHandler diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts index ac141baf8fc71..de4edc1edf873 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts @@ -22,13 +22,16 @@ import React from 'react'; import { FormData } from '../types'; import { useFormData } from '../hooks'; -interface Props { - children: (formData: FormData) => JSX.Element | null; +interface Props { + children: (formData: I) => JSX.Element | null; pathsToWatch?: string | string[]; } -export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) => { - const { 0: formData, 2: isReady } = useFormData({ watch: pathsToWatch }); +const FormDataProviderComp = function ({ + children, + pathsToWatch, +}: Props) { + const { 0: formData, 2: isReady } = useFormData({ watch: pathsToWatch }); if (!isReady) { // No field has mounted yet, don't render anything @@ -36,4 +39,6 @@ export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) = } return children(formData); -}); +}; + +export const FormDataProvider = React.memo(FormDataProviderComp) as typeof FormDataProviderComp; diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts index 3c4f9799bb1bc..812a18680d6b8 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts @@ -107,7 +107,7 @@ export const UseArray = ({ getNewItemAtIndex, ]); - // Create a new hook field with the "hasValue" set to false so we don't use its value to build the final form data. + // Create a new hook field with the "isIncludedInOutput" set to false so we don't use its value to build the final form data. // Apart from that the field behaves like a normal field and is hooked into the form validation lifecycle. const fieldConfigBase: FieldConfig & InternalFieldConfig = { defaultValue: fieldDefaultValue, diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx index dbf53a9f0a359..1a7f8832e4a4e 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx @@ -58,7 +58,7 @@ describe('', () => { OnUpdateHandler >; - expect(data.raw).toEqual({ + expect(data.internal).toEqual({ name: 'John', lastName: 'Snow', }); @@ -214,8 +214,8 @@ describe('', () => { expect(serializer).not.toBeCalled(); expect(formatter).not.toBeCalled(); - let formData = formHook.getFormData({ unflatten: false }); - expect(formData.name).toEqual('John-deserialized'); + const internalFormData = formHook.__getFormData$().value; + expect(internalFormData.name).toEqual('John-deserialized'); await act(async () => { form.setInputValue('myField', 'Mike'); @@ -224,9 +224,9 @@ describe('', () => { expect(formatter).toBeCalled(); // Formatters are executed on each value change expect(serializer).not.toBeCalled(); // Serializer are executed *only** when outputting the form data - formData = formHook.getFormData(); + const outputtedFormData = formHook.getFormData(); expect(serializer).toBeCalled(); - expect(formData.name).toEqual('MIKE-serialized'); + expect(outputtedFormData.name).toEqual('MIKE-serialized'); // Make sure that when we reset the form values, we don't serialize the fields serializer.mockReset(); diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/form_data_context.tsx b/src/plugins/es_ui_shared/static/forms/hook_form_lib/form_data_context.tsx index 0670220ccd0c9..6aef6d2b0d46a 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/form_data_context.tsx +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/form_data_context.tsx @@ -22,9 +22,9 @@ import React, { createContext, useContext, useMemo } from 'react'; import { FormData, FormHook } from './types'; import { Subject } from './lib'; -export interface Context { - getFormData$: () => Subject; - getFormData: FormHook['getFormData']; +export interface Context { + getFormData$: () => Subject; + getFormData: FormHook['getFormData']; } const FormDataContext = createContext | undefined>(undefined); @@ -45,6 +45,6 @@ export const FormDataContextProvider = ({ children, getFormData$, getFormData }: return {children}; }; -export function useFormDataContext() { - return useContext | undefined>(FormDataContext); +export function useFormDataContext() { + return useContext | undefined>(FormDataContext); } diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts index 7b21b6638aeac..f4f13a698ee30 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts @@ -63,6 +63,7 @@ export const useField = ( __removeField, __updateFormDataAt, __validateFields, + __getFormData$, } = form; const deserializeValue = useCallback( @@ -76,7 +77,7 @@ export const useField = ( ); const [value, setStateValue] = useState(deserializeValue); - const [errors, setErrors] = useState([]); + const [errors, setStateErrors] = useState([]); const [isPristine, setPristine] = useState(true); const [isValidating, setValidating] = useState(false); const [isChangingValue, setIsChangingValue] = useState(false); @@ -86,18 +87,12 @@ export const useField = ( const validateCounter = useRef(0); const changeCounter = useRef(0); const hasBeenReset = useRef(false); - const inflightValidation = useRef | null>(null); + const inflightValidation = useRef<(Promise & { cancel?(): void }) | null>(null); const debounceTimeout = useRef(null); + // ---------------------------------- // -- HELPERS // ---------------------------------- - const serializeValue: FieldHook['__serializeValue'] = useCallback( - (internalValue: I = value) => { - return serializer ? serializer(internalValue) : ((internalValue as unknown) as T); - }, - [serializer, value] - ); - /** * Filter an array of errors with specific validation type on them * @@ -117,6 +112,11 @@ export const useField = ( ); }; + /** + * If the field has some "formatters" defined in its config, run them in series and return + * the transformed value. This handler is called whenever the field value changes, right before + * updating the "value" state. + */ const formatInputValue = useCallback( (inputValue: unknown): T => { const isEmptyString = typeof inputValue === 'string' && inputValue.trim() === ''; @@ -125,11 +125,11 @@ export const useField = ( return inputValue as T; } - const formData = getFormData({ unflatten: false }); + const formData = __getFormData$().value; return formatters.reduce((output, formatter) => formatter(output, formData), inputValue) as T; }, - [formatters, getFormData] + [formatters, __getFormData$] ); const onValueChange = useCallback(async () => { @@ -147,7 +147,7 @@ export const useField = ( // Update the form data observable __updateFormDataAt(path, value); - // Validate field(s) (that will update form.isValid state) + // Validate field(s) (this will update the form.isValid state) await __validateFields(fieldsToValidateOnChange ?? [path]); if (isMounted.current === false) { @@ -162,15 +162,18 @@ export const useField = ( */ if (changeIteration === changeCounter.current) { if (valueChangeDebounceTime > 0) { - const delta = Date.now() - startTime; - if (delta < valueChangeDebounceTime) { + const timeElapsed = Date.now() - startTime; + + if (timeElapsed < valueChangeDebounceTime) { + const timeLeftToWait = valueChangeDebounceTime - timeElapsed; debounceTimeout.current = setTimeout(() => { debounceTimeout.current = null; setIsChangingValue(false); - }, valueChangeDebounceTime - delta); + }, timeLeftToWait); return; } } + setIsChangingValue(false); } }, [ @@ -183,41 +186,34 @@ export const useField = ( __validateFields, ]); + // Cancel any inflight validation (e.g an HTTP Request) const cancelInflightValidation = useCallback(() => { - // Cancel any inflight validation (like an HTTP Request) - if ( - inflightValidation.current && - typeof (inflightValidation.current as any).cancel === 'function' - ) { - (inflightValidation.current as any).cancel(); + if (inflightValidation.current && typeof inflightValidation.current.cancel === 'function') { + inflightValidation.current.cancel(); inflightValidation.current = null; } }, []); - const clearErrors: FieldHook['clearErrors'] = useCallback( - (validationType = VALIDATION_TYPES.FIELD) => { - setErrors((previousErrors) => filterErrors(previousErrors, validationType)); - }, - [] - ); - const runValidations = useCallback( - ({ - formData, - value: valueToValidate, - validationTypeToValidate, - }: { - formData: any; - value: I; - validationTypeToValidate?: string; - }): ValidationError[] | Promise => { + ( + { + formData, + value: valueToValidate, + validationTypeToValidate, + }: { + formData: any; + value: I; + validationTypeToValidate?: string; + }, + clearFieldErrors: FieldHook['clearErrors'] + ): ValidationError[] | Promise => { if (!validations) { return []; } // By default, for fields that have an asynchronous validation // we will clear the errors as soon as the field value changes. - clearErrors([VALIDATION_TYPES.FIELD, VALIDATION_TYPES.ASYNC]); + clearFieldErrors([VALIDATION_TYPES.FIELD, VALIDATION_TYPES.ASYNC]); cancelInflightValidation(); @@ -329,21 +325,33 @@ export const useField = ( // We first try to run the validations synchronously return runSync(); }, - [clearErrors, cancelInflightValidation, validations, getFormData, getFields, path] + [cancelInflightValidation, validations, getFormData, getFields, path] ); - // -- API // ---------------------------------- + // -- Internal API + // ---------------------------------- + const serializeValue: FieldHook['__serializeValue'] = useCallback( + (internalValue: I = value) => { + return serializer ? serializer(internalValue) : ((internalValue as unknown) as T); + }, + [serializer, value] + ); + + // ---------------------------------- + // -- Public API + // ---------------------------------- + const clearErrors: FieldHook['clearErrors'] = useCallback( + (validationType = VALIDATION_TYPES.FIELD) => { + setStateErrors((previousErrors) => filterErrors(previousErrors, validationType)); + }, + [] + ); - /** - * Validate a form field, running all its validations. - * If a validationType is provided then only that validation will be executed, - * skipping the other type of validation that might exist. - */ const validate: FieldHook['validate'] = useCallback( (validationData = {}) => { const { - formData = getFormData({ unflatten: false }), + formData = __getFormData$().value, value: valueToValidate = value, validationType, } = validationData; @@ -362,7 +370,7 @@ export const useField = ( // This is the most recent invocation setValidating(false); // Update the errors array - setErrors((prev) => { + setStateErrors((prev) => { const filteredErrors = filterErrors(prev, validationType); return [...filteredErrors, ..._validationErrors]; }); @@ -374,25 +382,23 @@ export const useField = ( }; }; - const validationErrors = runValidations({ - formData, - value: valueToValidate, - validationTypeToValidate: validationType, - }); + const validationErrors = runValidations( + { + formData, + value: valueToValidate, + validationTypeToValidate: validationType, + }, + clearErrors + ); if (Reflect.has(validationErrors, 'then')) { return (validationErrors as Promise).then(onValidationResult); } return onValidationResult(validationErrors as ValidationError[]); }, - [getFormData, value, runValidations] + [__getFormData$, value, runValidations, clearErrors] ); - /** - * Handler to change the field value - * - * @param newValue The new value to assign to the field - */ const setValue: FieldHook['setValue'] = useCallback( (newValue) => { setStateValue((prev) => { @@ -408,8 +414,8 @@ export const useField = ( [formatInputValue] ); - const _setErrors: FieldHook['setErrors'] = useCallback((_errors) => { - setErrors( + const setErrors: FieldHook['setErrors'] = useCallback((_errors) => { + setStateErrors( _errors.map((error) => ({ validationType: VALIDATION_TYPES.FIELD, __isBlocking__: true, @@ -418,11 +424,6 @@ export const useField = ( ); }, []); - /** - * Form "onChange" event handler - * - * @param event Form input change event - */ const onChange: FieldHook['onChange'] = useCallback( (event) => { const newValue = {}.hasOwnProperty.call(event!.target, 'checked') @@ -485,7 +486,7 @@ export const useField = ( case 'value': return setValue(nextValue); case 'errors': - return setErrors(nextValue); + return setStateErrors(nextValue); case 'isChangingValue': return setIsChangingValue(nextValue); case 'isPristine': @@ -539,7 +540,7 @@ export const useField = ( onChange, getErrorsMessages, setValue, - setErrors: _setErrors, + setErrors, clearErrors, validate, reset, @@ -563,7 +564,7 @@ export const useField = ( onChange, getErrorsMessages, setValue, - _setErrors, + setErrors, clearErrors, validate, reset, @@ -585,7 +586,8 @@ export const useField = ( useEffect(() => { // If the field value has been reset, we don't want to call the "onValueChange()" - // as it will set the "isPristine" state to true or validate the field, which initially we don't want. + // as it will set the "isPristine" state to true or validate the field, which we don't want + // to occur right after resetting the field state. if (hasBeenReset.current) { hasBeenReset.current = false; return; diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.test.tsx b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.test.tsx index b28c09d07fa98..9626aaa9b2459 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.test.tsx +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.test.tsx @@ -211,7 +211,13 @@ describe('useForm() hook', () => { test('should allow subscribing to the form data changes and provide a handler to build the form data', async () => { const TestComp = ({ onData }: { onData: OnUpdateHandler }) => { - const { form } = useForm(); + const { form } = useForm({ + serializer: (value) => ({ + user: { + name: value.user.name.toUpperCase(), + }, + }), + }); const { subscribe } = form; useEffect(() => { @@ -253,8 +259,9 @@ describe('useForm() hook', () => { OnUpdateHandler >; - expect(data.raw).toEqual({ 'user.name': 'John' }); - expect(data.format()).toEqual({ user: { name: 'John' } }); + expect(data.internal).toEqual({ user: { name: 'John' } }); + // Transform name to uppercase as decalred in our serializer func + expect(data.format()).toEqual({ user: { name: 'JOHN' } }); // As we have touched all fields, the validity went from "undefined" to "true" expect(isValid).toBe(true); }); @@ -302,10 +309,12 @@ describe('useForm() hook', () => { OnUpdateHandler >; - expect(data.raw).toEqual({ + expect(data.internal).toEqual({ title: defaultValue.title, subTitle: 'hasBeenOverridden', - 'user.name': defaultValue.user.name, + user: { + name: defaultValue.user.name, + }, }); }); }); diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts index be4535fec3669..869d1fac54b1e 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts @@ -58,8 +58,6 @@ export function useForm( return initDefaultValue(defaultValue); }, [defaultValue, initDefaultValue]); - const defaultValueDeserialized = useRef(defaultValueMemoized); - const { valueChangeDebounceTime, stripEmptyFields: doStripEmptyFields } = options ?? {}; const formOptions = useMemo( () => ({ @@ -72,26 +70,36 @@ export function useForm( const [isSubmitted, setIsSubmitted] = useState(false); const [isSubmitting, setSubmitting] = useState(false); const [isValid, setIsValid] = useState(undefined); + const fieldsRefs = useRef({}); const formUpdateSubscribers = useRef([]); const isMounted = useRef(false); + const defaultValueDeserialized = useRef(defaultValueMemoized); // formData$ is an observable we can subscribe to in order to receive live // update of the raw form data. As an observable it does not trigger any React // render(). - // The component is the one in charge of reading this observable - // and updating its state to trigger the necessary view render. - const formData$ = useRef | null>(null); + // The "useFormData()" hook is the one in charge of reading this observable + // and updating its own state that will trigger the necessary re-renders in the UI. + const formData$ = useRef | null>(null); + // ---------------------------------- // -- HELPERS // ---------------------------------- - const getFormData$ = useCallback((): Subject => { + const getFormData$ = useCallback((): Subject => { if (formData$.current === null) { - formData$.current = new Subject({} as T); + formData$.current = new Subject({}); } return formData$.current; }, []); + const updateFormData$ = useCallback( + (nextValue: FormData) => { + getFormData$().next(nextValue); + }, + [getFormData$] + ); + const fieldsToArray = useCallback<() => FieldHook[]>(() => Object.values(fieldsRefs.current), []); const getFieldsForOutput = useCallback( @@ -115,63 +123,24 @@ export function useForm( [] ); - const updateFormDataAt: FormHook['__updateFormDataAt'] = useCallback( + const updateFormDataAt: FormHook['__updateFormDataAt'] = useCallback( (path, value) => { - const _formData$ = getFormData$(); - const currentFormData = _formData$.value; + const currentFormData = getFormData$().value; if (currentFormData[path] !== value) { - _formData$.next({ ...currentFormData, [path]: value }); + updateFormData$({ ...currentFormData, [path]: value }); } - - return _formData$.value; }, - [getFormData$] + [getFormData$, updateFormData$] ); - const updateDefaultValueAt: FormHook['__updateDefaultValueAt'] = useCallback((path, value) => { - set(defaultValueDeserialized.current, path, value); - }, []); - - // -- API - // ---------------------------------- - const getFormData: FormHook['getFormData'] = useCallback( - (getDataOptions: Parameters['getFormData']>[0] = { unflatten: true }) => { - if (getDataOptions.unflatten) { - const fieldsToOutput = getFieldsForOutput(fieldsRefs.current, { - stripEmptyFields: formOptions.stripEmptyFields, - }); - const fieldsValue = mapFormFields(fieldsToOutput, (field) => field.__serializeValue()); - return serializer - ? (serializer(unflattenObject(fieldsValue) as I) as T) - : (unflattenObject(fieldsValue) as T); - } - - return Object.entries(fieldsRefs.current).reduce( - (acc, [key, field]) => ({ - ...acc, - [key]: field.value, - }), - {} as T - ); + const updateDefaultValueAt: FormHook['__updateDefaultValueAt'] = useCallback( + (path, value) => { + set(defaultValueDeserialized.current, path, value); }, - [getFieldsForOutput, formOptions.stripEmptyFields, serializer] + [] ); - const getErrors: FormHook['getErrors'] = useCallback(() => { - if (isValid === true) { - return []; - } - - return fieldsToArray().reduce((acc, field) => { - const fieldError = field.getErrorsMessages(); - if (fieldError === null) { - return acc; - } - return [...acc, fieldError]; - }, [] as string[]); - }, [isValid, fieldsToArray]); - const isFieldValid = (field: FieldHook) => field.isValid && !field.isValidating; const waitForFieldsToFinishValidating = useCallback(async () => { @@ -192,13 +161,13 @@ export function useForm( }); }, [fieldsToArray]); - const validateFields: FormHook['__validateFields'] = useCallback( + const validateFields: FormHook['__validateFields'] = useCallback( async (fieldNames) => { const fieldsToValidate = fieldNames .map((name) => fieldsRefs.current[name]) .filter((field) => field !== undefined); - const formData = getFormData({ unflatten: false }); + const formData = getFormData$().value; const validationResult = await Promise.all( fieldsToValidate.map((field) => field.validate({ formData })) ); @@ -245,34 +214,13 @@ export function useForm( return { areFieldsValid, isFormValid }; }, - [getFormData, fieldsToArray] + [getFormData$, fieldsToArray] ); - const validateAllFields = useCallback(async (): Promise => { - // Maybe some field are being validated because of their async validation(s). - // We make sure those validations have finished executing before proceeding. - await waitForFieldsToFinishValidating(); - - if (!isMounted.current) { - return false; - } - - const fieldsArray = fieldsToArray(); - const fieldsToValidate = fieldsArray.filter((field) => !field.isValidated); - - let isFormValid: boolean | undefined; - - if (fieldsToValidate.length === 0) { - isFormValid = fieldsArray.every(isFieldValid); - } else { - ({ isFormValid } = await validateFields(fieldsToValidate.map((field) => field.path))); - } - - setIsValid(isFormValid); - return isFormValid!; - }, [fieldsToArray, validateFields, waitForFieldsToFinishValidating]); - - const addField: FormHook['__addField'] = useCallback( + // ---------------------------------- + // -- Internal API + // ---------------------------------- + const addField: FormHook['__addField'] = useCallback( (field) => { fieldsRefs.current[field.path] = field; @@ -291,17 +239,17 @@ export function useForm( [updateFormDataAt] ); - const removeField: FormHook['__removeField'] = useCallback( + const removeField: FormHook['__removeField'] = useCallback( (_fieldNames) => { const fieldNames = Array.isArray(_fieldNames) ? _fieldNames : [_fieldNames]; - const currentFormData = { ...getFormData$().value } as FormData; + const currentFormData = { ...getFormData$().value }; fieldNames.forEach((name) => { delete fieldsRefs.current[name]; delete currentFormData[name]; }); - getFormData$().next(currentFormData as T); + updateFormData$(currentFormData); /** * After removing a field, the form validity might have changed @@ -316,40 +264,91 @@ export function useForm( return prev; }); }, - [getFormData$, fieldsToArray] + [getFormData$, updateFormData$, fieldsToArray] + ); + + const getFieldDefaultValue: FormHook['__getFieldDefaultValue'] = useCallback( + (fieldName) => get(defaultValueDeserialized.current, fieldName), + [] + ); + + const readFieldConfigFromSchema: FormHook['__readFieldConfigFromSchema'] = useCallback( + (fieldName) => { + const config = (get(schema ?? {}, fieldName) as FieldConfig) || {}; + + return config; + }, + [schema] ); - const setFieldValue: FormHook['setFieldValue'] = useCallback((fieldName, value) => { + // ---------------------------------- + // -- Public API + // ---------------------------------- + const getFormData: FormHook['getFormData'] = useCallback(() => { + const fieldsToOutput = getFieldsForOutput(fieldsRefs.current, { + stripEmptyFields: formOptions.stripEmptyFields, + }); + const fieldsValue = mapFormFields(fieldsToOutput, (field) => field.__serializeValue()); + return serializer + ? serializer(unflattenObject(fieldsValue)) + : unflattenObject(fieldsValue); + }, [getFieldsForOutput, formOptions.stripEmptyFields, serializer]); + + const getErrors: FormHook['getErrors'] = useCallback(() => { + if (isValid === true) { + return []; + } + + return fieldsToArray().reduce((acc, field) => { + const fieldError = field.getErrorsMessages(); + if (fieldError === null) { + return acc; + } + return [...acc, fieldError]; + }, [] as string[]); + }, [isValid, fieldsToArray]); + + const validate: FormHook['validate'] = useCallback(async (): Promise => { + // Maybe some field are being validated because of their async validation(s). + // We make sure those validations have finished executing before proceeding. + await waitForFieldsToFinishValidating(); + + if (!isMounted.current) { + return false; + } + + const fieldsArray = fieldsToArray(); + const fieldsToValidate = fieldsArray.filter((field) => !field.isValidated); + + let isFormValid: boolean | undefined; + + if (fieldsToValidate.length === 0) { + isFormValid = fieldsArray.every(isFieldValid); + } else { + ({ isFormValid } = await validateFields(fieldsToValidate.map((field) => field.path))); + } + + setIsValid(isFormValid); + return isFormValid!; + }, [fieldsToArray, validateFields, waitForFieldsToFinishValidating]); + + const setFieldValue: FormHook['setFieldValue'] = useCallback((fieldName, value) => { if (fieldsRefs.current[fieldName] === undefined) { return; } fieldsRefs.current[fieldName].setValue(value); }, []); - const setFieldErrors: FormHook['setFieldErrors'] = useCallback((fieldName, errors) => { + const setFieldErrors: FormHook['setFieldErrors'] = useCallback((fieldName, errors) => { if (fieldsRefs.current[fieldName] === undefined) { return; } fieldsRefs.current[fieldName].setErrors(errors); }, []); - const getFields: FormHook['getFields'] = useCallback(() => fieldsRefs.current, []); - - const getFieldDefaultValue: FormHook['__getFieldDefaultValue'] = useCallback( - (fieldName) => get(defaultValueDeserialized.current, fieldName), - [] - ); - - const readFieldConfigFromSchema: FormHook['__readFieldConfigFromSchema'] = useCallback( - (fieldName) => { - const config = (get(schema ?? {}, fieldName) as FieldConfig) || {}; + const getFields: FormHook['getFields'] = useCallback(() => fieldsRefs.current, []); - return config; - }, - [schema] - ); - - const submitForm: FormHook['submit'] = useCallback( + const submit: FormHook['submit'] = useCallback( async (e) => { if (e) { e.preventDefault(); @@ -358,7 +357,7 @@ export function useForm( setIsSubmitted(true); // User has attempted to submit the form at least once setSubmitting(true); - const isFormValid = await validateAllFields(); + const isFormValid = await validate(); const formData = isFormValid ? getFormData() : ({} as T); if (onSubmit) { @@ -371,13 +370,17 @@ export function useForm( return { data: formData, isValid: isFormValid! }; }, - [validateAllFields, getFormData, onSubmit] + [validate, getFormData, onSubmit] ); - const subscribe: FormHook['subscribe'] = useCallback( + const subscribe: FormHook['subscribe'] = useCallback( (handler) => { const subscription = getFormData$().subscribe((raw) => { - handler({ isValid, data: { raw, format: getFormData }, validate: validateAllFields }); + handler({ + isValid, + data: { internal: unflattenObject(raw), format: getFormData }, + validate, + }); }); formUpdateSubscribers.current.push(subscription); @@ -391,17 +394,13 @@ export function useForm( }, }; }, - [getFormData$, isValid, getFormData, validateAllFields] + [getFormData$, isValid, getFormData, validate] ); - /** - * Reset all the fields of the form to their default values - * and reset all the states to their original value. - */ - const reset: FormHook['reset'] = useCallback( + const reset: FormHook['reset'] = useCallback( (resetOptions = { resetValues: true }) => { const { resetValues = true, defaultValue: updatedDefaultValue } = resetOptions; - const currentFormData = { ...getFormData$().value } as FormData; + const currentFormData = { ...getFormData$().value }; if (updatedDefaultValue) { defaultValueDeserialized.current = initDefaultValue(updatedDefaultValue); @@ -417,25 +416,26 @@ export function useForm( currentFormData[path] = fieldDefaultValue; } }); + if (resetValues) { - getFormData$().next(currentFormData as T); + updateFormData$(currentFormData); } setIsSubmitted(false); setSubmitting(false); setIsValid(undefined); }, - [getFormData$, initDefaultValue, getFieldDefaultValue] + [getFormData$, updateFormData$, initDefaultValue, getFieldDefaultValue] ); - const form = useMemo>(() => { + const form = useMemo>(() => { return { isSubmitted, isSubmitting, isValid, id, - submit: submitForm, - validate: validateAllFields, + submit, + validate, subscribe, setFieldValue, setFieldErrors, @@ -458,7 +458,7 @@ export function useForm( isSubmitting, isValid, id, - submitForm, + submit, subscribe, setFieldValue, setFieldErrors, @@ -475,7 +475,7 @@ export function useForm( addField, removeField, validateFields, - validateAllFields, + validate, ]); useEffect(() => { diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data.test.tsx b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data.test.tsx index 0fb65daecf2f4..beb8e58edbf49 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data.test.tsx +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data.test.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef } from 'react'; import { act } from 'react-dom/test-utils'; import { registerTestBed, TestBed } from '../shared_imports'; @@ -25,37 +25,59 @@ import { Form, UseField } from '../components'; import { useForm } from './use_form'; import { useFormData, HookReturn } from './use_form_data'; -interface Props { - onChange(data: HookReturn): void; +interface Props { + onChange(data: HookReturn): void; watch?: string | string[]; } +interface Form1 { + title: string; +} + +interface Form2 { + user: { + firstName: string; + lastName: string; + }; +} + +interface Form3 { + title: string; + subTitle: string; +} + describe('useFormData() hook', () => { - const HookListenerComp = React.memo(({ onChange, watch }: Props) => { - const hookValue = useFormData({ watch }); + const HookListenerComp = function ({ onChange, watch }: Props) { + const hookValue = useFormData({ watch }); + const isMounted = useRef(false); useEffect(() => { - onChange(hookValue); + if (isMounted.current) { + onChange(hookValue); + } + isMounted.current = true; }, [hookValue, onChange]); return null; - }); + }; + + const HookListener = React.memo(HookListenerComp); describe('form data updates', () => { let testBed: TestBed; let onChangeSpy: jest.Mock; const getLastMockValue = () => { - return onChangeSpy.mock.calls[onChangeSpy.mock.calls.length - 1][0] as HookReturn; + return onChangeSpy.mock.calls[onChangeSpy.mock.calls.length - 1][0] as HookReturn; }; - const TestComp = (props: Props) => { - const { form } = useForm(); + const TestComp = (props: Props) => { + const { form } = useForm(); return (
- + ); }; @@ -70,9 +92,7 @@ describe('useFormData() hook', () => { }); test('should return the form data', () => { - // Called twice: - // once when the hook is called and once when the fields have mounted and updated the form data - expect(onChangeSpy).toBeCalledTimes(2); + expect(onChangeSpy).toBeCalledTimes(1); const [data] = getLastMockValue(); expect(data).toEqual({ title: 'titleInitialValue' }); }); @@ -86,7 +106,7 @@ describe('useFormData() hook', () => { setInputValue('titleField', 'titleChanged'); }); - expect(onChangeSpy).toBeCalledTimes(3); + expect(onChangeSpy).toBeCalledTimes(2); const [data] = getLastMockValue(); expect(data).toEqual({ title: 'titleChanged' }); }); @@ -96,17 +116,17 @@ describe('useFormData() hook', () => { let onChangeSpy: jest.Mock; const getLastMockValue = () => { - return onChangeSpy.mock.calls[onChangeSpy.mock.calls.length - 1][0] as HookReturn; + return onChangeSpy.mock.calls[onChangeSpy.mock.calls.length - 1][0] as HookReturn; }; - const TestComp = (props: Props) => { - const { form } = useForm(); + const TestComp = (props: Props) => { + const { form } = useForm(); return (
- + ); }; @@ -121,8 +141,8 @@ describe('useFormData() hook', () => { }); test('should expose a handler to build the form data', () => { - const { 1: format } = getLastMockValue(); - expect(format()).toEqual({ + const [formData] = getLastMockValue(); + expect(formData).toEqual({ user: { firstName: 'John', lastName: 'Snow', @@ -137,11 +157,11 @@ describe('useFormData() hook', () => { let onChangeSpy: jest.Mock; const getLastMockValue = () => { - return onChangeSpy.mock.calls[onChangeSpy.mock.calls.length - 1][0] as HookReturn; + return onChangeSpy.mock.calls[onChangeSpy.mock.calls.length - 1][0] as HookReturn; }; - const TestComp = (props: Props) => { - const { form } = useForm(); + const TestComp = (props: Props) => { + const { form } = useForm(); return (
@@ -190,9 +210,9 @@ describe('useFormData() hook', () => { return onChangeSpy.mock.calls[onChangeSpy.mock.calls.length - 1][0] as HookReturn; }; - const TestComp = ({ onChange }: Props) => { + const TestComp = ({ onChange }: Props) => { const { form } = useForm(); - const hookValue = useFormData({ form }); + const hookValue = useFormData({ form }); useEffect(() => { onChange(hookValue); diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data.ts index 6c6dee3624979..9487e2d30c680 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data.ts @@ -19,6 +19,7 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import { FormData, FormHook } from '../types'; +import { unflattenObject } from '../lib'; import { useFormDataContext, Context } from '../form_data_context'; interface Options { @@ -26,14 +27,16 @@ interface Options { form?: FormHook; } -export type HookReturn = [FormData, () => T, boolean]; +export type HookReturn = [I, () => T, boolean]; -export const useFormData = (options: Options = {}): HookReturn => { +export const useFormData = ( + options: Options = {} +): HookReturn => { const { watch, form } = options; - const ctx = useFormDataContext(); + const ctx = useFormDataContext(); - let getFormData: Context['getFormData']; - let getFormData$: Context['getFormData$']; + let getFormData: Context['getFormData']; + let getFormData$: Context['getFormData$']; if (form !== undefined) { getFormData = form.getFormData; @@ -50,30 +53,33 @@ export const useFormData = (options: Options = {}): const previousRawData = useRef(initialValue); const isMounted = useRef(false); - const [formData, setFormData] = useState(previousRawData.current); + const [formData, setFormData] = useState(() => unflattenObject(previousRawData.current)); - const formatFormData = useCallback(() => { - return getFormData({ unflatten: true }); - }, [getFormData]); + /** + * We do want to offer to the consumer a handler to serialize the form data that changes each time + * the formData **state** changes. This is why we added the "formData" dep to the array and added the eslint override. + */ + const serializer = useCallback(() => { + return getFormData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [getFormData, formData]); useEffect(() => { const subscription = getFormData$().subscribe((raw) => { + if (!isMounted.current && Object.keys(raw).length === 0) { + return; + } + if (watch) { - const valuesToWatchArray = Array.isArray(watch) - ? (watch as string[]) - : ([watch] as string[]); + const pathsToWatchArray: string[] = Array.isArray(watch) ? watch : [watch]; - if ( - valuesToWatchArray.some( - (value) => previousRawData.current[value] !== raw[value as keyof T] - ) - ) { + if (pathsToWatchArray.some((path) => previousRawData.current[path] !== raw[path])) { previousRawData.current = raw; // Only update the state if one of the field we watch has changed. - setFormData(raw); + setFormData(unflattenObject(raw)); } } else { - setFormData(raw); + setFormData(unflattenObject(raw)); } }); return subscription.unsubscribe; @@ -88,8 +94,8 @@ export const useFormData = (options: Options = {}): if (!isMounted.current && Object.keys(formData).length === 0) { // No field has mounted yet - return [formData, formatFormData, false]; + return [formData, serializer, false]; } - return [formData, formatFormData, true]; + return [formData, serializer, true]; }; diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/lib/utils.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/lib/utils.ts index 7d506e28794fd..f67070c8746a1 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/lib/utils.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/lib/utils.ts @@ -20,25 +20,11 @@ import { set } from '@elastic/safer-lodash-set'; import { FieldHook } from '../types'; -export const unflattenObject = (object: any) => +export const unflattenObject = (object: object): T => Object.entries(object).reduce((acc, [key, value]) => { set(acc, key, value); return acc; - }, {}); - -export const flattenObject = ( - object: Record, - to: Record = {}, - paths: string[] = [] -): Record => - Object.entries(object).reduce((acc, [key, value]) => { - const updatedPaths = [...paths, key]; - if (value !== null && !Array.isArray(value) && typeof value === 'object') { - return flattenObject(value, to, updatedPaths); - } - acc[updatedPaths.join('.')] = value; - return acc; - }, to); + }, {} as T); /** * Helper to map the object of fields to any of its value diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts index ae731caff2881..6196ae83a84a6 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/types.ts @@ -40,33 +40,36 @@ export interface FormHook submit: (e?: FormEvent | MouseEvent) => Promise<{ data: T; isValid: boolean }>; /** Use this handler to get the validity of the form. */ validate: () => Promise; - subscribe: (handler: OnUpdateHandler) => Subscription; + subscribe: (handler: OnUpdateHandler) => Subscription; /** Sets a field value imperatively. */ setFieldValue: (fieldName: string, value: FieldValue) => void; /** Sets a field errors imperatively. */ setFieldErrors: (fieldName: string, errors: ValidationError[]) => void; - /** Access any field on the form. */ + /** Access the fields on the form. */ getFields: () => FieldsMap; /** * Return the form data. It accepts an optional options object with an `unflatten` parameter (defaults to `true`). * If you are only interested in the raw form data, pass `unflatten: false` to the handler */ - getFormData: (options?: { unflatten?: boolean }) => T; + getFormData: () => T; /* Returns an array with of all errors in the form. */ getErrors: () => string[]; - /** Resets the form to its initial state. */ + /** + * Reset the form states to their initial value and optionally + * all the fields to their initial values. + */ reset: (options?: { resetValues?: boolean; defaultValue?: Partial }) => void; readonly __options: Required; - __getFormData$: () => Subject; + __getFormData$: () => Subject; __addField: (field: FieldHook) => void; __removeField: (fieldNames: string | string[]) => void; __validateFields: ( fieldNames: string[] ) => Promise<{ areFieldsValid: boolean; isFormValid: boolean | undefined }>; - __updateFormDataAt: (field: string, value: unknown) => T; + __updateFormDataAt: (field: string, value: unknown) => void; __updateDefaultValueAt: (field: string, value: unknown) => void; - __readFieldConfigFromSchema: (fieldName: string) => FieldConfig; - __getFieldDefaultValue: (fieldName: string) => unknown; + __readFieldConfigFromSchema: (field: string) => FieldConfig; + __getFieldDefaultValue: (path: string) => unknown; } export type FormSchema = { @@ -83,16 +86,18 @@ export interface FormConfig { +export interface OnFormUpdateArg { data: { - raw: { [key: string]: any }; + internal: I; format: () => T; }; validate: () => Promise; isValid?: boolean; } -export type OnUpdateHandler = (arg: OnFormUpdateArg) => void; +export type OnUpdateHandler = ( + arg: OnFormUpdateArg +) => void; export interface FormOptions { valueChangeDebounceTime?: number; @@ -119,10 +124,26 @@ export interface FieldHook { validationType?: 'field' | string; errorCode?: string; }) => string | null; + /** + * Form "onChange" event handler + * + * @param event Form input change event + */ onChange: (event: ChangeEvent<{ name?: string; value: string; checked?: boolean }>) => void; + /** + * Handler to change the field value + * + * @param value The new value to assign to the field. If you provide a callback, you wil receive + * the previous value and you need to return the next value. + */ setValue: (value: I | ((prevValue: I) => I)) => void; setErrors: (errors: ValidationError[]) => void; clearErrors: (type?: string | string[]) => void; + /** + * Validate a form field, running all its validations. + * If a validationType is provided then only that validation will be executed, + * skipping the other type of validation that might exist. + */ validate: (validateData?: { formData?: any; value?: I; @@ -166,19 +187,23 @@ export interface ValidationError { [key: string]: any; } -export interface ValidationFuncArg { +export interface ValidationFuncArg { path: string; value: V; form: { - getFormData: FormHook['getFormData']; - getFields: FormHook['getFields']; + getFormData: FormHook['getFormData']; + getFields: FormHook['getFields']; }; - formData: T; + formData: I; errors: readonly ValidationError[]; } -export type ValidationFunc = ( - data: ValidationFuncArg +export type ValidationFunc< + I extends FormData = FormData, + E extends string = string, + V = unknown +> = ( + data: ValidationFuncArg ) => ValidationError | void | undefined | Promise | void | undefined>; export interface FieldValidateResponse { @@ -199,11 +224,11 @@ type FormatterFunc = (value: any, formData: FormData) => unknown; type FieldValue = unknown; export interface ValidationConfig< - FormType extends FormData = any, - Error extends string = string, - ValueType = unknown + I extends FormData = FormData, + E extends string = string, + V = unknown > { - validator: ValidationFunc; + validator: ValidationFunc; type?: string; /** * By default all validation are blockers, which means that if they fail, the field is invalid. diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts index 28aae8c8f4834..fd7f5808f0340 100644 --- a/src/plugins/expressions/common/executor/executor.ts +++ b/src/plugins/expressions/common/executor/executor.ts @@ -170,8 +170,13 @@ export class Executor = Record = Record - >(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) { - const execution = this.createExecution(ast, context); + >( + ast: string | ExpressionAstExpression, + input: Input, + context?: ExtraContext, + options?: ExpressionExecOptions + ) { + const execution = this.createExecution(ast, context, options); execution.start(input); return (await execution.result) as Output; } diff --git a/src/plugins/expressions/common/expression_functions/specs/cumulative_sum.ts b/src/plugins/expressions/common/expression_functions/specs/cumulative_sum.ts new file mode 100644 index 0000000000000..970015638794f --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/cumulative_sum.ts @@ -0,0 +1,170 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../types'; +import { Datatable, DatatableRow } from '../../expression_types'; + +export interface CumulativeSumArgs { + by?: string[]; + inputColumnId: string; + outputColumnId: string; + outputColumnName?: string; +} + +export type ExpressionFunctionCumulativeSum = ExpressionFunctionDefinition< + 'cumulative_sum', + Datatable, + CumulativeSumArgs, + Datatable +>; + +/** + * Returns a string identifying the group of a row by a list of columns to group by + */ +function getBucketIdentifier(row: DatatableRow, groupColumns?: string[]) { + return (groupColumns || []) + .map((groupColumnId) => (row[groupColumnId] == null ? '' : String(row[groupColumnId]))) + .join('|'); +} + +/** + * Calculates the cumulative sum of a specified column in the data table. + * + * Also supports multiple series in a single data table - use the `by` argument + * to specify the columns to split the calculation by. + * For each unique combination of all `by` columns a separate cumulative sum will be calculated. + * The order of rows won't be changed - this function is not modifying any existing columns, it's only + * adding the specified `outputColumnId` column to every row of the table without adding or removing rows. + * + * Behavior: + * * Will write the cumulative sum of `inputColumnId` into `outputColumnId` + * * If provided will use `outputColumnName` as name for the newly created column. Otherwise falls back to `outputColumnId` + * * Cumulative sums always start with 0, a cell will contain its own value plus the values of + * all cells of the same series further up in the table. + * + * Edge cases: + * * Will return the input table if `inputColumnId` does not exist + * * Will throw an error if `outputColumnId` exists already in provided data table + * * If the row value contains `null` or `undefined`, it will be ignored and overwritten with the cumulative sum of + * all cells of the same series further up in the table. + * * For all values besides `null` and `undefined`, the value will be cast to a number before it's added to the + * cumulative sum of the current series - if this results in `NaN` (like in case of objects), all cells of the + * current series will be set to `NaN`. + * * To determine separate series defined by the `by` columns, the values of these columns will be cast to strings + * before comparison. If the values are objects, the return value of their `toString` method will be used for comparison. + * Missing values (`null` and `undefined`) will be treated as empty strings. + */ +export const cumulativeSum: ExpressionFunctionCumulativeSum = { + name: 'cumulative_sum', + type: 'datatable', + + inputTypes: ['datatable'], + + help: i18n.translate('expressions.functions.cumulativeSum.help', { + defaultMessage: 'Calculates the cumulative sum of a column in a data table', + }), + + args: { + by: { + help: i18n.translate('expressions.functions.cumulativeSum.args.byHelpText', { + defaultMessage: 'Column to split the cumulative sum calculation by', + }), + multi: true, + types: ['string'], + required: false, + }, + inputColumnId: { + help: i18n.translate('expressions.functions.cumulativeSum.args.inputColumnIdHelpText', { + defaultMessage: 'Column to calculate the cumulative sum of', + }), + types: ['string'], + required: true, + }, + outputColumnId: { + help: i18n.translate('expressions.functions.cumulativeSum.args.outputColumnIdHelpText', { + defaultMessage: 'Column to store the resulting cumulative sum in', + }), + types: ['string'], + required: true, + }, + outputColumnName: { + help: i18n.translate('expressions.functions.cumulativeSum.args.outputColumnNameHelpText', { + defaultMessage: 'Name of the column to store the resulting cumulative sum in', + }), + types: ['string'], + required: false, + }, + }, + + fn(input, { by, inputColumnId, outputColumnId, outputColumnName }) { + if (input.columns.some((column) => column.id === outputColumnId)) { + throw new Error( + i18n.translate('expressions.functions.cumulativeSum.columnConflictMessage', { + defaultMessage: + 'Specified outputColumnId {columnId} already exists. Please pick another column id.', + values: { + columnId: outputColumnId, + }, + }) + ); + } + + const inputColumnDefinition = input.columns.find((column) => column.id === inputColumnId); + + if (!inputColumnDefinition) { + return input; + } + + const outputColumnDefinition = { + ...inputColumnDefinition, + id: outputColumnId, + name: outputColumnName || outputColumnId, + }; + + const resultColumns = [...input.columns]; + // add output column after input column in the table + resultColumns.splice( + resultColumns.indexOf(inputColumnDefinition) + 1, + 0, + outputColumnDefinition + ); + + const accumulators: Partial> = {}; + return { + ...input, + columns: resultColumns, + rows: input.rows.map((row) => { + const newRow = { ...row }; + + const bucketIdentifier = getBucketIdentifier(row, by); + const accumulatorValue = accumulators[bucketIdentifier] ?? 0; + const currentValue = newRow[inputColumnId]; + if (currentValue != null) { + newRow[outputColumnId] = Number(currentValue) + accumulatorValue; + accumulators[bucketIdentifier] = newRow[outputColumnId]; + } else { + newRow[outputColumnId] = accumulatorValue; + } + + return newRow; + }), + }; + }, +}; diff --git a/src/plugins/expressions/common/expression_functions/specs/index.ts b/src/plugins/expressions/common/expression_functions/specs/index.ts index 5b9562dae5f2e..aadea5882b9c0 100644 --- a/src/plugins/expressions/common/expression_functions/specs/index.ts +++ b/src/plugins/expressions/common/expression_functions/specs/index.ts @@ -25,6 +25,7 @@ import { variableSet } from './var_set'; import { variable } from './var'; import { AnyExpressionFunctionDefinition } from '../types'; import { theme } from './theme'; +import { cumulativeSum } from './cumulative_sum'; export const functionSpecs: AnyExpressionFunctionDefinition[] = [ clog, @@ -34,6 +35,7 @@ export const functionSpecs: AnyExpressionFunctionDefinition[] = [ variableSet, variable, theme, + cumulativeSum, ]; export * from './clog'; @@ -43,3 +45,4 @@ export * from './kibana_context'; export * from './var_set'; export * from './var'; export * from './theme'; +export * from './cumulative_sum'; diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/cumulative_sum.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/cumulative_sum.test.ts new file mode 100644 index 0000000000000..037b3ddc25f89 --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/tests/cumulative_sum.test.ts @@ -0,0 +1,361 @@ +/* + * 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 { functionWrapper } from './utils'; +import { cumulativeSum, CumulativeSumArgs } from '../cumulative_sum'; +import { ExecutionContext } from '../../../execution/types'; +import { Datatable } from '../../../expression_types/specs/datatable'; + +describe('interpreter/functions#cumulative_sum', () => { + const fn = functionWrapper(cumulativeSum); + const runFn = (input: Datatable, args: CumulativeSumArgs) => + fn(input, args, {} as ExecutionContext) as Datatable; + + it('calculates cumulative sum', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [{ val: 5 }, { val: 7 }, { val: 3 }, { val: 2 }], + }, + { inputColumnId: 'val', outputColumnId: 'output' } + ); + expect(result.columns).toContainEqual({ + id: 'output', + name: 'output', + meta: { type: 'number' }, + }); + expect(result.rows.map((row) => row.output)).toEqual([5, 12, 15, 17]); + }); + + it('replaces null or undefined data with zeroes until there is real data', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [{}, { val: null }, { val: undefined }, { val: 1 }], + }, + { inputColumnId: 'val', outputColumnId: 'output' } + ); + expect(result.columns).toContainEqual({ + id: 'output', + name: 'output', + meta: { type: 'number' }, + }); + expect(result.rows.map((row) => row.output)).toEqual([0, 0, 0, 1]); + }); + + it('calculates cumulative sum for multiple series', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: 'A' }, + { val: 2, split: 'B' }, + { val: 3, split: 'B' }, + { val: 4, split: 'A' }, + { val: 5, split: 'A' }, + { val: 6, split: 'A' }, + { val: 7, split: 'B' }, + { val: 8, split: 'B' }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output', by: ['split'] } + ); + + expect(result.rows.map((row) => row.output)).toEqual([ + 1, + 2, + 2 + 3, + 1 + 4, + 1 + 4 + 5, + 1 + 4 + 5 + 6, + 2 + 3 + 7, + 2 + 3 + 7 + 8, + ]); + }); + + it('treats missing split column as separate series', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: 'A' }, + { val: 2, split: 'B' }, + { val: 3 }, + { val: 4, split: 'A' }, + { val: 5 }, + { val: 6, split: 'A' }, + { val: 7, split: 'B' }, + { val: 8, split: 'B' }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output', by: ['split'] } + ); + expect(result.rows.map((row) => row.output)).toEqual([ + 1, + 2, + 3, + 1 + 4, + 3 + 5, + 1 + 4 + 6, + 2 + 7, + 2 + 7 + 8, + ]); + }); + + it('treats null like undefined and empty string for split columns', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: 'A' }, + { val: 2, split: 'B' }, + { val: 3 }, + { val: 4, split: 'A' }, + { val: 5 }, + { val: 6, split: 'A' }, + { val: 7, split: null }, + { val: 8, split: 'B' }, + { val: 9, split: '' }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output', by: ['split'] } + ); + expect(result.rows.map((row) => row.output)).toEqual([ + 1, + 2, + 3, + 1 + 4, + 3 + 5, + 1 + 4 + 6, + 3 + 5 + 7, + 2 + 8, + 3 + 5 + 7 + 9, + ]); + }); + + it('calculates cumulative sum for multiple series by multiple split columns', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + { id: 'split2', name: 'split2', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: 'A', split2: 'C' }, + { val: 2, split: 'B', split2: 'C' }, + { val: 3, split2: 'C' }, + { val: 4, split: 'A', split2: 'C' }, + { val: 5 }, + { val: 6, split: 'A', split2: 'D' }, + { val: 7, split: 'B', split2: 'D' }, + { val: 8, split: 'B', split2: 'D' }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output', by: ['split', 'split2'] } + ); + expect(result.rows.map((row) => row.output)).toEqual([1, 2, 3, 1 + 4, 5, 6, 7, 7 + 8]); + }); + + it('splits separate series by the string representation of the cell values', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: { anObj: 3 } }, + { val: 2, split: { anotherObj: 5 } }, + { val: 10, split: 5 }, + { val: 11, split: '5' }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output', by: ['split'] } + ); + + expect(result.rows.map((row) => row.output)).toEqual([1, 1 + 2, 10, 21]); + }); + + it('casts values to number before calculating cumulative sum', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [{ val: 5 }, { val: '7' }, { val: '3' }, { val: 2 }], + }, + { inputColumnId: 'val', outputColumnId: 'output' } + ); + expect(result.rows.map((row) => row.output)).toEqual([5, 12, 15, 17]); + }); + + it('casts values to number before calculating cumulative sum for NaN like values', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [{ val: 5 }, { val: '7' }, { val: {} }, { val: 2 }], + }, + { inputColumnId: 'val', outputColumnId: 'output' } + ); + expect(result.rows.map((row) => row.output)).toEqual([5, 12, NaN, NaN]); + }); + + it('skips undefined and null values', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [ + { val: null }, + { val: 7 }, + { val: undefined }, + { val: undefined }, + { val: undefined }, + { val: undefined }, + { val: '3' }, + { val: 2 }, + { val: null }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output' } + ); + expect(result.rows.map((row) => row.output)).toEqual([0, 7, 7, 7, 7, 7, 10, 12, 12]); + }); + + it('copies over meta information from the source column', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { + id: 'val', + name: 'val', + meta: { + type: 'number', + + field: 'afield', + index: 'anindex', + params: { id: 'number', params: { pattern: '000' } }, + source: 'synthetic', + sourceParams: { + some: 'params', + }, + }, + }, + ], + rows: [{ val: 5 }], + }, + { inputColumnId: 'val', outputColumnId: 'output' } + ); + expect(result.columns).toContainEqual({ + id: 'output', + name: 'output', + meta: { + type: 'number', + + field: 'afield', + index: 'anindex', + params: { id: 'number', params: { pattern: '000' } }, + source: 'synthetic', + sourceParams: { + some: 'params', + }, + }, + }); + }); + + it('sets output name on output column if specified', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { + id: 'val', + name: 'val', + meta: { + type: 'number', + }, + }, + ], + rows: [{ val: 5 }], + }, + { inputColumnId: 'val', outputColumnId: 'output', outputColumnName: 'Output name' } + ); + expect(result.columns).toContainEqual({ + id: 'output', + name: 'Output name', + meta: { type: 'number' }, + }); + }); + + it('returns source table if input column does not exist', () => { + const input: Datatable = { + type: 'datatable', + columns: [ + { + id: 'val', + name: 'val', + meta: { + type: 'number', + }, + }, + ], + rows: [{ val: 5 }], + }; + expect(runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output' })).toBe(input); + }); + + it('throws an error if output column exists already', () => { + expect(() => + runFn( + { + type: 'datatable', + columns: [ + { + id: 'val', + name: 'val', + meta: { + type: 'number', + }, + }, + ], + rows: [{ val: 5 }], + }, + { inputColumnId: 'val', outputColumnId: 'val' } + ) + ).toThrow(); + }); +}); diff --git a/src/plugins/expressions/common/expression_functions/types.ts b/src/plugins/expressions/common/expression_functions/types.ts index caaef541aefd5..fb1823e85b391 100644 --- a/src/plugins/expressions/common/expression_functions/types.ts +++ b/src/plugins/expressions/common/expression_functions/types.ts @@ -29,6 +29,7 @@ import { ExpressionFunctionVarSet, ExpressionFunctionVar, ExpressionFunctionTheme, + ExpressionFunctionCumulativeSum, } from './specs'; import { ExpressionAstFunction } from '../ast'; import { PersistableStateDefinition } from '../../../kibana_utils/common'; @@ -131,4 +132,5 @@ export interface ExpressionFunctionDefinitions { var_set: ExpressionFunctionVarSet; var: ExpressionFunctionVar; theme: ExpressionFunctionTheme; + cumulative_sum: ExpressionFunctionCumulativeSum; } diff --git a/src/plugins/expressions/common/expression_types/specs/datatable.ts b/src/plugins/expressions/common/expression_types/specs/datatable.ts index c201e99faeb03..abb8fbd4348c4 100644 --- a/src/plugins/expressions/common/expression_types/specs/datatable.ts +++ b/src/plugins/expressions/common/expression_types/specs/datatable.ts @@ -22,6 +22,7 @@ import { map, pick, zipObject } from 'lodash'; import { ExpressionTypeDefinition } from '../types'; import { PointSeries, PointSeriesColumn } from './pointseries'; import { ExpressionValueRender } from './render'; +import { SerializedFieldFormat } from '../../types'; type State = string | number | boolean | null | undefined | SerializableState; @@ -41,22 +42,58 @@ export const isDatatable = (datatable: unknown): datatable is Datatable => /** * This type represents the `type` of any `DatatableColumn` in a `Datatable`. + * its duplicated from KBN_FIELD_TYPES */ -export type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +export type DatatableColumnType = + | '_source' + | 'attachment' + | 'boolean' + | 'date' + | 'geo_point' + | 'geo_shape' + | 'ip' + | 'murmur3' + | 'number' + | 'string' + | 'unknown' + | 'conflict' + | 'object' + | 'nested' + | 'histogram' + | 'null'; /** * This type represents a row in a `Datatable`. */ export type DatatableRow = Record; +/** + * Datatable column meta information + */ export interface DatatableColumnMeta { type: DatatableColumnType; + /** + * field this column is based on + */ field?: string; + /** + * index/table this column is based on + */ index?: string; - params?: SerializableState; + /** + * serialized field format + */ + params?: SerializedFieldFormat; + /** + * source function that produced this column + */ source?: string; + /** + * any extra parameters for the source that produced this column + */ sourceParams?: SerializableState; } + /** * This type represents the shape of a column in a `Datatable`. */ diff --git a/src/plugins/expressions/common/expression_types/specs/index.ts b/src/plugins/expressions/common/expression_types/specs/index.ts index 31210b11f6b7a..00c52a2545cd6 100644 --- a/src/plugins/expressions/common/expression_types/specs/index.ts +++ b/src/plugins/expressions/common/expression_types/specs/index.ts @@ -23,7 +23,6 @@ import { error } from './error'; import { filter } from './filter'; import { image } from './image'; import { kibanaContext } from './kibana_context'; -import { kibanaDatatable } from './kibana_datatable'; import { nullType } from './null'; import { num } from './num'; import { number } from './number'; @@ -42,7 +41,6 @@ export const typeSpecs: AnyExpressionTypeDefinition[] = [ filter, image, kibanaContext, - kibanaDatatable, nullType, num, number, @@ -60,7 +58,6 @@ export * from './error'; export * from './filter'; export * from './image'; export * from './kibana_context'; -export * from './kibana_datatable'; export * from './null'; export * from './num'; export * from './number'; diff --git a/src/plugins/expressions/common/expression_types/specs/kibana_datatable.ts b/src/plugins/expressions/common/expression_types/specs/kibana_datatable.ts deleted file mode 100644 index e226f3b124eed..0000000000000 --- a/src/plugins/expressions/common/expression_types/specs/kibana_datatable.ts +++ /dev/null @@ -1,75 +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 { map } from 'lodash'; -import { SerializedFieldFormat } from '../../types/common'; -import { Datatable, PointSeries, PointSeriesColumn } from '.'; - -const name = 'kibana_datatable'; - -export interface KibanaDatatableColumnMeta { - type: string; - indexPatternId?: string; - aggConfigParams?: Record; -} - -export interface KibanaDatatableColumn { - id: string; - name: string; - meta?: KibanaDatatableColumnMeta; - formatHint?: SerializedFieldFormat; -} - -export interface KibanaDatatableRow { - [key: string]: unknown; -} - -export interface KibanaDatatable { - type: typeof name; - columns: KibanaDatatableColumn[]; - rows: KibanaDatatableRow[]; -} - -export const kibanaDatatable = { - name, - from: { - datatable: (context: Datatable) => { - return { - type: name, - rows: context.rows, - columns: context.columns.map((column) => { - return { - id: column.name, - name: column.name, - }; - }), - }; - }, - pointseries: (context: PointSeries) => { - const columns = map(context.columns, (column: PointSeriesColumn, n) => { - return { id: n, name: n, ...column }; - }); - return { - type: name, - rows: context.rows, - columns, - }; - }, - }, -}; diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index 4a87fd9e7f331..3d0fb968e8a3a 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Executor } from '../executor'; +import { Executor, ExpressionExecOptions } from '../executor'; import { AnyExpressionRenderDefinition, ExpressionRendererRegistry } from '../expression_renderers'; import { ExpressionAstExpression } from '../ast'; import { ExecutionContract } from '../execution/execution_contract'; @@ -101,7 +101,8 @@ export interface ExpressionsServiceStart { run: = Record>( ast: string | ExpressionAstExpression, input: Input, - context?: ExtraContext + context?: ExtraContext, + options?: ExpressionExecOptions ) => Promise; /** @@ -117,7 +118,8 @@ export interface ExpressionsServiceStart { ast: string | ExpressionAstExpression, // This any is for legacy reasons. input: Input, - context?: ExtraContext + context?: ExtraContext, + options?: ExpressionExecOptions ) => ExecutionContract; /** @@ -212,8 +214,8 @@ export class ExpressionsService implements PersistableState AnyExpressionRenderDefinition) ): void => this.renderers.register(definition); - public readonly run: ExpressionsServiceStart['run'] = (ast, input, context) => - this.executor.run(ast, input, context); + public readonly run: ExpressionsServiceStart['run'] = (ast, input, context, options) => + this.executor.run(ast, input, context, options); public readonly getFunction: ExpressionsServiceStart['getFunction'] = (name) => this.executor.getFunction(name); @@ -244,8 +246,8 @@ export class ExpressionsService implements PersistableState => this.executor.getTypes(); - public readonly execute: ExpressionsServiceStart['execute'] = ((ast, input, context) => { - const execution = this.executor.createExecution(ast, context); + public readonly execute: ExpressionsServiceStart['execute'] = ((ast, input, context, options) => { + const execution = this.executor.createExecution(ast, context, options); execution.start(input); return execution.contract; }) as ExpressionsServiceStart['execute']; diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts index 039890c9233cf..893d68238747d 100644 --- a/src/plugins/expressions/public/index.ts +++ b/src/plugins/expressions/public/index.ts @@ -98,10 +98,6 @@ export { isExpressionAstBuilder, KIBANA_CONTEXT_NAME, KibanaContext, - KibanaDatatable, - KibanaDatatableColumn, - KibanaDatatableColumnMeta, - KibanaDatatableRow, KnownTypeToString, Overflow, parse, diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts index c4c40e0812e48..aef4b73f86e34 100644 --- a/src/plugins/expressions/public/loader.ts +++ b/src/plugins/expressions/public/loader.ts @@ -145,11 +145,18 @@ export class ExpressionLoader { this.execution.cancel(); } this.setParams(params); - this.execution = getExpressionsService().execute(expression, params.context, { - search: params.searchContext, - variables: params.variables || {}, - inspectorAdapters: params.inspectorAdapters, - }); + this.execution = getExpressionsService().execute( + expression, + params.context, + { + search: params.searchContext, + variables: params.variables || {}, + inspectorAdapters: params.inspectorAdapters, + }, + { + debug: params.debug, + } + ); const prevDataHandler = this.execution; const data = await prevDataHandler.getData(); @@ -181,6 +188,7 @@ export class ExpressionLoader { if (params.variables && this.params) { this.params.variables = params.variables; } + this.params.debug = Boolean(params.debug); this.params.inspectorAdapters = (params.inspectorAdapters || this.execution?.inspect()) as Adapters; diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index c7b6190b96ed7..4739b9434bdaa 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -82,7 +82,7 @@ export interface DatatableColumn { // Warning: (ae-missing-release-tag) "DatatableColumnType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +export type DatatableColumnType = '_source' | 'attachment' | 'boolean' | 'date' | 'geo_point' | 'geo_shape' | 'ip' | 'murmur3' | 'number' | 'string' | 'unknown' | 'conflict' | 'object' | 'nested' | 'histogram' | 'null'; // Warning: (ae-missing-release-tag) "DatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -228,7 +228,7 @@ export class Executor = Record AnyExpressionFunctionDefinition)): void; // (undocumented) registerType(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void; - run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext): Promise; + run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext, options?: ExpressionExecOptions): Promise; // (undocumented) readonly state: ExecutorContainer; // (undocumented) @@ -377,6 +377,10 @@ export interface ExpressionFunctionDefinitions { // // (undocumented) clog: ExpressionFunctionClog; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionCumulativeSum" needs to be exported by the entry point index.d.ts + // + // (undocumented) + cumulative_sum: ExpressionFunctionCumulativeSum; // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionFont" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -609,12 +613,12 @@ export type ExpressionsServiceSetup = Pick = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => ExecutionContract; + execute: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext, options?: ExpressionExecOptions) => ExecutionContract; fork: () => ExpressionsService; getFunction: (name: string) => ReturnType; getRenderer: (name: string) => ReturnType; getType: (name: string) => ReturnType; - run: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => Promise; + run: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext, options?: ExpressionExecOptions) => Promise; } // Warning: (ae-missing-release-tag) "ExpressionsSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -878,6 +882,8 @@ export interface IExpressionLoaderParams { // (undocumented) customRenderers?: []; // (undocumented) + debug?: boolean; + // (undocumented) disableCaching?: boolean; // (undocumented) inspectorAdapters?: Adapters; @@ -942,54 +948,6 @@ export type KIBANA_CONTEXT_NAME = 'kibana_context'; // @public (undocumented) export type KibanaContext = ExpressionValueSearchContext; -// Warning: (ae-missing-release-tag) "KibanaDatatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatable { - // (undocumented) - columns: KibanaDatatableColumn[]; - // (undocumented) - rows: KibanaDatatableRow[]; - // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts - // - // (undocumented) - type: typeof name_3; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableColumn { - // (undocumented) - formatHint?: SerializedFieldFormat; - // (undocumented) - id: string; - // (undocumented) - meta?: KibanaDatatableColumnMeta; - // (undocumented) - name: string; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableColumnMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableColumnMeta { - // (undocumented) - aggConfigParams?: Record; - // (undocumented) - indexPatternId?: string; - // (undocumented) - type: string; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableRow { - // (undocumented) - [key: string]: unknown; -} - // Warning: (ae-missing-release-tag) "KnownTypeToString" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -1075,7 +1033,7 @@ export interface Range { // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts // // (undocumented) - type: typeof name_4; + type: typeof name_3; } // Warning: (ae-missing-release-tag) "ReactExpressionRenderer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts index 37a4f4fee6336..054c5ac3dc467 100644 --- a/src/plugins/expressions/public/types/index.ts +++ b/src/plugins/expressions/public/types/index.ts @@ -45,6 +45,8 @@ export interface IExpressionLoaderParams { searchContext?: ExecutionContextSearch; context?: ExpressionValue; variables?: Record; + // Enables debug tracking on each expression in the AST + debug?: boolean; disableCaching?: boolean; customFunctions?: []; customRenderers?: []; diff --git a/src/plugins/expressions/server/index.ts b/src/plugins/expressions/server/index.ts index 6785457321595..cc22d4b500d97 100644 --- a/src/plugins/expressions/server/index.ts +++ b/src/plugins/expressions/server/index.ts @@ -89,10 +89,6 @@ export { isExpressionAstBuilder, KIBANA_CONTEXT_NAME, KibanaContext, - KibanaDatatable, - KibanaDatatableColumn, - KibanaDatatableColumnMeta, - KibanaDatatableRow, KnownTypeToString, Overflow, parse, diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md index fd6cc8d742cd4..fcdfd5ef3246c 100644 --- a/src/plugins/expressions/server/server.api.md +++ b/src/plugins/expressions/server/server.api.md @@ -79,7 +79,7 @@ export interface DatatableColumn { // Warning: (ae-missing-release-tag) "DatatableColumnType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +export type DatatableColumnType = '_source' | 'attachment' | 'boolean' | 'date' | 'geo_point' | 'geo_shape' | 'ip' | 'murmur3' | 'number' | 'string' | 'unknown' | 'conflict' | 'object' | 'nested' | 'histogram' | 'null'; // Warning: (ae-missing-release-tag) "DatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -210,7 +210,7 @@ export class Executor = Record AnyExpressionFunctionDefinition)): void; // (undocumented) registerType(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void; - run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext): Promise; + run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext, options?: ExpressionExecOptions): Promise; // (undocumented) readonly state: ExecutorContainer; // (undocumented) @@ -349,6 +349,10 @@ export interface ExpressionFunctionDefinitions { // // (undocumented) clog: ExpressionFunctionClog; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionCumulativeSum" needs to be exported by the entry point index.d.ts + // + // (undocumented) + cumulative_sum: ExpressionFunctionCumulativeSum; // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionFont" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -768,54 +772,6 @@ export type KIBANA_CONTEXT_NAME = 'kibana_context'; // @public (undocumented) export type KibanaContext = ExpressionValueSearchContext; -// Warning: (ae-missing-release-tag) "KibanaDatatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatable { - // (undocumented) - columns: KibanaDatatableColumn[]; - // (undocumented) - rows: KibanaDatatableRow[]; - // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts - // - // (undocumented) - type: typeof name_3; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableColumn { - // (undocumented) - formatHint?: SerializedFieldFormat; - // (undocumented) - id: string; - // (undocumented) - meta?: KibanaDatatableColumnMeta; - // (undocumented) - name: string; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableColumnMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableColumnMeta { - // (undocumented) - aggConfigParams?: Record; - // (undocumented) - indexPatternId?: string; - // (undocumented) - type: string; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableRow { - // (undocumented) - [key: string]: unknown; -} - // Warning: (ae-missing-release-tag) "KnownTypeToString" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -900,7 +856,7 @@ export interface Range { // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts // // (undocumented) - type: typeof name_4; + type: typeof name_3; } // Warning: (ae-missing-release-tag) "SerializedDatatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/home/public/application/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap b/src/plugins/home/public/application/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap index 9a9e055d54d2f..9992fe2c8cf5f 100644 --- a/src/plugins/home/public/application/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap +++ b/src/plugins/home/public/application/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap @@ -526,7 +526,7 @@ exports[`bulkCreate should display success message when bulkCreate is successful size="m" type="check" > -
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap index 45253f6ad27c0..8e7fac9c6c148 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap @@ -6,6 +6,7 @@ exports[`IndexedFieldsTable should filter based on the query bar 1`] = ` editField={[Function]} indexPattern={ Object { + "getFormatterForFieldNoDefault": [Function], "getNonScriptedFields": [Function], } } @@ -14,7 +15,7 @@ exports[`IndexedFieldsTable should filter based on the query bar 1`] = ` Object { "displayName": "Elastic", "excluded": false, - "format": undefined, + "format": "", "info": Array [], "name": "Elastic", "searchable": true, @@ -32,6 +33,7 @@ exports[`IndexedFieldsTable should filter based on the type filter 1`] = ` editField={[Function]} indexPattern={ Object { + "getFormatterForFieldNoDefault": [Function], "getNonScriptedFields": [Function], } } @@ -40,7 +42,7 @@ exports[`IndexedFieldsTable should filter based on the type filter 1`] = ` Object { "displayName": "timestamp", "excluded": false, - "format": undefined, + "format": "", "info": Array [], "name": "timestamp", "type": "date", @@ -57,6 +59,7 @@ exports[`IndexedFieldsTable should render normally 1`] = ` editField={[Function]} indexPattern={ Object { + "getFormatterForFieldNoDefault": [Function], "getNonScriptedFields": [Function], } } @@ -65,7 +68,7 @@ exports[`IndexedFieldsTable should render normally 1`] = ` Object { "displayName": "Elastic", "excluded": false, - "format": undefined, + "format": "", "info": Array [], "name": "Elastic", "searchable": true, @@ -74,7 +77,7 @@ exports[`IndexedFieldsTable should render normally 1`] = ` Object { "displayName": "timestamp", "excluded": false, - "format": undefined, + "format": "", "info": Array [], "name": "timestamp", "type": "date", @@ -82,7 +85,7 @@ exports[`IndexedFieldsTable should render normally 1`] = ` Object { "displayName": "conflictingField", "excluded": false, - "format": undefined, + "format": "", "info": Array [], "name": "conflictingField", "type": "conflict", diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap index 2d51b1722cfb2..2548e7e829789 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap @@ -143,7 +143,7 @@ exports[`Table should render normally 1`] = ` exports[`Table should render the boolean template (false) 1`] = ``; exports[`Table should render the boolean template (true) 1`] = ` -
fields, + getFormatterForFieldNoDefault: () => ({ params: () => ({}) }), } as unknown) as IndexPattern; const mockFieldToIndexPatternField = (spec: Record) => { diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx index 7be420e2af50d..92f0c4576e931 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx @@ -21,7 +21,6 @@ import React, { Component } from 'react'; import { createSelector } from 'reselect'; import { IndexPatternField, IndexPattern, IFieldType } from '../../../../../../plugins/data/public'; import { Table } from './components/table'; -import { getFieldFormat } from './lib'; import { IndexedFieldItem } from './types'; interface IndexedFieldsTableProps { @@ -73,7 +72,7 @@ export class IndexedFieldsTable extends Component< return { ...field.spec, displayName: field.displayName, - format: getFieldFormat(indexPattern, field.name), + format: indexPattern.getFormatterForFieldNoDefault(field.name)?.type?.title || '', excluded: fieldWildcardMatch ? fieldWildcardMatch(field.name) : false, info: helpers.getFieldInfo && helpers.getFieldInfo(indexPattern, field), }; diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/lib/get_field_format.test.ts b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/lib/get_field_format.test.ts deleted file mode 100644 index 2786df641fdb2..0000000000000 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/lib/get_field_format.test.ts +++ /dev/null @@ -1,53 +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 { IIndexPattern } from '../../../../../../data/public'; -import { getFieldFormat } from './get_field_format'; - -const indexPattern = ({ - fieldFormatMap: { - Elastic: { - type: { - title: 'string', - }, - }, - }, -} as unknown) as IIndexPattern; - -describe('getFieldFormat', () => { - test('should handle no arguments', () => { - expect(getFieldFormat()).toEqual(''); - }); - - test('should handle no field name', () => { - expect(getFieldFormat(indexPattern)).toEqual(''); - }); - - test('should handle empty name', () => { - expect(getFieldFormat(indexPattern, '')).toEqual(''); - }); - - test('should handle undefined field name', () => { - expect(getFieldFormat(indexPattern, 'none')).toEqual(undefined); - }); - - test('should retrieve field format', () => { - expect(getFieldFormat(indexPattern, 'Elastic')).toEqual('string'); - }); -}); diff --git a/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap index 3f4190eed9170..1e8fb6f9492fe 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap @@ -30,6 +30,7 @@ exports[`FieldEditor should render create new scripted field correctly 1`] = ` "getByName": [Function], }, "getFormatterForField": [Function], + "getFormatterForFieldNoDefault": [Function], } } isVisible={false} @@ -265,6 +266,7 @@ exports[`FieldEditor should render edit scripted field correctly 1`] = ` "getByName": [Function], }, "getFormatterForField": [Function], + "getFormatterForFieldNoDefault": [Function], } } isVisible={false} @@ -499,6 +501,7 @@ exports[`FieldEditor should show conflict field warning 1`] = ` "getByName": [Function], }, "getFormatterForField": [Function], + "getFormatterForFieldNoDefault": [Function], } } isVisible={false} @@ -762,6 +765,7 @@ exports[`FieldEditor should show deprecated lang warning 1`] = ` "getByName": [Function], }, "getFormatterForField": [Function], + "getFormatterForFieldNoDefault": [Function], } } isVisible={false} @@ -1077,6 +1081,7 @@ exports[`FieldEditor should show multiple type field warning with a table contai "getByName": [Function], }, "getFormatterForField": [Function], + "getFormatterForFieldNoDefault": [Function], } } isVisible={false} diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap index 13be0353e1640..b627dbe0576ee 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap @@ -54,7 +54,7 @@ exports[`UrlFormatEditor should render normally 1`] = ` -