diff --git a/.eslintrc.js b/.eslintrc.js index daf49d9d08281d..5b7dd6d6d03791 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -64,12 +64,6 @@ module.exports = { 'jsx-a11y/no-onchange': 'off', }, }, - { - files: ['src/core/public/application/**/*.{js,ts,tsx}'], - rules: { - 'react/no-danger': 'off', - }, - }, { files: ['src/legacy/core_plugins/console/**/*.{js,ts,tsx}'], rules: { diff --git a/Jenkinsfile b/Jenkinsfile index c002832d4d51a3..6030f2b4a021d8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,7 +4,7 @@ library 'kibana-pipeline-library' kibanaLibrary.load() stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a little bit - timeout(time: 180, unit: 'MINUTES') { + timeout(time: 120, unit: 'MINUTES') { timestamps { ansiColor('xterm') { catchError { diff --git a/docs/apm/advanced-queries.asciidoc b/docs/apm/advanced-queries.asciidoc index 815e7317607851..1f064c1cad3fdc 100644 --- a/docs/apm/advanced-queries.asciidoc +++ b/docs/apm/advanced-queries.asciidoc @@ -10,7 +10,7 @@ so it's easy to share a specific query or view with others. In the screenshot below, you can begin to see some of the transaction fields available for filtering on: [role="screenshot"] -image::apm/images/apm-query-bar.png[Example of the Kibana Query bar in APM UI in Kibana] +image::apm/images/apm-query-bar.png[Example of the Kibana Query bar in APM app in Kibana] [float] ==== Example queries diff --git a/docs/apm/agent-configuration.asciidoc b/docs/apm/agent-configuration.asciidoc index 4d2ae5d01688cc..6f147d0e3223ac 100644 --- a/docs/apm/agent-configuration.asciidoc +++ b/docs/apm/agent-configuration.asciidoc @@ -2,18 +2,15 @@ [[agent-configuration]] === APM Agent configuration -beta[] APM Agent configuration allows you to fine-tune your agent configuration directly in Kibana. +APM Agent configuration allows you to fine-tune your agent configuration directly in Kibana. Best of all, changes are automatically propagated to your APM agents so there's no need to redeploy. -To get started, simply choose the service and environment you wish to configure. +To get started, simply choose the services and environments you wish to configure. +The APM app will let you know when your configurations have been applied by your agents. [role="screenshot"] image::apm/images/apm-agent-configuration.png[APM Agent configuration in Kibana] -IMPORTANT: As this feature is in Beta, a limited number of configuration settings are supported. -We recommend you watch your agent logs to confirm that configuration has been applied. -If you have feedback, please reach out in our https://discuss.elastic.co/c/apm[Discuss forum]. - [float] ==== Precedence @@ -34,6 +31,26 @@ Kibana communicates any changed settings to APM Server so that your agents only [float] ==== Supported configurations +[float] +===== `CAPTURE_BODY` + +added[7.5.0] Can be `"off"`, `"errors"`, `"transactions"`, or `"all"`. Defaults to `"off"`. + +For transactions that are HTTP requests, the Agent can optionally capture the request body, e.g., POST variables. +Remember, request bodies often contain sensitive values like passwords, credit card numbers, etc. +If your service handles sensitive data, enable this feature with care. +Turning on body capturing can also significantly increase the overhead the overhead of the Agent, +and the Elasticsearch index size. + +[float] +===== `TRANSACTION_MAX_SPANS` + +added[7.5.0] A number between `0` and `32000`. Defaults to `500`. + +Limit the number of spans that are recorded per transaction. +This is helpful in cases where a transaction creates a very high amount of spans, e.g., thousands of SQL queries. +Setting an upper limit will help prevent the Agent and the APM Server from being overloaded. + [float] ===== `TRANSACTION_SAMPLE_RATE` diff --git a/docs/apm/errors.asciidoc b/docs/apm/errors.asciidoc index e80438975cba09..689fa1fffa89ee 100644 --- a/docs/apm/errors.asciidoc +++ b/docs/apm/errors.asciidoc @@ -10,12 +10,12 @@ This makes it very easy to quickly see which errors are affecting your services, and to take actions to rectify them. [role="screenshot"] -image::apm/images/apm-errors-overview.png[Example view of the errors overview in the APM UI in Kibana] +image::apm/images/apm-errors-overview.png[Example view of the errors overview in the APM app in Kibana] Selecting an error group ID or error message brings you to the *Error group*. [role="screenshot"] -image::apm/images/apm-error-group.png[Example view of the error group page in the APM UI in Kibana] +image::apm/images/apm-error-group.png[Example view of the error group page in the APM app in Kibana] Here, you'll see the error message, culprit, and the number of occurrences over time. @@ -43,4 +43,4 @@ With Watcher, your team can set up reports within minutes. Watches are managed separately in the dedicated Watcher UI available in Advanced Settings. [role="screenshot"] -image::apm/images/apm-errors-watcher-assistant.png[Example view of the Watcher assistant for errors in APM UI in Kibana] \ No newline at end of file +image::apm/images/apm-errors-watcher-assistant.png[Example view of the Watcher assistant for errors in APM app in Kibana] \ No newline at end of file diff --git a/docs/apm/getting-started.asciidoc b/docs/apm/getting-started.asciidoc index 8f1265b73a8f42..4a391f1a496721 100644 --- a/docs/apm/getting-started.asciidoc +++ b/docs/apm/getting-started.asciidoc @@ -10,7 +10,7 @@ image::apm/images/apm-setup.png[Installation instructions on the APM page in Kib Index patterns tell Kibana which Elasticsearch indices you want to explore. -An APM index pattern is necessary for certain features in the APM UI, like the query bar. +An APM index pattern is necessary for certain features in the APM app, like the query bar. To set up the correct index pattern, simply click *Load Kibana objects* at the bottom of the Setup Instructions. diff --git a/docs/apm/images/apm-metrics.png b/docs/apm/images/apm-metrics.png index a75702ad570d09..6a9789b5a6ecd0 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/jvm-metrics.png b/docs/apm/images/jvm-metrics.png new file mode 100644 index 00000000000000..ffeab27e102468 Binary files /dev/null and b/docs/apm/images/jvm-metrics.png differ diff --git a/docs/apm/metrics.asciidoc b/docs/apm/metrics.asciidoc index 3fc63d6a1344ad..ab394b785ef84a 100644 --- a/docs/apm/metrics.asciidoc +++ b/docs/apm/metrics.asciidoc @@ -2,13 +2,21 @@ === Metrics overview The *Metrics* overview provides agent-specific metrics, -which lets you perform more in-depth root cause analysis investigations within the APM UI. +which lets you perform more in-depth root cause analysis investigations within the APM app. If you're experiencing a problem with your service, you can use this page to attempt to find the underlying cause. For example, you might be able to correlate a high number of errors with a long transaction duration, high CPU usage, or a memory leak. [role="screenshot"] -image::apm/images/apm-metrics.png[Example view of the Metrics overview in APM UI in Kibana] +image::apm/images/apm-metrics.png[Example view of the Metrics overview in APM app in Kibana] + +If you're using the Java Agent, the metrics view focuses on JVMs. +A detailed view of metrics per JVM makes it much easier to analyze the provided metrics: +CPU usage, memory usage, heap or non-heap memory, +thread count, garbage collection rate, and garbage collection time spent per minute. + +[role="screenshot"] +image::apm/images/jvm-metrics.png[Example view of the Metrics overview for the Java Agent] [[machine-learning-integration]] === Machine Learning integration @@ -17,7 +25,7 @@ The Machine Learning integration will initiate a new job predefined to calculate The response time graph will show the expected bounds and annotate the graph when the anomaly score is 75 or above. [role="screenshot"] -image::apm/images/apm-ml-integration.png[Example view of anomaly scores on response times in APM UI in Kibana] +image::apm/images/apm-ml-integration.png[Example view of anomaly scores on response times in APM app in Kibana] Jobs can be created per transaction type and based on the average response time. You can manage jobs in the *Machine Learning jobs management*. diff --git a/docs/apm/services.asciidoc b/docs/apm/services.asciidoc index a6620b5cea7b7d..9af3e74562dab4 100644 --- a/docs/apm/services.asciidoc +++ b/docs/apm/services.asciidoc @@ -6,4 +6,4 @@ The *Services* overview gives you quick insights into the health and general per You can add services by setting the `service.name` configuration in each of the {apm-agents-ref}[APM agents] you’re instrumenting. [role="screenshot"] -image::apm/images/apm-services-overview.png[Example view of services table the APM UI 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] \ No newline at end of file diff --git a/docs/apm/spans.asciidoc b/docs/apm/spans.asciidoc index d23c4f5f4caeaa..75eae61b4cf12a 100644 --- a/docs/apm/spans.asciidoc +++ b/docs/apm/spans.asciidoc @@ -9,7 +9,7 @@ The span timeline visualization is a bird's-eye view of what your application wa This makes it useful for visualizing where the selected transaction spent most of its time. [role="screenshot"] -image::apm/images/apm-distributed-tracing.png[Example view of the distributed tracing in APM UI in Kibana] +image::apm/images/apm-distributed-tracing.png[Example view of the distributed tracing in APM app in Kibana] View a span in detail by clicking on it in the timeline waterfall. For example, in the below screenshot we've clicked on an SQL Select database query. @@ -20,13 +20,13 @@ Finally, APM knows which files are your code and which are just modules or libra These library frames will be minimized by default in order to show you the most relevant stack trace. [role="screenshot"] -image::apm/images/apm-span-detail.png[Example view of a span detail in the APM UI in Kibana] +image::apm/images/apm-span-detail.png[Example view of a span detail in the APM app in Kibana] If your span timeline is colorful, it's indicative of a <>. Services in a distributed trace are separated by color and listed in the order they occur. [role="screenshot"] -image::apm/images/apm-services-trace.png[Example of distributed trace colors in the APM UI in Kibana] +image::apm/images/apm-services-trace.png[Example of distributed trace colors in the APM app in Kibana] Don't forget, 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, @@ -37,4 +37,4 @@ After exploring these traces, you can return to the full trace by clicking *View full trace* in the upper right hand corner of the page. [role="screenshot"] -image::apm/images/apm-transaction-sample.png[Example of distributed trace colors in the APM UI in Kibana] +image::apm/images/apm-transaction-sample.png[Example of distributed trace colors in the APM app in Kibana] diff --git a/docs/apm/traces.asciidoc b/docs/apm/traces.asciidoc index 214b9978185034..09d8f52b92840c 100644 --- a/docs/apm/traces.asciidoc +++ b/docs/apm/traces.asciidoc @@ -11,7 +11,7 @@ it's the collective amount of pain a specific endpoint is causing your users. If there's a particular endpoint you're worried about, you can click on it to view the <>. [role="screenshot"] -image::apm/images/apm-traces.png[Example view of the Traces overview in APM UI in Kibana] +image::apm/images/apm-traces.png[Example view of the Traces overview in APM app in Kibana] [float] [[distributed-tracing]] @@ -22,7 +22,7 @@ Distributed tracing is a key feature of modern application performance monitorin service-based architectures. Distributed tracing allows APM users to automatically trace requests all the way through the service architecture, -and visualize those traces in one single view in the APM UI. +and visualize those traces in one single view in the APM app. This is accomplished by tracing all of the requests, from the initial web request to your front-end service, to queries made to your back-end services. This makes finding possible bottlenecks throughout your application much easier and faster. @@ -31,6 +31,6 @@ By definition, a distributed trace includes more than one transaction. You can use the <> to view a waterfall display of all of the transactions from individual services that are connected in a trace. [role="screenshot"] -image::apm/images/apm-distributed-tracing.png[Example view of the distributed tracing in APM UI in Kibana] +image::apm/images/apm-distributed-tracing.png[Example view of the distributed tracing in APM app in Kibana] TIP: Distributed tracing is supported by all APM agents and there’s no additional configuration needed. \ No newline at end of file diff --git a/docs/apm/transactions.asciidoc b/docs/apm/transactions.asciidoc index e70587baa65ea7..33f61adc8be630 100644 --- a/docs/apm/transactions.asciidoc +++ b/docs/apm/transactions.asciidoc @@ -10,9 +10,9 @@ The *Transactions* table, however, provides only a list of _transaction groups_ In other words, this view groups all transactions of the same name together, and only displays one transaction for each group. [role="screenshot"] -image::apm/images/apm-transactions-overview.png[Example view of transactions table in the APM UI in Kibana] +image::apm/images/apm-transactions-overview.png[Example view of transactions table in the APM app in Kibana] -*Time spent by span type* -- beta[] Certain agents support breakdown graphs in the APM UI. +*Time spent by span type* -- Most agents support breakdown graphs in the APM app. This graph is an easy way to 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? @@ -22,8 +22,6 @@ This could be a sign that the agent does not have auto-instrumentation for whate It's important to note that if you have asynchronous spans, the sum of all span times may exceed the duration of the transaction. -TIP: If the *Time spent by span type* chart is missing in the APM UI, it means your agent does not support this feature yet. - *Transaction duration* shows the response times for this service and is broken down into average, 95th, and 99th percentile. 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, diff --git a/docs/developer/add-data-guide.asciidoc b/docs/developer/add-data-guide.asciidoc index 9c44ae9c9ded30..aec44a9537ee8b 100644 --- a/docs/developer/add-data-guide.asciidoc +++ b/docs/developer/add-data-guide.asciidoc @@ -11,10 +11,13 @@ Each tutorial contains three sets of instructions: [float] === Creating a new tutorial +// TODO: update path to where the directory must be created on the new platform 1. Create a new directory in the link:https://github.com/elastic/kibana/tree/master/src/legacy/core_plugins/kibana/server/tutorials[tutorials directory]. 2. In the new directory, create a file called `index.js` that exports a function. -The function must return a JavaScript object that conforms to the link:https://github.com/elastic/kibana/blob/master/src/legacy/core_plugins/kibana/common/tutorials/tutorial_schema.js[tutorial schema]. -3. Register the tutorial in link:https://github.com/elastic/kibana/blob/master/src/legacy/core_plugins/kibana/server/tutorials/register.js[register.js] by calling `server.registerTutorial(myFuncImportedFromIndexJs)`. +The function must return a JavaScript object that conforms to the link:https://github.com/elastic/kibana/blob/master/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts[tutorial schema]. +// TODO: update path to where the tutorial must be registered on the new platform +3. Register the tutorial in link:https://github.com/elastic/kibana/blob/master/src/legacy/core_plugins/kibana/server/tutorials/register.js[register.js] by calling `server.newPlatform.setup.plugins.home.tutorials.registerTutorial(myFuncImportedFromIndexJs)`. +// TODO: update path to where the image assets must be added on the new platform 4. Add image assets to the link:https://github.com/elastic/kibana/tree/master/src/legacy/core_plugins/kibana/public/home/tutorial_resources[tutorial_resources directory]. 5. Run Kibana locally to preview the tutorial. 6. Create a PR and go through the review process to get the changes approved. diff --git a/docs/developer/plugin/development-uiexports.asciidoc b/docs/developer/plugin/development-uiexports.asciidoc index de713416ae2cd5..6368446f7fb437 100644 --- a/docs/developer/plugin/development-uiexports.asciidoc +++ b/docs/developer/plugin/development-uiexports.asciidoc @@ -8,7 +8,6 @@ An aggregate list of available UiExport types: | Type | Purpose | hacks | Any module that should be included in every application | visTypes | Modules that register providers with the `ui/registry/vis_types` registry. -| fieldFormats | Modules that register providers with the `ui/registry/field_formats` registry. | inspectorViews | Modules that register custom inspector views via the `viewRegistry` in `ui/inspector`. | chromeNavControls | Modules that register providers with the `ui/registry/chrome_nav_controls` registry. | navbarExtensions | Modules that register providers with the `ui/registry/navbar_extensions` registry. diff --git a/docs/development/core/public/kibana-plugin-public.chromehelpextension.appname.md b/docs/development/core/public/kibana-plugin-public.chromehelpextension.appname.md new file mode 100644 index 00000000000000..d817238c9287d3 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.chromehelpextension.appname.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeHelpExtension](./kibana-plugin-public.chromehelpextension.md) > [appName](./kibana-plugin-public.chromehelpextension.appname.md) + +## ChromeHelpExtension.appName property + +Provide your plugin's name to create a header for separation + +Signature: + +```typescript +appName: string; +``` diff --git a/docs/development/core/public/kibana-plugin-public.chromehelpextension.content.md b/docs/development/core/public/kibana-plugin-public.chromehelpextension.content.md new file mode 100644 index 00000000000000..b51d4928e991dc --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.chromehelpextension.content.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeHelpExtension](./kibana-plugin-public.chromehelpextension.md) > [content](./kibana-plugin-public.chromehelpextension.content.md) + +## ChromeHelpExtension.content property + +Custom content to occur below the list of links + +Signature: + +```typescript +content?: (element: HTMLDivElement) => () => void; +``` diff --git a/docs/development/core/public/kibana-plugin-public.chromehelpextension.links.md b/docs/development/core/public/kibana-plugin-public.chromehelpextension.links.md new file mode 100644 index 00000000000000..de17ca8d86e379 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.chromehelpextension.links.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeHelpExtension](./kibana-plugin-public.chromehelpextension.md) > [links](./kibana-plugin-public.chromehelpextension.links.md) + +## ChromeHelpExtension.links property + +Creates unified links for sending users to documentation, GitHub, Discuss, or a custom link/button + +Signature: + +```typescript +links?: ChromeHelpExtensionMenuLink[]; +``` diff --git a/docs/development/core/public/kibana-plugin-public.chromehelpextension.md b/docs/development/core/public/kibana-plugin-public.chromehelpextension.md index 82e7ba9bec4a37..6f0007335c5558 100644 --- a/docs/development/core/public/kibana-plugin-public.chromehelpextension.md +++ b/docs/development/core/public/kibana-plugin-public.chromehelpextension.md @@ -2,11 +2,20 @@ [Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeHelpExtension](./kibana-plugin-public.chromehelpextension.md) -## ChromeHelpExtension type +## ChromeHelpExtension interface Signature: ```typescript -export declare type ChromeHelpExtension = (element: HTMLDivElement) => () => void; +export interface ChromeHelpExtension ``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [appName](./kibana-plugin-public.chromehelpextension.appname.md) | string | Provide your plugin's name to create a header for separation | +| [content](./kibana-plugin-public.chromehelpextension.content.md) | (element: HTMLDivElement) => () => void | Custom content to occur below the list of links | +| [links](./kibana-plugin-public.chromehelpextension.links.md) | ChromeHelpExtensionMenuLink[] | Creates unified links for sending users to documentation, GitHub, Discuss, or a custom link/button | + diff --git a/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenucustomlink.md b/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenucustomlink.md new file mode 100644 index 00000000000000..daca70f3b79c1e --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenucustomlink.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeHelpExtensionMenuCustomLink](./kibana-plugin-public.chromehelpextensionmenucustomlink.md) + +## ChromeHelpExtensionMenuCustomLink type + + +Signature: + +```typescript +export declare type ChromeHelpExtensionMenuCustomLink = EuiButtonEmptyProps & { + linkType: 'custom'; + content: React.ReactNode; +}; +``` diff --git a/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenudiscusslink.md b/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenudiscusslink.md new file mode 100644 index 00000000000000..8dd1c796bebf17 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenudiscusslink.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeHelpExtensionMenuDiscussLink](./kibana-plugin-public.chromehelpextensionmenudiscusslink.md) + +## ChromeHelpExtensionMenuDiscussLink type + + +Signature: + +```typescript +export declare type ChromeHelpExtensionMenuDiscussLink = EuiButtonEmptyProps & { + linkType: 'discuss'; + href: string; +}; +``` diff --git a/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenudocumentationlink.md b/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenudocumentationlink.md new file mode 100644 index 00000000000000..0114cc245a8747 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenudocumentationlink.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeHelpExtensionMenuDocumentationLink](./kibana-plugin-public.chromehelpextensionmenudocumentationlink.md) + +## ChromeHelpExtensionMenuDocumentationLink type + + +Signature: + +```typescript +export declare type ChromeHelpExtensionMenuDocumentationLink = EuiButtonEmptyProps & { + linkType: 'documentation'; + href: string; +}; +``` diff --git a/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenugithublink.md b/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenugithublink.md new file mode 100644 index 00000000000000..5dd33f1a05a7fa --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenugithublink.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeHelpExtensionMenuGitHubLink](./kibana-plugin-public.chromehelpextensionmenugithublink.md) + +## ChromeHelpExtensionMenuGitHubLink type + + +Signature: + +```typescript +export declare type ChromeHelpExtensionMenuGitHubLink = EuiButtonEmptyProps & { + linkType: 'github'; + labels: string[]; + title?: string; +}; +``` diff --git a/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenulink.md b/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenulink.md new file mode 100644 index 00000000000000..072ce165e23c5e --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.chromehelpextensionmenulink.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeHelpExtensionMenuLink](./kibana-plugin-public.chromehelpextensionmenulink.md) + +## ChromeHelpExtensionMenuLink type + + +Signature: + +```typescript +export declare type ChromeHelpExtensionMenuLink = ExclusiveUnion>>; +``` diff --git a/docs/development/core/public/kibana-plugin-public.md b/docs/development/core/public/kibana-plugin-public.md index cec307032094ec..22794ca9455401 100644 --- a/docs/development/core/public/kibana-plugin-public.md +++ b/docs/development/core/public/kibana-plugin-public.md @@ -33,6 +33,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ChromeBadge](./kibana-plugin-public.chromebadge.md) | | | [ChromeBrand](./kibana-plugin-public.chromebrand.md) | | | [ChromeDocTitle](./kibana-plugin-public.chromedoctitle.md) | APIs for accessing and updating the document title. | +| [ChromeHelpExtension](./kibana-plugin-public.chromehelpextension.md) | | | [ChromeNavControl](./kibana-plugin-public.chromenavcontrol.md) | | | [ChromeNavControls](./kibana-plugin-public.chromenavcontrols.md) | [APIs](./kibana-plugin-public.chromenavcontrols.md) for registering new controls to be displayed in the navigation bar. | | [ChromeNavLink](./kibana-plugin-public.chromenavlink.md) | | @@ -98,7 +99,11 @@ The plugin integrates with the core system via lifecycle events: `setup` | --- | --- | | [AppUnmount](./kibana-plugin-public.appunmount.md) | A function called when an application should be unmounted from the page. This function should be synchronous. | | [ChromeBreadcrumb](./kibana-plugin-public.chromebreadcrumb.md) | | -| [ChromeHelpExtension](./kibana-plugin-public.chromehelpextension.md) | | +| [ChromeHelpExtensionMenuCustomLink](./kibana-plugin-public.chromehelpextensionmenucustomlink.md) | | +| [ChromeHelpExtensionMenuDiscussLink](./kibana-plugin-public.chromehelpextensionmenudiscusslink.md) | | +| [ChromeHelpExtensionMenuDocumentationLink](./kibana-plugin-public.chromehelpextensionmenudocumentationlink.md) | | +| [ChromeHelpExtensionMenuGitHubLink](./kibana-plugin-public.chromehelpextensionmenugithublink.md) | | +| [ChromeHelpExtensionMenuLink](./kibana-plugin-public.chromehelpextensionmenulink.md) | | | [ChromeNavLinkUpdateableFields](./kibana-plugin-public.chromenavlinkupdateablefields.md) | | | [HandlerContextType](./kibana-plugin-public.handlercontexttype.md) | Extracts the type of the first argument of a [HandlerFunction](./kibana-plugin-public.handlerfunction.md) to represent the type of the context. | | [HandlerFunction](./kibana-plugin-public.handlerfunction.md) | A function that accepts a context object and an optional number of additional arguments. Used for the generic types in [IContextContainer](./kibana-plugin-public.icontextcontainer.md) | diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 38fceeb47d6fd2..977a65f62202d2 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -46,6 +46,7 @@ adapt to the interval between measurements. Keys are http://en.wikipedia.org/wik `dateFormat:tz`:: The timezone that Kibana uses. The default value of `Browser` uses the timezone detected by the browser. `dateNanosFormat`:: The format to use for displaying https://momentjs.com/docs/#/displaying/format/[pretty formatted dates] of {ref}/date_nanos.html[Elasticsearch date_nanos type]. `defaultIndex`:: The index to access if no index is set. The default is `null`. +`defaultRoute`:: The default route when opening Kibana. Use this setting to route users to a specific dashboard, application, or saved object as they enter each space. `fields:popularLimit`:: The top N most popular fields to show. `filterEditor:suggestValues`:: Set this property to `false` to prevent the filter editor from suggesting values for fields. `filters:pinnedByDefault`:: Set this property to `true` to make filters have a global state (be pinned) by default. diff --git a/docs/management/managing-fields.asciidoc b/docs/management/managing-fields.asciidoc index 6bfd36bc1c067b..308e61abf70e55 100644 --- a/docs/management/managing-fields.asciidoc +++ b/docs/management/managing-fields.asciidoc @@ -29,7 +29,7 @@ include::field-formatters/url-formatter.asciidoc[] Date fields support the `Date`, `Url`, and `String` formatters. -The `Date` formatter enables you to choose the display format of date stamps using the http://moment.js[moment.js] +The `Date` formatter enables you to choose the display format of date stamps using the https://momentjs.com/[moment.js] standard format definitions. include::field-formatters/string-formatter.asciidoc[] @@ -65,7 +65,7 @@ the https://adamwdraper.github.io/Numeral-js/[numeral.js] standard format defini Scripted fields compute data on the fly from the data in your Elasticsearch indices. Scripted field data is shown on the Discover tab as part of the document data, and you can use scripted fields in your visualizations. Scripted field values are computed at query time so they aren't indexed and cannot be searched using Kibana's default -query language. However they can be queried using Kibana's new <>. Scripted +query language. However they can be queried using Kibana's new <>. Scripted fields are also supported in the filter bar. WARNING: Computing data on the fly with scripted fields can be very resource intensive and can have a direct impact on diff --git a/docs/settings/monitoring-settings.asciidoc b/docs/settings/monitoring-settings.asciidoc index 68dd9a8b3cefb8..2fc74d2ffee32b 100644 --- a/docs/settings/monitoring-settings.asciidoc +++ b/docs/settings/monitoring-settings.asciidoc @@ -45,14 +45,26 @@ production cluster as well as monitor data sent to a dedicated monitoring cluster. `xpack.monitoring.elasticsearch.username`:: -Specifies the user ID that {kib} uses for authentication when it retrieves data -from the monitoring cluster. If not set, {kib} uses the value of the -`elasticsearch.username` setting. +Specifies the username used by {kib} monitoring to establish a persistent connection +in {kib} to the {es} monitoring cluster and to verify licensing status on the {es} +monitoring cluster. + +Every other request performed by the Stack Monitoring UI to the monitoring {es} +cluster uses the authenticated user's credentials, which must be the same on +both the {es} monitoring cluster and the {es} production cluster. + +If not set, {kib} uses the value of the `elasticsearch.username` setting. `xpack.monitoring.elasticsearch.password`:: -Specifies the password that {kib} uses for authentication when it retrieves data -from the monitoring cluster. If not set, {kib} uses the value of the -`elasticsearch.password` setting. +Specifies the password used by {kib} monitoring to establish a persistent connection +in {kib} to the {es} monitoring cluster and to verify licensing status on the {es} +monitoring cluster. + +Every other request performed by the Stack Monitoring UI to the monitoring {es} +cluster uses the authenticated user's credentials, which must be the same on +both the {es} monitoring cluster and the {es} production cluster. + +If not set, {kib} uses the value of the `elasticsearch.password` setting. `telemetry.enabled`:: Set to `true` (default) to send cluster statistics to Elastic. Reporting your diff --git a/docs/setup/install/brew.asciidoc b/docs/setup/install/brew.asciidoc index ad531a83d3690d..3fe104bd047941 100644 --- a/docs/setup/install/brew.asciidoc +++ b/docs/setup/install/brew.asciidoc @@ -32,13 +32,13 @@ and data directory are stored in the following locations. | Type | Description | Default Location | Setting | home | Kibana home directory or `$KIBANA_HOME` - | /usr/local/var/homebrew/linked/kibana + | /usr/local/var/homebrew/linked/kibana-full d| | bin | Binary scripts including `kibana` to start a node and `kibana-plugin` to install plugins - | /usr/local/var/homebrew/linked/kibana/bin + | /usr/local/var/homebrew/linked/kibana-full/bin d| | conf @@ -59,7 +59,7 @@ and data directory are stored in the following locations. | plugins | Plugin files location. Each plugin will be contained in a subdirectory. - | /usr/local/var/homebrew/linked/kibana/plugins + | /usr/local/var/homebrew/linked/kibana-full/plugins d| |======================================================================= diff --git a/docs/spaces/index.asciidoc b/docs/spaces/index.asciidoc index fc858eb6d86ef6..69655aac521e7a 100644 --- a/docs/spaces/index.asciidoc +++ b/docs/spaces/index.asciidoc @@ -22,6 +22,7 @@ Kibana supports spaces in several ways. You can: * <> * <> * <> +* <> * <> [float] @@ -108,6 +109,13 @@ interface. {kib} also has beta <> and <> APIs if you want to automate this process. +[float] +[[spaces-default-route]] +=== Configure a Space-level landing page + +You can create a custom experience for users by configuring the {kib} landing page on a per-space basis. +The landing page can route users to a specific dashboard, application, or saved object as they enter each space. +To configure the landing page, use the `defaultRoute` setting in < Advanced settings>>. [float] [[spaces-delete-started]] diff --git a/package.json b/package.json index ac06753d7d0464..798073ef8a427c 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "@elastic/charts": "^14.0.0", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "1.0.5", - "@elastic/eui": "14.9.0", + "@elastic/eui": "16.0.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "2.3.3", @@ -132,7 +132,7 @@ "@types/recompose": "^0.30.5", "JSONStream": "1.3.5", "abortcontroller-polyfill": "^1.3.0", - "angular": "^1.7.8", + "angular": "^1.7.9", "angular-aria": "^1.7.8", "angular-elastic": "^2.5.1", "angular-recursion": "^1.0.5", diff --git a/packages/kbn-es/README.md b/packages/kbn-es/README.md index 0d9af2442ebf42..4d4c2aa94db07f 100644 --- a/packages/kbn-es/README.md +++ b/packages/kbn-es/README.md @@ -59,4 +59,62 @@ Cloned location of elasticsearch repository, used when running from source Type: `String` -Location where snapshots are cached \ No newline at end of file +Location where snapshots are cached + +## Snapshot Pinning + +Sometimes we need to pin snapshots for a specific version. We'd really like to get this automated, but until that is completed here are the steps to take to build, upload, and switch to pinned snapshots for a branch. + +To use these steps you'll need to setup the google-cloud-sdk, which can be installed on macOS with `brew cask install google-cloud-sdk`. Login with the CLI and you'll have access to the `gsutil` to do efficient/parallel uploads to GCS from the command line. + + 1. Clone the elasticsearch repo somewhere + 2. Checkout the branch you want to build + 3. Run the following to delete old distributables + + ``` + find distribution/archives -type f \( -name 'elasticsearch-*-*.tar.gz' -o -name 'elasticsearch-*-*.zip' \) -not -path *no-jdk* -exec rm {} \; + ``` + + 4. Build the new artifacts + + ``` + ./gradlew -p distribution/archives assemble --parallel + ``` + + 4. Copy new artifacts to your `~/Downloads/tmp-artifacts` + + ``` + rm -rf ~/Downloads/tmp-artifacts + mkdir ~/Downloads/tmp-artifacts + find distribution/archives -type f \( -name 'elasticsearch-*-*.tar.gz' -o -name 'elasticsearch-*-*.zip' \) -not -path *no-jdk* -exec cp {} ~/Downloads/tmp-artifacts \; + ``` + + 5. Calculate shasums of the uploads + + ``` + cd ~/Downloads/tmp-artifacts + find * -exec bash -c "shasum -a 512 {} > {}.sha512" \; + ``` + + 6. Check that the files in `~/Downloads/tmp-artifacts` look reasonable + 7. Upload the files to GCS + + ``` + gsutil -m rsync . gs://kibana-ci-tmp-artifacts/ + ``` + + 8. Once the artifacts are uploaded, modify `packages/kbn-es/src/custom_snapshots.js` in a PR to use a URL formatted like: + + ``` + // force use of manually created snapshots until ReindexPutMappings fix + if (!process.env.KBN_ES_SNAPSHOT_URL && !process.argv.some(isVersionFlag)) { + // return undefined; + return 'https://storage.googleapis.com/kibana-ci-tmp-artifacts/{name}-{version}-{os}-x86_64.{ext}'; + } + ``` + + For 6.8, the format of the url should look like: + + ``` + 'https://storage.googleapis.com/kibana-ci-tmp-artifacts/{name}-{version}.{ext}'; + ``` \ No newline at end of file diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index 9aa9ff980d44ca..f3532cd717ac8f 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -9,6 +9,7 @@ - [Challenges on the server](#challenges-on-the-server) - [Challenges in the browser](#challenges-in-the-browser) - [Plan of action](#plan-of-action) + - [Shared application plugins](#shared-application-plugins) - [Server-side plan of action](#server-side-plan-of-action) - [De-couple from hapi.js server and request objects](#de-couple-from-hapijs-server-and-request-objects) - [Introduce new plugin definition shim](#introduce-new-plugin-definition-shim) @@ -314,6 +315,43 @@ First, decouple your plugin's business logic from the dependencies that are not Once those things are finished for any given plugin, it can officially be switched to the new plugin system. +### Shared application plugins + +Some services have been already moved to the new platform. + +Below you can find their new locations: + +| Service | Old place | New place in the NP | +| --------------- | ----------------------------------------- | --------------------------------------------------- | +| *FieldFormats* | ui/registry/field_formats | plugins/data/public | + +The `FieldFormats` service has been moved to the `data` plugin in the New Platform. If your plugin has any imports from `ui/registry/field_formats`, you'll need to update your imports as follows: + +Use it in your New Platform plugin: + +```ts +class MyPlugin { + setup (core, { data }) { + data.fieldFormats.register(myFieldFormat); + // ... + } + start (core, { data }) { + data.fieldFormats.getType(myFieldFormatId); + // ... + } +} +``` + +Or, in your legacy platform plugin, consume it through the `ui/new_platform` module: + +```ts +import { npSetup, npStart } from 'ui/new_platform'; + +npSetup.plugins.data.fieldFormats.register(myFieldFormat); +npStart.plugins.data.fieldFormats.getType(myFieldFormatId); +// ... +``` + ## Server-side plan of action Legacy server-side plugins access functionality from core and other plugins at runtime via function arguments, which is similar to how they must be architected to use the new plugin system. This greatly simplifies the plan of action for migrating server-side plugins. diff --git a/src/core/public/application/integration_tests/router.test.tsx b/src/core/public/application/integration_tests/router.test.tsx index e6a1070e1a6847..593858851d3872 100644 --- a/src/core/public/application/integration_tests/router.test.tsx +++ b/src/core/public/application/integration_tests/router.test.tsx @@ -18,7 +18,6 @@ */ import React from 'react'; -import ReactDOM from 'react-dom'; import { mount, ReactWrapper } from 'enzyme'; import { createMemoryHistory, History } from 'history'; import { BehaviorSubject } from 'rxjs'; @@ -31,13 +30,8 @@ import { AppRouter, AppNotFound } from '../ui'; const createMountHandler = (htmlString: string) => jest.fn(async ({ appBasePath: basename, element: el }: AppMountParameters) => { - ReactDOM.render( -
, - el - ); - return jest.fn(() => ReactDOM.unmountComponentAtNode(el)); + el.innerHTML = `
\nbasename: ${basename}\nhtml: ${htmlString}\n
`; + return jest.fn(() => (el.innerHTML = '')); }); describe('AppContainer', () => { diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index 3390480e56bdd4..96567394216869 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -418,17 +418,20 @@ describe('start', () => { .pipe(toArray()) .toPromise(); - chrome.setHelpExtension(() => () => undefined); + chrome.setHelpExtension({ appName: 'App name', content: () => () => undefined }); chrome.setHelpExtension(undefined); service.stop(); await expect(promise).resolves.toMatchInlineSnapshot(` - Array [ - undefined, - [Function], - undefined, - ] - `); + Array [ + undefined, + Object { + "appName": "App name", + "content": [Function], + }, + undefined, + ] + `); }); }); }); diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index e686f03413dd52..cc23b79e1c621d 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -36,6 +36,7 @@ import { NavControlsService, ChromeNavControls } from './nav_controls'; import { DocTitleService, ChromeDocTitle } from './doc_title'; import { LoadingIndicator, HeaderWrapper as Header } from './ui'; import { DocLinksStart } from '../doc_links'; +import { ChromeHelpExtensionMenuLink } from './ui/header/header_help_menu'; export { ChromeNavControls, ChromeRecentlyAccessed, ChromeDocTitle }; @@ -58,7 +59,20 @@ export interface ChromeBrand { export type ChromeBreadcrumb = EuiBreadcrumb; /** @public */ -export type ChromeHelpExtension = (element: HTMLDivElement) => () => void; +export interface ChromeHelpExtension { + /** + * Provide your plugin's name to create a header for separation + */ + appName: string; + /** + * Creates unified links for sending users to documentation, GitHub, Discuss, or a custom link/button + */ + links?: ChromeHelpExtensionMenuLink[]; + /** + * Custom content to occur below the list of links + */ + content?: (element: HTMLDivElement) => () => void; +} interface ConstructorParams { browserSupportsCsp: boolean; diff --git a/src/core/public/chrome/index.ts b/src/core/public/chrome/index.ts index b220a81f775f8d..4a500836990a7a 100644 --- a/src/core/public/chrome/index.ts +++ b/src/core/public/chrome/index.ts @@ -26,6 +26,13 @@ export { ChromeBrand, ChromeHelpExtension, } from './chrome_service'; +export { + ChromeHelpExtensionMenuLink, + ChromeHelpExtensionMenuCustomLink, + ChromeHelpExtensionMenuDiscussLink, + ChromeHelpExtensionMenuDocumentationLink, + ChromeHelpExtensionMenuGitHubLink, +} from './ui/header/header_help_menu'; export { ChromeNavLink, ChromeNavLinks, ChromeNavLinkUpdateableFields } from './nav_links'; export { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem } from './recently_accessed'; export { ChromeNavControl, ChromeNavControls } from './nav_controls'; diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index 1e97899be5854e..904618651201f4 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -24,21 +24,13 @@ import * as Rx from 'rxjs'; import { // TODO: add type annotations - // @ts-ignore EuiHeader, - // @ts-ignore EuiHeaderLogo, - // @ts-ignore EuiHeaderSection, - // @ts-ignore EuiHeaderSectionItem, - // @ts-ignore EuiHeaderSectionItemButton, - // @ts-ignore - EuiHideFor, EuiHorizontalRule, EuiIcon, - // @ts-ignore EuiImage, // @ts-ignore EuiNavDrawer, diff --git a/src/core/public/chrome/ui/header/header_help_menu.tsx b/src/core/public/chrome/ui/header/header_help_menu.tsx index c04fbaa07ba712..50659a777eccbd 100644 --- a/src/core/public/chrome/ui/header/header_help_menu.tsx +++ b/src/core/public/chrome/ui/header/header_help_menu.tsx @@ -18,11 +18,12 @@ */ import * as Rx from 'rxjs'; -import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; -import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { InjectedIntl, injectI18n, FormattedMessage } from '@kbn/i18n/react'; import { EuiButtonEmpty, + EuiButtonEmptyProps, EuiFlexGroup, EuiFlexItem, EuiHeaderSectionItemButton, @@ -30,8 +31,11 @@ import { EuiPopover, EuiPopoverTitle, EuiSpacer, + EuiTitle, + EuiHorizontalRule, } from '@elastic/eui'; +import { ExclusiveUnion } from '@elastic/eui'; import { HeaderExtension } from './header_extension'; import { ChromeHelpExtension } from '../../chrome_service'; import { @@ -41,6 +45,69 @@ import { KIBANA_FEEDBACK_LINK, } from '../../constants'; +/** @public */ +export type ChromeHelpExtensionMenuGitHubLink = EuiButtonEmptyProps & { + /** + * Creates a link to a new github issue in the Kibana repo + */ + linkType: 'github'; + /** + * Include at least one app-specific label to be applied to the new github issue + */ + labels: string[]; + /** + * Provides initial text for the title of the issue + */ + title?: string; +}; + +/** @public */ +export type ChromeHelpExtensionMenuDiscussLink = EuiButtonEmptyProps & { + /** + * Creates a generic give feedback link with comment icon + */ + linkType: 'discuss'; + /** + * URL to discuss page. + * i.e. `https://discuss.elastic.co/c/${appName}` + */ + href: string; +}; + +/** @public */ +export type ChromeHelpExtensionMenuDocumentationLink = EuiButtonEmptyProps & { + /** + * Creates a deep-link to app-specific documentation + */ + linkType: 'documentation'; + /** + * URL to documentation page. + * i.e. `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/${appName}.html`, + */ + href: string; +}; + +/** @public */ +export type ChromeHelpExtensionMenuCustomLink = EuiButtonEmptyProps & { + /** + * Extend EuiButtonEmpty to provide extra functionality + */ + linkType: 'custom'; + /** + * Content of the button (in lieu of `children`) + */ + content: React.ReactNode; +}; + +/** @public */ +export type ChromeHelpExtensionMenuLink = ExclusiveUnion< + ChromeHelpExtensionMenuGitHubLink, + ExclusiveUnion< + ChromeHelpExtensionMenuDiscussLink, + ExclusiveUnion + > +>; + interface Props { helpExtension$: Rx.Observable; intl: InjectedIntl; @@ -84,6 +151,36 @@ class HeaderHelpMenuUI extends Component { } } + createGithubUrl = (labels: string[], title?: string) => { + const url = new URL('https://github.com/elastic/kibana/issues/new?'); + + if (labels.length) { + url.searchParams.set('labels', labels.join(',')); + } + + if (title) { + url.searchParams.set('title', title); + } + + return url.toString(); + }; + + createCustomLink = ( + index: number, + text: React.ReactNode, + addSpacer?: boolean, + buttonProps?: EuiButtonEmptyProps + ) => { + return ( + + + {text} + + {addSpacer && } + + ); + }; + public render() { const { intl, kibanaVersion, useDefaultContent, kibanaDocLink } = this.props; const { helpExtension } = this.state; @@ -137,6 +234,74 @@ class HeaderHelpMenuUI extends Component { ) : null; + let customContent; + if (helpExtension) { + const { appName, links, content } = helpExtension; + + const getFeedbackText = () => + i18n.translate('core.ui.chrome.headerGlobalNav.helpMenuGiveFeedbackOnApp', { + defaultMessage: 'Give feedback on {appName}', + values: { appName: helpExtension.appName }, + }); + + const customLinks = + links && + links.map((link, index) => { + const { linkType, title, labels = [], content: text, ...rest } = link; + switch (linkType) { + case 'documentation': + return this.createCustomLink( + index, + , + index < links.length - 1, + { + target: '_blank', + rel: 'noopener', + ...rest, + } + ); + case 'github': + return this.createCustomLink(index, getFeedbackText(), index < links.length - 1, { + iconType: 'logoGithub', + href: this.createGithubUrl(labels, title), + target: '_blank', + rel: 'noopener', + ...rest, + }); + case 'discuss': + return this.createCustomLink(index, getFeedbackText(), index < links.length - 1, { + iconType: 'editorComment', + target: '_blank', + rel: 'noopener', + ...rest, + }); + case 'custom': + return this.createCustomLink(index, text, index < links.length - 1, { ...rest }); + default: + break; + } + }); + + customContent = ( + <> + +

{appName}

+
+ + {customLinks} + {content && ( + <> + {customLinks && } + + + )} + + ); + } + const button = ( { - +

+ +

{
{defaultContent} - {defaultContent && helpExtension && } - {helpExtension && } + {defaultContent && customContent && } + {customContent}
); diff --git a/src/core/public/chrome/ui/header/index.ts b/src/core/public/chrome/ui/header/index.ts index f9c122b864dce2..6d59fc6d9433b8 100644 --- a/src/core/public/chrome/ui/header/index.ts +++ b/src/core/public/chrome/ui/header/index.ts @@ -19,3 +19,10 @@ export { Header, HeaderProps } from './header'; export { HeaderWrapper } from './header_wrapper'; +export { + ChromeHelpExtensionMenuLink, + ChromeHelpExtensionMenuCustomLink, + ChromeHelpExtensionMenuDiscussLink, + ChromeHelpExtensionMenuDocumentationLink, + ChromeHelpExtensionMenuGitHubLink, +} from './header_help_menu'; diff --git a/src/core/public/chrome/ui/index.ts b/src/core/public/chrome/ui/index.ts index 69582f6f1ed52a..81b2fdfb0fcc0e 100644 --- a/src/core/public/chrome/ui/index.ts +++ b/src/core/public/chrome/ui/index.ts @@ -18,4 +18,12 @@ */ export { LoadingIndicator } from './loading_indicator'; -export { Header, HeaderWrapper } from './header'; +export { + Header, + HeaderWrapper, + ChromeHelpExtensionMenuLink, + ChromeHelpExtensionMenuCustomLink, + ChromeHelpExtensionMenuDiscussLink, + ChromeHelpExtensionMenuDocumentationLink, + ChromeHelpExtensionMenuGitHubLink, +} from './header'; diff --git a/src/core/public/index.ts b/src/core/public/index.ts index e040b29814900a..c723c282a7caa1 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -40,6 +40,11 @@ import { ChromeBrand, ChromeBreadcrumb, ChromeHelpExtension, + ChromeHelpExtensionMenuLink, + ChromeHelpExtensionMenuCustomLink, + ChromeHelpExtensionMenuDiscussLink, + ChromeHelpExtensionMenuDocumentationLink, + ChromeHelpExtensionMenuGitHubLink, ChromeNavControl, ChromeNavControls, ChromeNavLink, @@ -242,6 +247,11 @@ export { ChromeBrand, ChromeBreadcrumb, ChromeHelpExtension, + ChromeHelpExtensionMenuLink, + ChromeHelpExtensionMenuCustomLink, + ChromeHelpExtensionMenuDiscussLink, + ChromeHelpExtensionMenuDocumentationLink, + ChromeHelpExtensionMenuGitHubLink, ChromeNavControl, ChromeNavControls, ChromeNavLink, diff --git a/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap b/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap index 94c11f01854270..626c91b6a9668e 100644 --- a/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap +++ b/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap @@ -31,7 +31,7 @@ Array [ ] `; -exports[`FlyoutService openFlyout() renders a flyout to the DOM 2`] = `"
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 [ @@ -74,4 +74,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/public.api.md b/src/core/public/public.api.md index 330b89e1184efd..16a634b2d32872 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -5,7 +5,9 @@ ```ts import { Breadcrumb } from '@elastic/eui'; +import { EuiButtonEmptyProps } from '@elastic/eui'; import { EuiGlobalToastListToast } from '@elastic/eui'; +import { ExclusiveUnion } from '@elastic/eui'; import { IconType } from '@elastic/eui'; import { Observable } from 'rxjs'; import React from 'react'; @@ -119,7 +121,39 @@ export interface ChromeDocTitle { } // @public (undocumented) -export type ChromeHelpExtension = (element: HTMLDivElement) => () => void; +export interface ChromeHelpExtension { + appName: string; + content?: (element: HTMLDivElement) => () => void; + links?: ChromeHelpExtensionMenuLink[]; +} + +// @public (undocumented) +export type ChromeHelpExtensionMenuCustomLink = EuiButtonEmptyProps & { + linkType: 'custom'; + content: React.ReactNode; +}; + +// @public (undocumented) +export type ChromeHelpExtensionMenuDiscussLink = EuiButtonEmptyProps & { + linkType: 'discuss'; + href: string; +}; + +// @public (undocumented) +export type ChromeHelpExtensionMenuDocumentationLink = EuiButtonEmptyProps & { + linkType: 'documentation'; + href: string; +}; + +// @public (undocumented) +export type ChromeHelpExtensionMenuGitHubLink = EuiButtonEmptyProps & { + linkType: 'github'; + labels: string[]; + title?: string; +}; + +// @public (undocumented) +export type ChromeHelpExtensionMenuLink = ExclusiveUnion>>; // @public (undocumented) export interface ChromeNavControl { diff --git a/src/dev/build/tasks/clean_tasks.js b/src/dev/build/tasks/clean_tasks.js index 3ec1d6b6967e9d..c33dfe62621282 100644 --- a/src/dev/build/tasks/clean_tasks.js +++ b/src/dev/build/tasks/clean_tasks.js @@ -169,7 +169,6 @@ export const CleanExtraFilesFromModulesTask = { await scanDelete({ directory: build.resolvePath('node_modules'), regularExpressions, - excludePaths: [build.resolvePath('node_modules/@elastic/ctags-langserver/vendor')], }) ); diff --git a/src/fixtures/stubbed_logstash_index_pattern.js b/src/fixtures/stubbed_logstash_index_pattern.js index e1fa5db8b7140b..9f6d648477d29d 100644 --- a/src/fixtures/stubbed_logstash_index_pattern.js +++ b/src/fixtures/stubbed_logstash_index_pattern.js @@ -21,6 +21,7 @@ import StubIndexPattern from 'test_utils/stub_index_pattern'; import stubbedLogstashFields from 'fixtures/logstash_fields'; import { getKbnFieldType } from '../plugins/data/common'; +import { mockUiSettings } from '../legacy/ui/public/new_platform/new_platform.karma_mock'; export default function stubbedLogstashIndexPatternService() { const mockLogstashFields = stubbedLogstashFields(); @@ -40,7 +41,7 @@ export default function stubbedLogstashIndexPatternService() { }; }); - const indexPattern = new StubIndexPattern('logstash-*', cfg => cfg, 'time', fields); + const indexPattern = new StubIndexPattern('logstash-*', cfg => cfg, 'time', fields, mockUiSettings); indexPattern.id = 'logstash-*'; indexPattern.isTimeNanosBased = () => false; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx index 5b389f5b98aba1..23c9c9ffc94bbe 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx @@ -21,13 +21,12 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPopover } from '@elastic/ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { useState } from 'react'; -import { CoreStart } from 'src/core/public'; import { IndexPattern } from '../../index_patterns'; import { FilterEditor } from './filter_editor'; import { FilterItem } from './filter_item'; import { FilterOptions } from './filter_options'; -import { useKibana, KibanaContextProvider } from '../../../../../../plugins/kibana_react/public'; -import { DataPublicPluginStart, esFilters } from '../../../../../../plugins/data/public'; +import { useKibana } from '../../../../../../plugins/kibana_react/public'; +import { esFilters } from '../../../../../../plugins/data/public'; interface Props { filters: esFilters.Filter[]; @@ -35,48 +34,15 @@ interface Props { className: string; indexPatterns: IndexPattern[]; intl: InjectedIntl; - - // TODO: Only for filter-bar directive! - uiSettings?: CoreStart['uiSettings']; - docLinks?: CoreStart['docLinks']; - pluginDataStart?: DataPublicPluginStart; } function FilterBarUI(props: Props) { const [isAddFilterPopoverOpen, setIsAddFilterPopoverOpen] = useState(false); const kibana = useKibana(); - const uiSettings = kibana.services.uiSettings || props.uiSettings; + const uiSettings = kibana.services.uiSettings; if (!uiSettings) return null; - function hasContext() { - return Boolean(kibana.services.uiSettings); - } - - function wrapInContextIfMissing(content: JSX.Element) { - // TODO: Relevant only as long as directives are used! - if (!hasContext()) { - if (props.docLinks && props.uiSettings && props.pluginDataStart) { - return ( - - {content} - - ); - } else { - throw new Error( - 'Rending filter bar requires providing sufficient context: uiSettings, docLinks and NP data plugin' - ); - } - } - return content; - } - function onFiltersUpdated(filters: esFilters.Filter[]) { if (props.onFiltersUpdated) { props.onFiltersUpdated(filters); @@ -119,7 +85,7 @@ function FilterBarUI(props: Props) { ); - return wrapInContextIfMissing( + return ( { const filter = { ...phraseFilter, diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx index 426c21c99ccdbd..c8b36d84f440e1 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx @@ -24,7 +24,7 @@ import { withKibana, KibanaReactContextValue, } from '../../../../../../../plugins/kibana_react/public'; -import { IDataPluginServices } from '../../../types'; +import { IDataPluginServices } from '../../../../../../../plugins/data/public'; export interface PhraseSuggestorProps { kibana: KibanaReactContextValue; diff --git a/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts b/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts index 6084b4c106452c..91964655f6f3e7 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts @@ -17,15 +17,13 @@ * under the License. */ -// @ts-ignore -import { fieldFormats } from 'ui/registry/field_formats'; import { i18n } from '@kbn/i18n'; // @ts-ignore import { ObjDefine } from './obj_define'; // @ts-ignore import { shortenDottedString } from '../../../../../core_plugins/kibana/common/utils/shorten_dotted_string'; import { IndexPattern } from '../index_patterns'; -import { getNotifications } from '../services'; +import { getNotifications, getFieldFormats } from '../services'; import { FieldFormat, @@ -104,6 +102,8 @@ export class Field implements FieldType { let format = spec.format; if (!format || !(format instanceof FieldFormat)) { + const fieldFormats = getFieldFormats(); + format = indexPattern.fieldFormatMap[spec.name] || fieldFormats.getDefaultInstance(spec.type, spec.esTypes); diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts index 2d43faf49f63db..ee9f9b493ebf25 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts @@ -27,17 +27,14 @@ import mockLogStashFields from '../../../../../../fixtures/logstash_fields'; import { stubbedSavedObjectIndexPattern } from '../../../../../../fixtures/stubbed_saved_object_index_pattern'; import { Field } from '../index_patterns_service'; -import { setNotifications } from '../services'; +import { setNotifications, setFieldFormats } from '../services'; // Temporary disable eslint, will be removed after moving to new platform folder // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { notificationServiceMock } from '../../../../../../core/public/notifications/notifications_service.mock'; +import { FieldFormatRegisty } from '../../../../../../plugins/data/public'; -jest.mock('ui/registry/field_formats', () => ({ - fieldFormats: { - getDefaultInstance: jest.fn(), - }, -})); +jest.mock('ui/new_platform'); jest.mock('../../../../../../plugins/kibana_utils/public', () => { const originalModule = jest.requireActual('../../../../../../plugins/kibana_utils/public'); @@ -142,6 +139,9 @@ describe('IndexPattern', () => { // create an indexPattern instance for each test beforeEach(() => { setNotifications(notifications); + setFieldFormats(({ + getDefaultInstance: jest.fn(), + } as unknown) as FieldFormatRegisty); return create(indexPatternId).then((pattern: IndexPattern) => { indexPattern = pattern; diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.ts b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.ts index 12aa3c2fb0d51a..f77342c7bc2744 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.ts @@ -19,8 +19,6 @@ import _, { each, reject } from 'lodash'; import { i18n } from '@kbn/i18n'; -// @ts-ignore -import { fieldFormats } from 'ui/registry/field_formats'; import { SavedObjectsClientContract } from 'src/core/public'; import { DuplicateField, @@ -30,6 +28,12 @@ import { MappingObject, } from '../../../../../../plugins/kibana_utils/public'; +import { + ES_FIELD_TYPES, + KBN_FIELD_TYPES, + IIndexPattern, +} from '../../../../../../plugins/data/public'; + import { findIndexPatternByTitle, getRoutes } from '../utils'; import { IndexPatternMissingIndices } from '../errors'; import { Field, FieldList, FieldListInterface, FieldType } from '../fields'; @@ -37,8 +41,7 @@ import { createFieldsFetcher } from './_fields_fetcher'; import { formatHitProvider } from './format_hit'; import { flattenHitWrapper } from './flatten_hit'; import { IIndexPatternsApiClient } from './index_patterns_api_client'; -import { ES_FIELD_TYPES, IIndexPattern } from '../../../../../../plugins/data/public'; -import { getNotifications } from '../services'; +import { getNotifications, getFieldFormats } from '../services'; const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3; const type = 'index-pattern'; @@ -114,7 +117,10 @@ export class IndexPattern implements IIndexPattern { this.fields = new FieldList(this, [], this.shortDotsEnable); this.fieldsFetcher = createFieldsFetcher(this, apiClient, this.getConfig('metaFields')); this.flattenHit = flattenHitWrapper(this, this.getConfig('metaFields')); - this.formatHit = formatHitProvider(this, fieldFormats.getDefaultInstance('string')); + this.formatHit = formatHitProvider( + this, + getFieldFormats().getDefaultInstance(KBN_FIELD_TYPES.STRING) + ); this.formatField = this.formatHit.formatField; } @@ -125,12 +131,14 @@ export class IndexPattern implements IIndexPattern { } private deserializeFieldFormatMap(mapping: any) { - const FieldFormat = fieldFormats.getType(mapping.id); + const FieldFormat = getFieldFormats().getType(mapping.id); + return FieldFormat && new FieldFormat(mapping.params, this.getConfig); } private initFields(input?: any) { const newValue = input || this.fields; + this.fields = new FieldList(this, newValue, this.shortDotsEnable); } @@ -451,6 +459,7 @@ export class IndexPattern implements IIndexPattern { const { toasts } = getNotifications(); toasts.addDanger(message); + throw err; } @@ -492,6 +501,7 @@ export class IndexPattern implements IIndexPattern { if (err instanceof IndexPatternMissingIndices) { toasts.addDanger((err as any).message); + return []; } diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts index 8a5c78d13c251c..0a5d1bfcae21f9 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.test.ts @@ -29,12 +29,6 @@ jest.mock('../errors', () => ({ IndexPatternMissingIndices: jest.fn(), })); -jest.mock('ui/registry/field_formats', () => ({ - fieldFormats: { - getDefaultInstance: jest.fn(), - }, -})); - jest.mock('./index_pattern', () => { class IndexPattern { init = async () => { diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts index 2c58af9deaf49d..c8e80b3aede20e 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_patterns.ts @@ -24,8 +24,6 @@ import { UiSettingsClientContract, HttpServiceBase, } from 'src/core/public'; -// @ts-ignore -import { fieldFormats } from 'ui/registry/field_formats'; import { createIndexPatternCache } from './_pattern_cache'; import { IndexPattern } from './index_pattern'; @@ -34,8 +32,6 @@ import { IndexPatternsApiClient, GetFieldsOptions } from './index_patterns_api_c const indexPatternCache = createIndexPatternCache(); export class IndexPatterns { - fieldFormats: fieldFormats; - private config: UiSettingsClientContract; private savedObjectsClient: SavedObjectsClientContract; private savedObjectsCache?: Array>> | null; diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts b/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts index 9ce1b5f2e4a208..c9c52400b1f19c 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts @@ -23,9 +23,10 @@ import { HttpServiceBase, NotificationsStart, } from 'src/core/public'; +import { FieldFormatsStart } from '../../../../../plugins/data/public'; import { Field, FieldList, FieldListInterface, FieldType } from './fields'; import { createIndexPatternSelect } from './components'; -import { setNotifications } from './services'; +import { setNotifications, setFieldFormats } from './services'; import { createFlattenHitWrapper, @@ -40,6 +41,7 @@ export interface IndexPatternDependencies { savedObjectsClient: SavedObjectsClientContract; http: HttpServiceBase; notifications: NotificationsStart; + fieldFormats: FieldFormatsStart; } /** @@ -64,8 +66,15 @@ export class IndexPatternsService { return this.setupApi; } - public start({ uiSettings, savedObjectsClient, http, notifications }: IndexPatternDependencies) { + public start({ + uiSettings, + savedObjectsClient, + http, + notifications, + fieldFormats, + }: IndexPatternDependencies) { setNotifications(notifications); + setFieldFormats(fieldFormats); return { ...this.setupApi, diff --git a/src/legacy/core_plugins/data/public/index_patterns/services.ts b/src/legacy/core_plugins/data/public/index_patterns/services.ts index 5cc087548d6fb1..ecd898b28de636 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/services.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/services.ts @@ -19,7 +19,12 @@ import { NotificationsStart } from 'src/core/public'; import { createGetterSetter } from '../../../../../plugins/kibana_utils/public'; +import { FieldFormatsStart } from '../../../../../plugins/data/public'; export const [getNotifications, setNotifications] = createGetterSetter( 'Notifications' ); + +export const [getFieldFormats, setFieldFormats] = createGetterSetter( + 'FieldFormats' +); diff --git a/src/legacy/core_plugins/data/public/plugin.ts b/src/legacy/core_plugins/data/public/plugin.ts index 2059f61fde59ed..da24576655d2b3 100644 --- a/src/legacy/core_plugins/data/public/plugin.ts +++ b/src/legacy/core_plugins/data/public/plugin.ts @@ -94,6 +94,7 @@ export class DataPlugin implements Plugin void; onChange: (payload: { dateRange: TimeRange; query?: Query }) => void; onRefresh?: (payload: { dateRange: TimeRange }) => void; + dataTestSubj?: string; disableAutoFocus?: boolean; screenTitle?: string; indexPatterns?: Array; @@ -189,6 +190,7 @@ function QueryBarTopRowUI(props: Props) { onChange={onQueryChange} onSubmit={onInputSubmit} persistedLog={persistedLog} + dataTestSubj={props.dataTestSubj} /> ); diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index e97c06ace15797..c8b76c9cda99db 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -35,8 +35,8 @@ import { withKibana, KibanaReactContextValue, } from '../../../../../../../plugins/kibana_react/public'; -import { IDataPluginServices } from '../../../types'; import { + IDataPluginServices, TimeRange, Query, esFilters, @@ -64,7 +64,7 @@ export interface SearchBarOwnProps { isLoading?: boolean; customSubmitButton?: React.ReactNode; screenTitle?: string; - + dataTestSubj?: string; // Togglers showQueryBar?: boolean; showQueryInput?: boolean; @@ -415,6 +415,7 @@ class SearchBarUI extends Component { customSubmitButton={ this.props.customSubmitButton ? this.props.customSubmitButton : undefined } + dataTestSubj={this.props.dataTestSubj} /> ); } diff --git a/src/legacy/core_plugins/data/public/shim/legacy_module.ts b/src/legacy/core_plugins/data/public/shim/legacy_module.ts index edc389b4119712..06c5caa04ba9ac 100644 --- a/src/legacy/core_plugins/data/public/shim/legacy_module.ts +++ b/src/legacy/core_plugins/data/public/shim/legacy_module.ts @@ -19,58 +19,11 @@ import { once } from 'lodash'; -import { wrapInI18nContext } from 'ui/i18n'; - // @ts-ignore import { uiModules } from 'ui/modules'; -import { npStart } from 'ui/new_platform'; -import { FilterBar } from '../filter'; import { IndexPatterns } from '../index_patterns/index_patterns'; /** @internal */ export const initLegacyModule = once((indexPatterns: IndexPatterns): void => { - uiModules - .get('app/kibana', ['react']) - .directive('filterBar', () => { - return { - restrict: 'E', - template: '', - compile: (elem: any) => { - const child = document.createElement('filter-bar-helper'); - - // Copy attributes to the child directive - for (const attr of elem[0].attributes) { - child.setAttribute(attr.name, attr.value); - } - - child.setAttribute('ui-settings', 'uiSettings'); - child.setAttribute('doc-links', 'docLinks'); - child.setAttribute('plugin-data-start', 'pluginDataStart'); - - // Append helper directive - elem.append(child); - - const linkFn = ($scope: any) => { - $scope.uiSettings = npStart.core.uiSettings; - $scope.docLinks = npStart.core.docLinks; - $scope.pluginDataStart = npStart.plugins.data; - }; - - return linkFn; - }, - }; - }) - .directive('filterBarHelper', (reactDirective: any) => { - return reactDirective(wrapInI18nContext(FilterBar), [ - ['uiSettings', { watchDepth: 'reference' }], - ['docLinks', { watchDepth: 'reference' }], - ['onFiltersUpdated', { watchDepth: 'reference' }], - ['indexPatterns', { watchDepth: 'collection' }], - ['filters', { watchDepth: 'collection' }], - ['className', { watchDepth: 'reference' }], - ['pluginDataStart', { watchDepth: 'reference' }], - ]); - }); - uiModules.get('kibana/index_patterns').value('indexPatterns', indexPatterns); }); diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index c7cda8aec0165d..dcb7d7998ff1a4 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -64,7 +64,6 @@ export default function (kibana) { hacks: [ 'plugins/kibana/dev_tools', ], - fieldFormats: ['plugins/kibana/field_formats/register'], savedObjectTypes: [ 'plugins/kibana/visualize/saved_visualizations/saved_visualization_register', 'plugins/kibana/discover/saved_searches/saved_search_register', diff --git a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/get_app_state_mock.ts b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/get_app_state_mock.ts index 1f2094d68063d6..d9dea35a8a1c03 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/get_app_state_mock.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/get_app_state_mock.ts @@ -17,7 +17,7 @@ * under the License. */ -import { AppStateClass } from 'ui/state_management/app_state'; +import { AppStateClass } from '../legacy_imports'; /** * A poor excuse for a mock just to get some basic tests to run in jest without requiring the injector. diff --git a/src/legacy/core_plugins/kibana/public/dashboard/_dashboard_app.scss b/src/legacy/core_plugins/kibana/public/dashboard/_dashboard_app.scss index eebfad5979d68f..14c35759d70a99 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/_dashboard_app.scss +++ b/src/legacy/core_plugins/kibana/public/dashboard/_dashboard_app.scss @@ -1,7 +1,7 @@ .dshAppContainer { - flex: 1; display: flex; flex-direction: column; + height: 100%; } .dshStartScreen { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/application.ts b/src/legacy/core_plugins/kibana/public/dashboard/application.ts new file mode 100644 index 00000000000000..d507d547d9ba95 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/dashboard/application.ts @@ -0,0 +1,228 @@ +/* + * 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 { EuiConfirmModal, EuiIcon } from '@elastic/eui'; +import angular, { IModule } from 'angular'; +import { IPrivate } from 'ui/private'; +import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; +import { + AppMountContext, + ChromeStart, + LegacyCoreStart, + SavedObjectsClientContract, + UiSettingsClientContract, +} from 'kibana/public'; +import { Storage } from '../../../../../plugins/kibana_utils/public'; +import { + GlobalStateProvider, + StateManagementConfigProvider, + AppStateProvider, + PrivateProvider, + EventsProvider, + PersistedState, + createTopNavDirective, + createTopNavHelper, + PromiseServiceCreator, + KbnUrlProvider, + RedirectWhenMissingProvider, + confirmModalFactory, + configureAppAngularModule, +} from './legacy_imports'; + +// @ts-ignore +import { initDashboardApp } from './legacy_app'; +import { DataStart } from '../../../data/public'; +import { EmbeddablePublicPlugin } from '../../../../../plugins/embeddable/public'; +import { NavigationStart } from '../../../navigation/public'; +import { DataPublicPluginStart as NpDataStart } from '../../../../../plugins/data/public'; +import { SharePluginStart } from '../../../../../plugins/share/public'; + +export interface RenderDeps { + core: LegacyCoreStart; + indexPatterns: DataStart['indexPatterns']['indexPatterns']; + dataStart: DataStart; + npDataStart: NpDataStart; + navigation: NavigationStart; + savedObjectsClient: SavedObjectsClientContract; + savedObjectRegistry: any; + dashboardConfig: any; + savedDashboards: any; + dashboardCapabilities: any; + uiSettings: UiSettingsClientContract; + chrome: ChromeStart; + addBasePath: (path: string) => string; + savedQueryService: DataStart['search']['services']['savedQueryService']; + embeddables: ReturnType; + localStorage: Storage; + share: SharePluginStart; +} + +let angularModuleInstance: IModule | null = null; + +export const renderApp = (element: HTMLElement, appBasePath: string, deps: RenderDeps) => { + if (!angularModuleInstance) { + angularModuleInstance = createLocalAngularModule(deps.core, deps.navigation); + // global routing stuff + configureAppAngularModule(angularModuleInstance, deps.core as LegacyCoreStart, true); + // custom routing stuff + initDashboardApp(angularModuleInstance, deps); + } + const $injector = mountDashboardApp(appBasePath, element); + return () => { + $injector.get('$rootScope').$destroy(); + }; +}; + +const mainTemplate = (basePath: string) => `
+ +
+
+`; + +const moduleName = 'app/dashboard'; + +const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react']; + +function mountDashboardApp(appBasePath: string, element: HTMLElement) { + const mountpoint = document.createElement('div'); + mountpoint.setAttribute('style', 'height: 100%'); + // eslint-disable-next-line + mountpoint.innerHTML = mainTemplate(appBasePath); + // bootstrap angular into detached element and attach it later to + // make angular-within-angular possible + const $injector = angular.bootstrap(mountpoint, [moduleName]); + // initialize global state handler + element.appendChild(mountpoint); + return $injector; +} + +function createLocalAngularModule(core: AppMountContext['core'], navigation: NavigationStart) { + createLocalI18nModule(); + createLocalPrivateModule(); + createLocalPromiseModule(); + createLocalConfigModule(core); + createLocalKbnUrlModule(); + createLocalStateModule(); + createLocalPersistedStateModule(); + createLocalTopNavModule(navigation); + createLocalConfirmModalModule(); + createLocalIconModule(); + + const dashboardAngularModule = angular.module(moduleName, [ + ...thirdPartyAngularDependencies, + 'app/dashboard/Config', + 'app/dashboard/I18n', + 'app/dashboard/Private', + 'app/dashboard/PersistedState', + 'app/dashboard/TopNav', + 'app/dashboard/State', + 'app/dashboard/ConfirmModal', + 'app/dashboard/icon', + ]); + return dashboardAngularModule; +} + +function createLocalIconModule() { + angular + .module('app/dashboard/icon', ['react']) + .directive('icon', reactDirective => reactDirective(EuiIcon)); +} + +function createLocalConfirmModalModule() { + angular + .module('app/dashboard/ConfirmModal', ['react']) + .factory('confirmModal', confirmModalFactory) + .directive('confirmModal', reactDirective => reactDirective(EuiConfirmModal)); +} + +function createLocalStateModule() { + angular + .module('app/dashboard/State', [ + 'app/dashboard/Private', + 'app/dashboard/Config', + 'app/dashboard/KbnUrl', + 'app/dashboard/Promise', + 'app/dashboard/PersistedState', + ]) + .factory('AppState', function(Private: any) { + return Private(AppStateProvider); + }) + .service('getAppState', function(Private: any) { + return Private(AppStateProvider).getAppState; + }) + .service('globalState', function(Private: any) { + return Private(GlobalStateProvider); + }); +} + +function createLocalPersistedStateModule() { + angular + .module('app/dashboard/PersistedState', ['app/dashboard/Private', 'app/dashboard/Promise']) + .factory('PersistedState', (Private: IPrivate) => { + const Events = Private(EventsProvider); + return class AngularPersistedState extends PersistedState { + constructor(value: any, path: any) { + super(value, path, Events); + } + }; + }); +} + +function createLocalKbnUrlModule() { + angular + .module('app/dashboard/KbnUrl', ['app/dashboard/Private', 'ngRoute']) + .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)) + .service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider)); +} + +function createLocalConfigModule(core: AppMountContext['core']) { + angular + .module('app/dashboard/Config', ['app/dashboard/Private']) + .provider('stateManagementConfig', StateManagementConfigProvider) + .provider('config', () => { + return { + $get: () => ({ + get: core.uiSettings.get.bind(core.uiSettings), + }), + }; + }); +} + +function createLocalPromiseModule() { + angular.module('app/dashboard/Promise', []).service('Promise', PromiseServiceCreator); +} + +function createLocalPrivateModule() { + angular.module('app/dashboard/Private', []).provider('Private', PrivateProvider); +} + +function createLocalTopNavModule(navigation: NavigationStart) { + angular + .module('app/dashboard/TopNav', ['react']) + .directive('kbnTopNav', createTopNavDirective) + .directive('kbnTopNavHelper', createTopNavHelper(navigation.ui)); +} + +function createLocalI18nModule() { + angular + .module('app/dashboard/I18n', []) + .provider('i18n', I18nProvider) + .filter('i18n', i18nFilter) + .directive('i18nId', i18nDirective); +} diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html index f644f3811e3e09..b645bb408300f1 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.html @@ -4,11 +4,11 @@ >
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index d5da4ba51e55b6..0ce8f2ef59fc0c 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -17,26 +17,16 @@ * under the License. */ -import _ from 'lodash'; - -// @ts-ignore -import { uiModules } from 'ui/modules'; -import { IInjector } from 'ui/chrome'; - -// @ts-ignore -import * as filterActions from 'plugins/kibana/discover/doc_table/actions/filter'; +import { StaticIndexPattern, SavedQuery } from 'plugins/data'; +import moment from 'moment'; +import { Subscription } from 'rxjs'; import { AppStateClass as TAppStateClass, AppState as TAppState, -} from 'ui/state_management/app_state'; - -import { KbnUrl } from 'ui/url/kbn_url'; -import { IndexPattern } from 'ui/index_patterns'; -import { IPrivate } from 'ui/private'; -import { StaticIndexPattern, SavedQuery } from 'plugins/data'; -import moment from 'moment'; -import { Subscription } from 'rxjs'; + IInjector, + KbnUrl, +} from './legacy_imports'; import { ViewMode } from '../../../embeddable_api/public/np_ready/public'; import { SavedObjectDashboard } from './saved_dashboard/saved_dashboard'; @@ -44,6 +34,7 @@ import { DashboardAppState, SavedDashboardPanel, ConfirmModalFn } from './types' import { TimeRange, Query, esFilters } from '../../../../../../src/plugins/data/public'; import { DashboardAppController } from './dashboard_app_controller'; +import { RenderDeps } from './application'; export interface DashboardAppScope extends ng.IScope { dash: SavedObjectDashboard; @@ -90,54 +81,40 @@ export interface DashboardAppScope extends ng.IScope { kbnTopNav: any; enterEditMode: () => void; timefilterSubscriptions$: Subscription; + isVisible: boolean; } -const app = uiModules.get('app/dashboard', ['elasticsearch', 'ngRoute', 'react', 'kibana/config']); - -app.directive('dashboardApp', function($injector: IInjector) { - const AppState = $injector.get>('AppState'); - const kbnUrl = $injector.get('kbnUrl'); - const confirmModal = $injector.get('confirmModal'); - const config = $injector.get('config'); - - const Private = $injector.get('Private'); +export function initDashboardAppDirective(app: any, deps: RenderDeps) { + app.directive('dashboardApp', function($injector: IInjector) { + const AppState = $injector.get>('AppState'); + const kbnUrl = $injector.get('kbnUrl'); + const confirmModal = $injector.get('confirmModal'); + const config = deps.uiSettings; - const indexPatterns = $injector.get<{ - getDefault: () => Promise; - }>('indexPatterns'); - - return { - restrict: 'E', - controllerAs: 'dashboardApp', - controller: ( - $scope: DashboardAppScope, - $route: any, - $routeParams: { - id?: string; - }, - getAppState: { - previouslyStored: () => TAppState | undefined; - }, - dashboardConfig: { - getHideWriteControls: () => boolean; - }, - localStorage: { - get: (prop: string) => unknown; - } - ) => - new DashboardAppController({ - $route, - $scope, - $routeParams, - getAppState, - dashboardConfig, - localStorage, - Private, - kbnUrl, - AppStateClass: AppState, - indexPatterns, - config, - confirmModal, - }), - }; -}); + return { + restrict: 'E', + controllerAs: 'dashboardApp', + controller: ( + $scope: DashboardAppScope, + $route: any, + $routeParams: { + id?: string; + }, + getAppState: any, + globalState: any + ) => + new DashboardAppController({ + $route, + $scope, + $routeParams, + getAppState, + globalState, + kbnUrl, + AppStateClass: AppState, + config, + confirmModal, + ...deps, + }), + }; + }); +} diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx index 457d8972876ae6..16c0e4437c344c 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app_controller.tsx @@ -23,41 +23,23 @@ import React from 'react'; import angular from 'angular'; import { uniq } from 'lodash'; -import chrome from 'ui/chrome'; -import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; -import { toastNotifications } from 'ui/notify'; - -// @ts-ignore -import { ConfirmationButtonTypes } from 'ui/modals/confirm_modal'; -import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter'; - -import { docTitle } from 'ui/doc_title/doc_title'; - -import { showSaveModal, SaveResult } from 'ui/saved_objects/show_saved_object_save_modal'; - -import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; - -import { timefilter } from 'ui/timefilter'; - -import { getUnhashableStatesProvider } from 'ui/state_management/state_hashing/get_unhashable_states_provider'; +import { Subscription } from 'rxjs'; import { + subscribeWithScope, + ConfirmationButtonTypes, + showSaveModal, + SaveResult, + migrateLegacyQuery, + State, AppStateClass as TAppStateClass, - AppState as TAppState, -} from 'ui/state_management/app_state'; - -import { KbnUrl } from 'ui/url/kbn_url'; -import { IndexPattern } from 'ui/index_patterns'; -import { IPrivate } from 'ui/private'; -import { SavedQuery } from 'src/legacy/core_plugins/data/public'; -import { SaveOptions } from 'ui/saved_objects/saved_object'; -import { capabilities } from 'ui/capabilities'; -import { Subscription } from 'rxjs'; -import { npStart } from 'ui/new_platform'; -import { unhashUrl } from 'ui/state_management/state_hashing'; -import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; + KbnUrl, + SaveOptions, + SavedObjectFinder, + unhashUrl, +} from './legacy_imports'; +import { FilterStateManager, IndexPattern, SavedQuery } from '../../../data/public'; import { Query } from '../../../../../plugins/data/public'; -import { start as data } from '../../../data/public/legacy'; import { DashboardContainer, @@ -72,7 +54,6 @@ import { ViewMode, openAddPanelFlyout, } from '../../../embeddable_api/public/np_ready/public'; -import { start } from '../../../embeddable_api/public/np_ready/public/legacy'; import { DashboardAppState, NavAction, ConfirmModalFn, SavedDashboardPanel } from './types'; import { showOptionsPopover } from './top_nav/show_options_popover'; @@ -87,8 +68,23 @@ import { getDashboardTitle } from './dashboard_strings'; import { DashboardAppScope } from './dashboard_app'; import { VISUALIZE_EMBEDDABLE_TYPE } from '../visualize/embeddable'; import { convertSavedDashboardPanelToPanelState } from './lib/embeddable_saved_object_converters'; - -const { savedQueryService } = data.search.services; +import { RenderDeps } from './application'; + +export interface DashboardAppControllerDependencies extends RenderDeps { + $scope: DashboardAppScope; + $route: any; + $routeParams: any; + getAppState: any; + globalState: State; + indexPatterns: { + getDefault: () => Promise; + }; + dashboardConfig: any; + kbnUrl: KbnUrl; + AppStateClass: TAppStateClass; + config: any; + confirmModal: ConfirmModalFn; +} export class DashboardAppController { // Part of the exposed plugin API - do not remove without careful consideration. @@ -101,58 +97,55 @@ export class DashboardAppController { $route, $routeParams, getAppState, + globalState, dashboardConfig, localStorage, - Private, kbnUrl, AppStateClass, indexPatterns, config, confirmModal, - }: { - $scope: DashboardAppScope; - $route: any; - $routeParams: any; - getAppState: { - previouslyStored: () => TAppState | undefined; - }; - indexPatterns: { - getDefault: () => Promise; - }; - dashboardConfig: any; - localStorage: { - get: (prop: string) => unknown; - }; - Private: IPrivate; - kbnUrl: KbnUrl; - AppStateClass: TAppStateClass; - config: any; - confirmModal: ConfirmModalFn; - }) { - const queryFilter = Private(FilterBarQueryFilterProvider); - const getUnhashableStates = Private(getUnhashableStatesProvider); + savedQueryService, + embeddables, + share, + dashboardCapabilities, + npDataStart: { + query: { + filterManager, + timefilter: { timefilter }, + }, + }, + core: { notifications, overlays, chrome, injectedMetadata }, + }: DashboardAppControllerDependencies) { + new FilterStateManager(globalState, getAppState, filterManager); + const queryFilter = filterManager; + + function getUnhashableStates(): State[] { + return [getAppState(), globalState].filter(Boolean); + } let lastReloadRequestTime = 0; const dash = ($scope.dash = $route.current.locals.dash); if (dash.id) { - docTitle.change(dash.title); + chrome.docTitle.change(dash.title); } const dashboardStateManager = new DashboardStateManager({ savedDashboard: dash, AppStateClass, hideWriteControls: dashboardConfig.getHideWriteControls(), + kibanaVersion: injectedMetadata.getKibanaVersion(), }); $scope.appState = dashboardStateManager.getAppState(); - // The 'previouslyStored' check is so we only update the time filter on dashboard open, not during + // The hash check is so we only update the time filter on dashboard open, not during // normal cross app navigation. - if (dashboardStateManager.getIsTimeSavedWithDashboard() && !getAppState.previouslyStored()) { + if (dashboardStateManager.getIsTimeSavedWithDashboard() && !globalState.$inheritedGlobalState) { dashboardStateManager.syncTimefilterWithDashboard(timefilter); } - $scope.showSaveQuery = capabilities.get().dashboard.saveQuery as boolean; + $scope.showSaveQuery = dashboardCapabilities.saveQuery as boolean; const updateIndexPatterns = (container?: DashboardContainer) => { if (!container || isErrorEmbeddable(container)) { @@ -187,10 +180,7 @@ export class DashboardAppController { [key: string]: DashboardPanelState; } = {}; dashboardStateManager.getPanels().forEach((panel: SavedDashboardPanel) => { - embeddablesMap[panel.panelIndex] = convertSavedDashboardPanelToPanelState( - panel, - dashboardStateManager.getUseMargins() - ); + embeddablesMap[panel.panelIndex] = convertSavedDashboardPanelToPanelState(panel); }); let expandedPanelId; if (dashboardContainer && !isErrorEmbeddable(dashboardContainer)) { @@ -239,7 +229,7 @@ export class DashboardAppController { let outputSubscription: Subscription | undefined; const dashboardDom = document.getElementById('dashboardViewport'); - const dashboardFactory = start.getEmbeddableFactory( + const dashboardFactory = embeddables.getEmbeddableFactory( DASHBOARD_CONTAINER_TYPE ) as DashboardContainerFactory; dashboardFactory @@ -334,7 +324,7 @@ export class DashboardAppController { // Push breadcrumbs to new header navigation const updateBreadcrumbs = () => { - chrome.breadcrumbs.set([ + chrome.setBreadcrumbs([ { text: i18n.translate('kbn.dashboard.dashboardAppBreadcrumbsTitle', { defaultMessage: 'Dashboard', @@ -495,7 +485,7 @@ export class DashboardAppController { }); $scope.$watch( - () => capabilities.get().dashboard.saveQuery, + () => dashboardCapabilities.saveQuery, newCapability => { $scope.showSaveQuery = newCapability as boolean; } @@ -595,7 +585,7 @@ export class DashboardAppController { return saveDashboard(angular.toJson, timefilter, dashboardStateManager, saveOptions) .then(function(id) { if (id) { - toastNotifications.addSuccess({ + notifications.toasts.addSuccess({ title: i18n.translate('kbn.dashboard.dashboardWasSavedSuccessMessage', { defaultMessage: `Dashboard '{dashTitle}' was saved`, values: { dashTitle: dash.title }, @@ -606,14 +596,14 @@ export class DashboardAppController { if (dash.id !== $routeParams.id) { kbnUrl.change(createDashboardEditUrl(dash.id)); } else { - docTitle.change(dash.lastSavedTitle); + chrome.docTitle.change(dash.lastSavedTitle); updateViewMode(ViewMode.VIEW); } } return { id }; }) .catch(error => { - toastNotifications.addDanger({ + notifications.toasts.addDanger({ title: i18n.translate('kbn.dashboard.dashboardWasNotSavedDangerMessage', { defaultMessage: `Dashboard '{dashTitle}' was not saved. Error: {errorMessage}`, values: { @@ -734,10 +724,10 @@ export class DashboardAppController { if (dashboardContainer && !isErrorEmbeddable(dashboardContainer)) { openAddPanelFlyout({ embeddable: dashboardContainer, - getAllFactories: start.getEmbeddableFactories, - getFactory: start.getEmbeddableFactory, - notifications: npStart.core.notifications, - overlays: npStart.core.overlays, + getAllFactories: embeddables.getEmbeddableFactories, + getFactory: embeddables.getEmbeddableFactory, + notifications, + overlays, SavedObjectFinder, }); } @@ -757,7 +747,7 @@ export class DashboardAppController { }); }; navActions[TopNavIds.SHARE] = anchorElement => { - npStart.plugins.share.toggleShareContextMenu({ + share.toggleShareContextMenu({ anchorElement, allowEmbed: true, allowShortUrl: !dashboardConfig.getHideWriteControls(), @@ -784,8 +774,15 @@ export class DashboardAppController { }, }); + const visibleSubscription = chrome.getIsVisible$().subscribe(isVisible => { + $scope.$evalAsync(() => { + $scope.isVisible = isVisible; + }); + }); + $scope.$on('$destroy', () => { updateSubscription.unsubscribe(); + visibleSubscription.unsubscribe(); $scope.timefilterSubscriptions$.unsubscribe(); dashboardStateManager.destroy(); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state.test.ts index 5e81373001bf57..c236ac7843c038 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state.test.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state.test.ts @@ -21,16 +21,13 @@ import './np_core.test.mocks'; import { DashboardStateManager } from './dashboard_state_manager'; import { getAppStateMock, getSavedDashboardMock } from './__tests__'; -import { AppStateClass } from 'ui/state_management/app_state'; +import { AppStateClass } from './legacy_imports'; import { DashboardAppState } from './types'; -import { TimeRange, TimefilterContract } from 'src/plugins/data/public'; +import { TimeRange, TimefilterContract, InputTimeRange } from 'src/plugins/data/public'; import { ViewMode } from 'src/plugins/embeddable/public'; -import { InputTimeRange } from 'ui/timefilter'; -jest.mock('ui/registry/field_formats', () => ({ - fieldFormats: { - getDefaultInstance: jest.fn(), - }, +jest.mock('ui/state_management/state', () => ({ + State: {}, })); describe('DashboardState', function() { @@ -52,6 +49,7 @@ describe('DashboardState', function() { savedDashboard, AppStateClass: getAppStateMock() as AppStateClass, hideWriteControls: false, + kibanaVersion: '7.0.0', }); } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts index d5af4c93d0e0cc..ac8628ec2a9d94 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts @@ -20,15 +20,21 @@ import { i18n } from '@kbn/i18n'; import _ from 'lodash'; -import { stateMonitorFactory, StateMonitor } from 'ui/state_management/state_monitor_factory'; -import { Timefilter } from 'ui/timefilter'; -import { AppStateClass as TAppStateClass } from 'ui/state_management/app_state'; -import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; import { Moment } from 'moment'; import { DashboardContainer } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public'; import { ViewMode } from '../../../../../../src/plugins/embeddable/public'; -import { Query, esFilters } from '../../../../../../src/plugins/data/public'; +import { + stateMonitorFactory, + StateMonitor, + AppStateClass as TAppStateClass, + migrateLegacyQuery, +} from './legacy_imports'; +import { + Query, + esFilters, + TimefilterContract as Timefilter, +} from '../../../../../../src/plugins/data/public'; import { getAppStateDefaults, migrateAppState } from './lib'; import { convertPanelStateToSavedDashboardPanel } from './lib/embeddable_saved_object_converters'; @@ -54,6 +60,7 @@ export class DashboardStateManager { }; private stateDefaults: DashboardAppStateDefaults; private hideWriteControls: boolean; + private kibanaVersion: string; public isDirty: boolean; private changeListeners: Array<(status: { dirty: boolean }) => void>; private stateMonitor: StateMonitor; @@ -68,11 +75,14 @@ export class DashboardStateManager { savedDashboard, AppStateClass, hideWriteControls, + kibanaVersion, }: { savedDashboard: SavedObjectDashboard; AppStateClass: TAppStateClass; hideWriteControls: boolean; + kibanaVersion: string; }) { + this.kibanaVersion = kibanaVersion; this.savedDashboard = savedDashboard; this.hideWriteControls = hideWriteControls; @@ -84,7 +94,7 @@ export class DashboardStateManager { // appState based on the URL (the url trumps the defaults). This means if we update the state format at all and // want to handle BWC, we must not only migrate the data stored with saved Dashboard, but also any old state in the // url. - migrateAppState(this.appState); + migrateAppState(this.appState, kibanaVersion); this.isDirty = false; @@ -146,7 +156,8 @@ export class DashboardStateManager { } convertedPanelStateMap[panelState.explicitInput.id] = convertPanelStateToSavedDashboardPanel( - panelState + panelState, + this.kibanaVersion ); if ( diff --git a/src/legacy/core_plugins/kibana/public/dashboard/global_state_sync.ts b/src/legacy/core_plugins/kibana/public/dashboard/global_state_sync.ts new file mode 100644 index 00000000000000..8a733f940734b9 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/dashboard/global_state_sync.ts @@ -0,0 +1,67 @@ +/* + * 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 { State } from './legacy_imports'; +import { DataPublicPluginStart as NpDataStart } from '../../../../../plugins/data/public'; + +/** + * Helper function to sync the global state with the various state providers + * when a local angular application mounts. There are three different ways + * global state can be passed into the application: + * * parameter in the URL hash - e.g. shared link + * * in-memory state in the data plugin exports (timefilter and filterManager) - e.g. default values + * + * This function looks up the three sources (earlier in the list means it takes precedence), + * puts it into the globalState object and syncs it with the url. + * + * Currently the legacy chrome takes care of restoring the global state when navigating from + * one app to another - to migrate away from that it will become necessary to also write the current + * state to local storage + */ +export function syncOnMount( + globalState: State, + { + query: { + filterManager, + timefilter: { timefilter }, + }, + }: NpDataStart +) { + // pull in global state information from the URL + globalState.fetch(); + // remember whether there were info in the URL + const hasGlobalURLState = Boolean(Object.keys(globalState.toObject()).length); + + // sync kibana platform state with the angular global state + if (!globalState.time) { + globalState.time = timefilter.getTime(); + } + if (!globalState.refreshInterval) { + globalState.refreshInterval = timefilter.getRefreshInterval(); + } + if (!globalState.filters && filterManager.getGlobalFilters().length > 0) { + globalState.filters = filterManager.getGlobalFilters(); + } + // only inject cross app global state if there is none in the url itself (that takes precedence) + if (hasGlobalURLState) { + // set flag the global state is set from the URL + globalState.$inheritedGlobalState = true; + } + globalState.save(); +} diff --git a/src/legacy/core_plugins/kibana/public/dashboard/help_menu/help_menu_util.js b/src/legacy/core_plugins/kibana/public/dashboard/help_menu/help_menu_util.js index aeabff2d97007b..55abfa179b56d8 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/help_menu/help_menu_util.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/help_menu/help_menu_util.js @@ -17,15 +17,19 @@ * under the License. */ -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { HelpMenu } from './help_menu'; +import { i18n } from '@kbn/i18n'; +import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; export function addHelpMenuToAppChrome(chrome) { - chrome.helpExtension.set(domElement => { - render(, domElement); - return () => { - unmountComponentAtNode(domElement); - }; + chrome.setHelpExtension({ + appName: i18n.translate('kbn.dashboard.helpMenu.appName', { + defaultMessage: 'Dashboards', + }), + links: [ + { + linkType: 'documentation', + href: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/dashboard.html`, + }, + ], }); } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/index.js b/src/legacy/core_plugins/kibana/public/dashboard/index.js deleted file mode 100644 index 712e05c92e5e8b..00000000000000 --- a/src/legacy/core_plugins/kibana/public/dashboard/index.js +++ /dev/null @@ -1,207 +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 './dashboard_app'; -import { i18n } from '@kbn/i18n'; -import './saved_dashboard/saved_dashboards'; -import './dashboard_config'; -import uiRoutes from 'ui/routes'; -import chrome from 'ui/chrome'; -import { wrapInI18nContext } from 'ui/i18n'; -import { toastNotifications } from 'ui/notify'; - -import dashboardTemplate from './dashboard_app.html'; -import dashboardListingTemplate from './listing/dashboard_listing_ng_wrapper.html'; - -import { DashboardConstants, createDashboardEditUrl } from './dashboard_constants'; -import { InvalidJSONProperty, SavedObjectNotFound } from '../../../../../plugins/kibana_utils/public'; -import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue'; -import { SavedObjectsClientProvider } from 'ui/saved_objects'; -import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; -import { DashboardListing, EMPTY_FILTER } from './listing/dashboard_listing'; -import { uiModules } from 'ui/modules'; -import 'ui/capabilities/route_setup'; -import { addHelpMenuToAppChrome } from './help_menu/help_menu_util'; - -import { npStart } from 'ui/new_platform'; - -// load directives -import '../../../data/public'; - -const app = uiModules.get('app/dashboard', [ - 'ngRoute', - 'react', -]); - -app.directive('dashboardListing', function (reactDirective) { - return reactDirective(wrapInI18nContext(DashboardListing)); -}); - -function createNewDashboardCtrl($scope) { - $scope.visitVisualizeAppLinkText = i18n.translate('kbn.dashboard.visitVisualizeAppLinkText', { - defaultMessage: 'visit the Visualize app', - }); - addHelpMenuToAppChrome(chrome); -} - -uiRoutes - .defaults(/dashboard/, { - requireDefaultIndex: true, - requireUICapability: 'dashboard.show', - badge: uiCapabilities => { - if (uiCapabilities.dashboard.showWriteControls) { - return undefined; - } - - return { - text: i18n.translate('kbn.dashboard.badge.readOnly.text', { - defaultMessage: 'Read only', - }), - tooltip: i18n.translate('kbn.dashboard.badge.readOnly.tooltip', { - defaultMessage: 'Unable to save dashboards', - }), - iconType: 'glasses' - }; - } - }) - .when(DashboardConstants.LANDING_PAGE_PATH, { - template: dashboardListingTemplate, - controller($injector, $location, $scope, Private, config) { - const services = Private(SavedObjectRegistryProvider).byLoaderPropertiesName; - const kbnUrl = $injector.get('kbnUrl'); - const dashboardConfig = $injector.get('dashboardConfig'); - - $scope.listingLimit = config.get('savedObjects:listingLimit'); - $scope.create = () => { - kbnUrl.redirect(DashboardConstants.CREATE_NEW_DASHBOARD_URL); - }; - $scope.find = (search) => { - return services.dashboards.find(search, $scope.listingLimit); - }; - $scope.editItem = ({ id }) => { - kbnUrl.redirect(`${createDashboardEditUrl(id)}?_a=(viewMode:edit)`); - }; - $scope.getViewUrl = ({ id }) => { - return chrome.addBasePath(`#${createDashboardEditUrl(id)}`); - }; - $scope.delete = (dashboards) => { - return services.dashboards.delete(dashboards.map(d => d.id)); - }; - $scope.hideWriteControls = dashboardConfig.getHideWriteControls(); - $scope.initialFilter = ($location.search()).filter || EMPTY_FILTER; - chrome.breadcrumbs.set([{ - text: i18n.translate('kbn.dashboard.dashboardBreadcrumbsTitle', { - defaultMessage: 'Dashboards', - }), - }]); - addHelpMenuToAppChrome(chrome); - }, - resolve: { - dash: function ($route, Private, redirectWhenMissing, kbnUrl) { - const savedObjectsClient = Private(SavedObjectsClientProvider); - const title = $route.current.params.title; - if (title) { - return savedObjectsClient.find({ - search: `"${title}"`, - search_fields: 'title', - type: 'dashboard', - }).then(results => { - // The search isn't an exact match, lets see if we can find a single exact match to use - const matchingDashboards = results.savedObjects.filter( - dashboard => dashboard.attributes.title.toLowerCase() === title.toLowerCase()); - if (matchingDashboards.length === 1) { - kbnUrl.redirect(createDashboardEditUrl(matchingDashboards[0].id)); - } else { - kbnUrl.redirect(`${DashboardConstants.LANDING_PAGE_PATH}?filter="${title}"`); - } - throw uiRoutes.WAIT_FOR_URL_CHANGE_TOKEN; - }).catch(redirectWhenMissing({ - 'dashboard': DashboardConstants.LANDING_PAGE_PATH - })); - } - } - } - }) - .when(DashboardConstants.CREATE_NEW_DASHBOARD_URL, { - template: dashboardTemplate, - controller: createNewDashboardCtrl, - requireUICapability: 'dashboard.createNew', - resolve: { - dash: function (savedDashboards, redirectWhenMissing) { - return savedDashboards.get() - .catch(redirectWhenMissing({ - 'dashboard': DashboardConstants.LANDING_PAGE_PATH - })); - } - } - }) - .when(createDashboardEditUrl(':id'), { - template: dashboardTemplate, - controller: createNewDashboardCtrl, - resolve: { - dash: function (savedDashboards, $route, redirectWhenMissing, kbnUrl, AppState) { - const id = $route.current.params.id; - - return savedDashboards.get(id) - .then((savedDashboard) => { - npStart.core.chrome.recentlyAccessed.add(savedDashboard.getFullPath(), savedDashboard.title, id); - return savedDashboard; - }) - .catch((error) => { - // A corrupt dashboard was detected (e.g. with invalid JSON properties) - if (error instanceof InvalidJSONProperty) { - toastNotifications.addDanger(error.message); - kbnUrl.redirect(DashboardConstants.LANDING_PAGE_PATH); - return; - } - - // Preserve BWC of v5.3.0 links for new, unsaved dashboards. - // See https://github.com/elastic/kibana/issues/10951 for more context. - if (error instanceof SavedObjectNotFound && id === 'create') { - // Note "new AppState" is necessary so the state in the url is preserved through the redirect. - kbnUrl.redirect(DashboardConstants.CREATE_NEW_DASHBOARD_URL, {}, new AppState()); - toastNotifications.addWarning(i18n.translate('kbn.dashboard.urlWasRemovedInSixZeroWarningMessage', - { defaultMessage: 'The url "dashboard/create" was removed in 6.0. Please update your bookmarks.' } - )); - } else { - throw error; - } - }) - .catch(redirectWhenMissing({ - 'dashboard': DashboardConstants.LANDING_PAGE_PATH - })); - } - } - }); - -FeatureCatalogueRegistryProvider.register(() => { - return { - id: 'dashboard', - title: i18n.translate('kbn.dashboard.featureCatalogue.dashboardTitle', { - defaultMessage: 'Dashboard', - }), - description: i18n.translate('kbn.dashboard.featureCatalogue.dashboardDescription', { - defaultMessage: 'Display and share a collection of visualizations and saved searches.', - }), - icon: 'dashboardApp', - path: `/app/kibana#${DashboardConstants.LANDING_PAGE_PATH}`, - showOnHomePage: true, - category: FeatureCatalogueCategory.DATA - }; -}); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/index.ts b/src/legacy/core_plugins/kibana/public/dashboard/index.ts new file mode 100644 index 00000000000000..d134739aa24c26 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/dashboard/index.ts @@ -0,0 +1,69 @@ +/* + * 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 { + npSetup, + npStart, + SavedObjectRegistryProvider, + legacyChrome, + IPrivate, +} from './legacy_imports'; +import { DashboardPlugin, LegacyAngularInjectedDependencies } from './plugin'; +import { start as data } from '../../../data/public/legacy'; +import { localApplicationService } from '../local_application_service'; +import { start as embeddables } from '../../../embeddable_api/public/np_ready/public/legacy'; +import { start as navigation } from '../../../navigation/public/legacy'; +import './saved_dashboard/saved_dashboards'; +import './dashboard_config'; + +/** + * Get dependencies relying on the global angular context. + * They also have to get resolved together with the legacy imports above + */ +async function getAngularDependencies(): Promise { + const injector = await legacyChrome.dangerouslyGetActiveInjector(); + + const Private = injector.get('Private'); + + const savedObjectRegistry = Private(SavedObjectRegistryProvider); + + return { + dashboardConfig: injector.get('dashboardConfig'), + savedObjectRegistry, + savedDashboards: injector.get('savedDashboards'), + }; +} + +(async () => { + const instance = new DashboardPlugin(); + instance.setup(npSetup.core, { + ...npSetup.plugins, + __LEGACY: { + localApplicationService, + getAngularDependencies, + }, + }); + instance.start(npStart.core, { + ...npStart.plugins, + data, + npData: npStart.plugins.data, + embeddables, + navigation, + }); +})(); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy_app.js b/src/legacy/core_plugins/kibana/public/dashboard/legacy_app.js new file mode 100644 index 00000000000000..c7f2adb4b875b9 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_app.js @@ -0,0 +1,224 @@ +/* + * 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 dashboardTemplate from './dashboard_app.html'; +import dashboardListingTemplate from './listing/dashboard_listing_ng_wrapper.html'; + +import { ensureDefaultIndexPattern } from './legacy_imports'; +import { initDashboardAppDirective } from './dashboard_app'; +import { DashboardConstants, createDashboardEditUrl } from './dashboard_constants'; +import { + InvalidJSONProperty, + SavedObjectNotFound, +} from '../../../../../plugins/kibana_utils/public'; +import { DashboardListing, EMPTY_FILTER } from './listing/dashboard_listing'; +import { addHelpMenuToAppChrome } from './help_menu/help_menu_util'; +import { registerTimefilterWithGlobalStateFactory } from '../../../../ui/public/timefilter/setup_router'; +import { syncOnMount } from './global_state_sync'; + +export function initDashboardApp(app, deps) { + initDashboardAppDirective(app, deps); + + app.directive('dashboardListing', function (reactDirective) { + return reactDirective(DashboardListing); + }); + + function createNewDashboardCtrl($scope) { + $scope.visitVisualizeAppLinkText = i18n.translate('kbn.dashboard.visitVisualizeAppLinkText', { + defaultMessage: 'visit the Visualize app', + }); + addHelpMenuToAppChrome(deps.chrome, deps.core.docLinks); + } + + app.run(globalState => { + syncOnMount(globalState, deps.npDataStart); + }); + + app.run((globalState, $rootScope) => { + registerTimefilterWithGlobalStateFactory( + deps.npDataStart.query.timefilter.timefilter, + globalState, + $rootScope + ); + }); + + app.config(function ($routeProvider) { + const defaults = { + reloadOnSearch: false, + requireUICapability: 'dashboard.show', + badge: () => { + if (deps.dashboardCapabilities.showWriteControls) { + return undefined; + } + + return { + text: i18n.translate('kbn.dashboard.badge.readOnly.text', { + defaultMessage: 'Read only', + }), + tooltip: i18n.translate('kbn.dashboard.badge.readOnly.tooltip', { + defaultMessage: 'Unable to save dashboards', + }), + iconType: 'glasses', + }; + }, + }; + + $routeProvider + .when(DashboardConstants.LANDING_PAGE_PATH, { + ...defaults, + template: dashboardListingTemplate, + controller($injector, $location, $scope) { + const services = deps.savedObjectRegistry.byLoaderPropertiesName; + const kbnUrl = $injector.get('kbnUrl'); + const dashboardConfig = deps.dashboardConfig; + + $scope.listingLimit = deps.uiSettings.get('savedObjects:listingLimit'); + $scope.create = () => { + kbnUrl.redirect(DashboardConstants.CREATE_NEW_DASHBOARD_URL); + }; + $scope.find = search => { + return services.dashboards.find(search, $scope.listingLimit); + }; + $scope.editItem = ({ id }) => { + kbnUrl.redirect(`${createDashboardEditUrl(id)}?_a=(viewMode:edit)`); + }; + $scope.getViewUrl = ({ id }) => { + return deps.addBasePath(`#${createDashboardEditUrl(id)}`); + }; + $scope.delete = dashboards => { + return services.dashboards.delete(dashboards.map(d => d.id)); + }; + $scope.hideWriteControls = dashboardConfig.getHideWriteControls(); + $scope.initialFilter = $location.search().filter || EMPTY_FILTER; + deps.chrome.setBreadcrumbs([ + { + text: i18n.translate('kbn.dashboard.dashboardBreadcrumbsTitle', { + defaultMessage: 'Dashboards', + }), + }, + ]); + addHelpMenuToAppChrome(deps.chrome, deps.core.docLinks); + }, + resolve: { + dash: function ($rootScope, $route, redirectWhenMissing, kbnUrl) { + return ensureDefaultIndexPattern(deps.core, deps.dataStart, $rootScope, kbnUrl).then(() => { + const savedObjectsClient = deps.savedObjectsClient; + const title = $route.current.params.title; + if (title) { + return savedObjectsClient + .find({ + search: `"${title}"`, + search_fields: 'title', + type: 'dashboard', + }) + .then(results => { + // The search isn't an exact match, lets see if we can find a single exact match to use + const matchingDashboards = results.savedObjects.filter( + dashboard => dashboard.attributes.title.toLowerCase() === title.toLowerCase() + ); + if (matchingDashboards.length === 1) { + kbnUrl.redirect(createDashboardEditUrl(matchingDashboards[0].id)); + } else { + kbnUrl.redirect(`${DashboardConstants.LANDING_PAGE_PATH}?filter="${title}"`); + } + $rootScope.$digest(); + return new Promise(() => {}); + }); + } + }); + }, + }, + }) + .when(DashboardConstants.CREATE_NEW_DASHBOARD_URL, { + ...defaults, + template: dashboardTemplate, + controller: createNewDashboardCtrl, + requireUICapability: 'dashboard.createNew', + resolve: { + dash: function (redirectWhenMissing, $rootScope, kbnUrl) { + return ensureDefaultIndexPattern(deps.core, deps.dataStart, $rootScope, kbnUrl) + .then(() => { + return deps.savedDashboards.get(); + }) + .catch( + redirectWhenMissing({ + dashboard: DashboardConstants.LANDING_PAGE_PATH, + }) + ); + }, + }, + }) + .when(createDashboardEditUrl(':id'), { + ...defaults, + template: dashboardTemplate, + controller: createNewDashboardCtrl, + resolve: { + dash: function ($rootScope, $route, redirectWhenMissing, kbnUrl, AppState) { + const id = $route.current.params.id; + + return ensureDefaultIndexPattern(deps.core, deps.dataStart, $rootScope, kbnUrl) + .then(() => { + return deps.savedDashboards.get(id); + }) + .then(savedDashboard => { + deps.chrome.recentlyAccessed.add( + savedDashboard.getFullPath(), + savedDashboard.title, + id + ); + return savedDashboard; + }) + .catch(error => { + // A corrupt dashboard was detected (e.g. with invalid JSON properties) + if (error instanceof InvalidJSONProperty) { + deps.toastNotifications.addDanger(error.message); + kbnUrl.redirect(DashboardConstants.LANDING_PAGE_PATH); + return; + } + + // Preserve BWC of v5.3.0 links for new, unsaved dashboards. + // See https://github.com/elastic/kibana/issues/10951 for more context. + if (error instanceof SavedObjectNotFound && id === 'create') { + // Note "new AppState" is necessary so the state in the url is preserved through the redirect. + kbnUrl.redirect(DashboardConstants.CREATE_NEW_DASHBOARD_URL, {}, new AppState()); + deps.toastNotifications.addWarning( + i18n.translate('kbn.dashboard.urlWasRemovedInSixZeroWarningMessage', { + defaultMessage: + 'The url "dashboard/create" was removed in 6.0. Please update your bookmarks.', + }) + ); + return new Promise(() => {}); + } else { + throw error; + } + }) + .catch( + redirectWhenMissing({ + dashboard: DashboardConstants.LANDING_PAGE_PATH, + }) + ); + }, + }, + }) + .when(`dashboard/:tail*?`, { redirectTo: `/${deps.core.injectedMetadata.getInjectedVar('kbnDefaultAppId')}` }) + .when(`dashboards/:tail*?`, { redirectTo: `/${deps.core.injectedMetadata.getInjectedVar('kbnDefaultAppId')}` }); + }); +} diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts new file mode 100644 index 00000000000000..7c3c389330887e --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * The imports in this file are static functions and types which still live in legacy folders and are used + * within dashboard. To consolidate them all in one place, they are re-exported from this file. Eventually + * this list should become empty. Imports from the top level of shimmed or moved plugins can be imported + * directly where they are needed. + */ + +import chrome from 'ui/chrome'; + +export const legacyChrome = chrome; +export { State } from 'ui/state_management/state'; +export { AppState } from 'ui/state_management/app_state'; +export { AppStateClass } from 'ui/state_management/app_state'; +export { SaveOptions } from 'ui/saved_objects/saved_object'; +export { npSetup, npStart } from 'ui/new_platform'; +export { SavedObjectRegistryProvider } from 'ui/saved_objects'; +export { IPrivate } from 'ui/private'; +export { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal'; +export { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; +// @ts-ignore +export { ConfirmationButtonTypes } from 'ui/modals/confirm_modal'; +export { showSaveModal, SaveResult } from 'ui/saved_objects/show_saved_object_save_modal'; +export { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; +export { KbnUrl } from 'ui/url/kbn_url'; +// @ts-ignore +export { GlobalStateProvider } from 'ui/state_management/global_state'; +// @ts-ignore +export { StateManagementConfigProvider } from 'ui/state_management/config_provider'; +// @ts-ignore +export { AppStateProvider } from 'ui/state_management/app_state'; +// @ts-ignore +export { PrivateProvider } from 'ui/private/private'; +// @ts-ignore +export { EventsProvider } from 'ui/events'; +export { PersistedState } from 'ui/persisted_state'; +// @ts-ignore +export { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav'; +// @ts-ignore +export { PromiseServiceCreator } from 'ui/promises/promises'; +// @ts-ignore +export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url'; +// @ts-ignore +export { confirmModalFactory } from 'ui/modals/confirm_modal'; +export { configureAppAngularModule } from 'ui/legacy_compat'; +export { stateMonitorFactory, StateMonitor } from 'ui/state_management/state_monitor_factory'; +export { ensureDefaultIndexPattern } from 'ui/legacy_compat'; +export { unhashUrl } from 'ui/state_management/state_hashing'; +export { IInjector } from 'ui/chrome'; +export { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/lib/embeddable_saved_object_converters.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/lib/embeddable_saved_object_converters.test.ts index 99bb6b115b985d..3f04cad4f322b6 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/lib/embeddable_saved_object_converters.test.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/lib/embeddable_saved_object_converters.test.ts @@ -48,7 +48,7 @@ test('convertSavedDashboardPanelToPanelState', () => { version: '7.0.0', }; - expect(convertSavedDashboardPanelToPanelState(savedDashboardPanel, true)).toEqual({ + expect(convertSavedDashboardPanelToPanelState(savedDashboardPanel)).toEqual({ gridData: { x: 0, y: 0, @@ -82,7 +82,7 @@ test('convertSavedDashboardPanelToPanelState does not include undefined id', () version: '7.0.0', }; - const converted = convertSavedDashboardPanelToPanelState(savedDashboardPanel, false); + const converted = convertSavedDashboardPanelToPanelState(savedDashboardPanel); expect(converted.hasOwnProperty('savedObjectId')).toBe(false); }); @@ -103,7 +103,7 @@ test('convertPanelStateToSavedDashboardPanel', () => { type: 'search', }; - expect(convertPanelStateToSavedDashboardPanel(dashboardPanel)).toEqual({ + expect(convertPanelStateToSavedDashboardPanel(dashboardPanel, '6.3.0')).toEqual({ type: 'search', embeddableConfig: { something: 'hi!', @@ -137,6 +137,6 @@ test('convertPanelStateToSavedDashboardPanel will not add an undefined id when n type: 'search', }; - const converted = convertPanelStateToSavedDashboardPanel(dashboardPanel); + const converted = convertPanelStateToSavedDashboardPanel(dashboardPanel, '8.0.0'); expect(converted.hasOwnProperty('id')).toBe(false); }); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/lib/embeddable_saved_object_converters.ts b/src/legacy/core_plugins/kibana/public/dashboard/lib/embeddable_saved_object_converters.ts index 4a3bc3b2281069..2d42609e1e25fe 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/lib/embeddable_saved_object_converters.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/lib/embeddable_saved_object_converters.ts @@ -18,12 +18,10 @@ */ import { omit } from 'lodash'; import { DashboardPanelState } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public'; -import chrome from 'ui/chrome'; import { SavedDashboardPanel } from '../types'; export function convertSavedDashboardPanelToPanelState( - savedDashboardPanel: SavedDashboardPanel, - useMargins: boolean + savedDashboardPanel: SavedDashboardPanel ): DashboardPanelState { return { type: savedDashboardPanel.type, @@ -38,13 +36,14 @@ export function convertSavedDashboardPanelToPanelState( } export function convertPanelStateToSavedDashboardPanel( - panelState: DashboardPanelState + panelState: DashboardPanelState, + version: string ): SavedDashboardPanel { const customTitle: string | undefined = panelState.explicitInput.title ? (panelState.explicitInput.title as string) : undefined; return { - version: chrome.getKibanaVersion(), + version, type: panelState.type, gridData: panelState.gridData, panelIndex: panelState.explicitInput.id, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/lib/migrate_app_state.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/lib/migrate_app_state.test.ts index 10c27226300a58..4aa2461bb65930 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/lib/migrate_app_state.test.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/lib/migrate_app_state.test.ts @@ -43,7 +43,7 @@ test('migrate app state from 6.0', async () => { getQueryParamName: () => 'a', save: mockSave, }; - migrateAppState(appState); + migrateAppState(appState, '8.0'); expect(appState.uiState).toBeUndefined(); const newPanel = (appState.panels[0] as unknown) as SavedDashboardPanel; @@ -58,6 +58,7 @@ test('migrate app state from 6.0', async () => { }); test('migrate sort from 6.1', async () => { + const TARGET_VERSION = '8.0'; const mockSave = jest.fn(); const appState = { uiState: { @@ -80,7 +81,7 @@ test('migrate sort from 6.1', async () => { save: mockSave, useMargins: false, }; - migrateAppState(appState); + migrateAppState(appState, TARGET_VERSION); expect(appState.uiState).toBeUndefined(); const newPanel = (appState.panels[0] as unknown) as SavedDashboardPanel; @@ -112,7 +113,7 @@ test('migrates 6.0 even when uiState does not exist', async () => { getQueryParamName: () => 'a', save: mockSave, }; - migrateAppState(appState); + migrateAppState(appState, '8.0'); expect((appState as any).uiState).toBeUndefined(); const newPanel = (appState.panels[0] as unknown) as SavedDashboardPanel; @@ -147,7 +148,7 @@ test('6.2 migration adjusts w & h without margins', async () => { save: mockSave, useMargins: false, }; - migrateAppState(appState); + migrateAppState(appState, '8.0'); expect((appState as any).uiState).toBeUndefined(); const newPanel = (appState.panels[0] as unknown) as SavedDashboardPanel; @@ -184,7 +185,7 @@ test('6.2 migration adjusts w & h with margins', async () => { save: mockSave, useMargins: true, }; - migrateAppState(appState); + migrateAppState(appState, '8.0'); expect((appState as any).uiState).toBeUndefined(); const newPanel = (appState.panels[0] as unknown) as SavedDashboardPanel; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/lib/migrate_app_state.ts b/src/legacy/core_plugins/kibana/public/dashboard/lib/migrate_app_state.ts index 9bd93029f06d88..c4ad754548459d 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/lib/migrate_app_state.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/lib/migrate_app_state.ts @@ -18,7 +18,6 @@ */ import semver from 'semver'; -import chrome from 'ui/chrome'; import { i18n } from '@kbn/i18n'; import { createUiStatsReporter, METRIC_TYPE } from '../../../../ui_metric/public'; import { @@ -37,7 +36,10 @@ import { migratePanelsTo730 } from '../migrations/migrate_to_730_panels'; * * Once we hit a major version, we can remove support for older style URLs and get rid of this logic. */ -export function migrateAppState(appState: { [key: string]: unknown } | DashboardAppState) { +export function migrateAppState( + appState: { [key: string]: unknown } | DashboardAppState, + kibanaVersion: string +) { if (!appState.panels) { throw new Error( i18n.translate('kbn.dashboard.panel.invalidData', { @@ -73,7 +75,7 @@ export function migrateAppState(appState: { [key: string]: unknown } | Dashboard | SavedDashboardPanel630 | SavedDashboardPanel640To720 >, - chrome.getKibanaVersion(), + kibanaVersion, appState.useMargins, appState.uiState ); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/lib/save_dashboard.ts b/src/legacy/core_plugins/kibana/public/dashboard/lib/save_dashboard.ts index 168f320b5ea7ec..e0d82373d3ad9d 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/lib/save_dashboard.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/lib/save_dashboard.ts @@ -17,8 +17,8 @@ * under the License. */ -import { SaveOptions } from 'ui/saved_objects/saved_object'; -import { Timefilter } from 'ui/timefilter'; +import { TimefilterContract } from 'src/plugins/data/public'; +import { SaveOptions } from '../legacy_imports'; import { updateSavedDashboard } from './update_saved_dashboard'; import { DashboardStateManager } from '../dashboard_state_manager'; @@ -32,7 +32,7 @@ import { DashboardStateManager } from '../dashboard_state_manager'; */ export function saveDashboard( toJson: (obj: any) => string, - timeFilter: Timefilter, + timeFilter: TimefilterContract, dashboardStateManager: DashboardStateManager, saveOptions: SaveOptions ): Promise { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/lib/update_saved_dashboard.ts b/src/legacy/core_plugins/kibana/public/dashboard/lib/update_saved_dashboard.ts index 707b5a0f5f5f57..ce9096b3a56f0c 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/lib/update_saved_dashboard.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/lib/update_saved_dashboard.ts @@ -18,16 +18,15 @@ */ import _ from 'lodash'; -import { AppState } from 'ui/state_management/app_state'; -import { Timefilter } from 'ui/timefilter'; -import { RefreshInterval } from 'src/plugins/data/public'; +import { RefreshInterval, TimefilterContract } from 'src/plugins/data/public'; +import { AppState } from '../legacy_imports'; import { FilterUtils } from './filter_utils'; import { SavedObjectDashboard } from '../saved_dashboard/saved_dashboard'; export function updateSavedDashboard( savedDashboard: SavedObjectDashboard, appState: AppState, - timeFilter: Timefilter, + timeFilter: TimefilterContract, toJson: (object: T) => string ) { savedDashboard.title = appState.title; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/listing/__snapshots__/dashboard_listing.test.js.snap b/src/legacy/core_plugins/kibana/public/dashboard/listing/__snapshots__/dashboard_listing.test.js.snap index 1ed05035f5f4ce..b2f004568841a3 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/listing/__snapshots__/dashboard_listing.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/dashboard/listing/__snapshots__/dashboard_listing.test.js.snap @@ -1,533 +1,545 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`after fetch hideWriteControls 1`] = ` - - - - - } - /> -
- } - tableColumns={ - Array [ - Object { - "field": "title", - "name": "Title", - "render": [Function], - "sortable": true, - }, + + + + + + } + /> +
+ } + tableColumns={ + Array [ + Object { + "field": "title", + "name": "Title", + "render": [Function], + "sortable": true, + }, + Object { + "dataType": "string", + "field": "description", + "name": "Description", + "sortable": true, + }, + ] + } + tableListTitle="Dashboards" + toastNotifications={Object {}} + uiSettings={ Object { - "dataType": "string", - "field": "description", - "name": "Description", - "sortable": true, - }, - ] - } - tableListTitle="Dashboards" - toastNotifications={Object {}} - uiSettings={ - Object { - "get": [MockFunction], + "get": [MockFunction], + } } - } -/> + /> + `; exports[`after fetch initialFilter 1`] = ` - - - - - } - body={ - -

+ + + -

-

- - - , + + } + body={ + +

+ +

+

+ + + , + } } - } + /> +

+
+ } + iconType="dashboardApp" + title={ +

+ -

- - } - iconType="dashboardApp" - title={ -

- -

- } - /> -

- } - tableColumns={ - Array [ - Object { - "field": "title", - "name": "Title", - "render": [Function], - "sortable": true, - }, + + } + /> + + } + tableColumns={ + Array [ + Object { + "field": "title", + "name": "Title", + "render": [Function], + "sortable": true, + }, + Object { + "dataType": "string", + "field": "description", + "name": "Description", + "sortable": true, + }, + ] + } + tableListTitle="Dashboards" + toastNotifications={Object {}} + uiSettings={ Object { - "dataType": "string", - "field": "description", - "name": "Description", - "sortable": true, - }, - ] - } - tableListTitle="Dashboards" - toastNotifications={Object {}} - uiSettings={ - Object { - "get": [MockFunction], + "get": [MockFunction], + } } - } -/> + /> + `; exports[`after fetch renders call to action when no dashboards exist 1`] = ` - - - - - } - body={ - -

+ + + -

-

- - - , + + } + body={ + +

+ +

+

+ + + , + } } - } + /> +

+
+ } + iconType="dashboardApp" + title={ +

+ -

- - } - iconType="dashboardApp" - title={ -

- -

- } - /> - - } - tableColumns={ - Array [ - Object { - "field": "title", - "name": "Title", - "render": [Function], - "sortable": true, - }, +

+ } + /> + + } + tableColumns={ + Array [ + Object { + "field": "title", + "name": "Title", + "render": [Function], + "sortable": true, + }, + Object { + "dataType": "string", + "field": "description", + "name": "Description", + "sortable": true, + }, + ] + } + tableListTitle="Dashboards" + toastNotifications={Object {}} + uiSettings={ Object { - "dataType": "string", - "field": "description", - "name": "Description", - "sortable": true, - }, - ] - } - tableListTitle="Dashboards" - toastNotifications={Object {}} - uiSettings={ - Object { - "get": [MockFunction], + "get": [MockFunction], + } } - } -/> + /> + `; exports[`after fetch renders table rows 1`] = ` - - - - - } - body={ - -

+ + + -

-

- - - , + + } + body={ + +

+ +

+

+ + + , + } } - } + /> +

+
+ } + iconType="dashboardApp" + title={ +

+ -

- - } - iconType="dashboardApp" - title={ -

- -

- } - /> - - } - tableColumns={ - Array [ - Object { - "field": "title", - "name": "Title", - "render": [Function], - "sortable": true, - }, +

+ } + /> + + } + tableColumns={ + Array [ + Object { + "field": "title", + "name": "Title", + "render": [Function], + "sortable": true, + }, + Object { + "dataType": "string", + "field": "description", + "name": "Description", + "sortable": true, + }, + ] + } + tableListTitle="Dashboards" + toastNotifications={Object {}} + uiSettings={ Object { - "dataType": "string", - "field": "description", - "name": "Description", - "sortable": true, - }, - ] - } - tableListTitle="Dashboards" - toastNotifications={Object {}} - uiSettings={ - Object { - "get": [MockFunction], + "get": [MockFunction], + } } - } -/> + /> + `; exports[`after fetch renders warning when listingLimit is exceeded 1`] = ` - - - - - } - body={ - -

+ + + -

-

- - - , + + } + body={ + +

+ +

+

+ + + , + } } - } + /> +

+
+ } + iconType="dashboardApp" + title={ +

+ -

- - } - iconType="dashboardApp" - title={ -

- -

- } - /> - - } - tableColumns={ - Array [ - Object { - "field": "title", - "name": "Title", - "render": [Function], - "sortable": true, - }, +

+ } + /> + + } + tableColumns={ + Array [ + Object { + "field": "title", + "name": "Title", + "render": [Function], + "sortable": true, + }, + Object { + "dataType": "string", + "field": "description", + "name": "Description", + "sortable": true, + }, + ] + } + tableListTitle="Dashboards" + toastNotifications={Object {}} + uiSettings={ Object { - "dataType": "string", - "field": "description", - "name": "Description", - "sortable": true, - }, - ] - } - tableListTitle="Dashboards" - toastNotifications={Object {}} - uiSettings={ - Object { - "get": [MockFunction], + "get": [MockFunction], + } } - } -/> + /> + `; exports[`renders empty page in before initial fetch to avoid flickering 1`] = ` - - - - - } - body={ - -

+ + + -

-

- - - , + + } + body={ + +

+ +

+

+ + + , + } } - } + /> +

+
+ } + iconType="dashboardApp" + title={ +

+ -

- - } - iconType="dashboardApp" - title={ -

- -

- } - /> - - } - tableColumns={ - Array [ - Object { - "field": "title", - "name": "Title", - "render": [Function], - "sortable": true, - }, +

+ } + /> + + } + tableColumns={ + Array [ + Object { + "field": "title", + "name": "Title", + "render": [Function], + "sortable": true, + }, + Object { + "dataType": "string", + "field": "description", + "name": "Description", + "sortable": true, + }, + ] + } + tableListTitle="Dashboards" + toastNotifications={Object {}} + uiSettings={ Object { - "dataType": "string", - "field": "description", - "name": "Description", - "sortable": true, - }, - ] - } - tableListTitle="Dashboards" - toastNotifications={Object {}} - uiSettings={ - Object { - "get": [MockFunction], + "get": [MockFunction], + } } - } -/> + /> + `; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/listing/dashboard_listing.js b/src/legacy/core_plugins/kibana/public/dashboard/listing/dashboard_listing.js index c222fcd3c928ce..98581223afa461 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/listing/dashboard_listing.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/listing/dashboard_listing.js @@ -19,7 +19,7 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { EuiLink, EuiButton, EuiEmptyPrompt } from '@elastic/eui'; @@ -41,27 +41,29 @@ export class DashboardListing extends React.Component { render() { return ( - + + + ); } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts new file mode 100644 index 00000000000000..780fa6571e4e79 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts @@ -0,0 +1,151 @@ +/* + * 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 { + App, + CoreSetup, + CoreStart, + LegacyCoreStart, + Plugin, + SavedObjectsClientContract, +} from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { RenderDeps } from './application'; +import { LocalApplicationService } from '../local_application_service'; +import { DataStart } from '../../../data/public'; +import { DataPublicPluginStart as NpDataStart } from '../../../../../plugins/data/public'; +import { EmbeddablePublicPlugin } from '../../../../../plugins/embeddable/public'; +import { Storage } from '../../../../../plugins/kibana_utils/public'; +import { NavigationStart } from '../../../navigation/public'; +import { DashboardConstants } from './dashboard_constants'; +import { + FeatureCatalogueCategory, + HomePublicPluginSetup, +} from '../../../../../plugins/home/public'; +import { SharePluginStart } from '../../../../../plugins/share/public'; + +export interface LegacyAngularInjectedDependencies { + dashboardConfig: any; + savedObjectRegistry: any; + savedDashboards: any; +} + +export interface DashboardPluginStartDependencies { + data: DataStart; + npData: NpDataStart; + embeddables: ReturnType; + navigation: NavigationStart; + share: SharePluginStart; +} + +export interface DashboardPluginSetupDependencies { + __LEGACY: { + getAngularDependencies: () => Promise; + localApplicationService: LocalApplicationService; + }; + home: HomePublicPluginSetup; +} + +export class DashboardPlugin implements Plugin { + private startDependencies: { + dataStart: DataStart; + npDataStart: NpDataStart; + savedObjectsClient: SavedObjectsClientContract; + embeddables: ReturnType; + navigation: NavigationStart; + share: SharePluginStart; + } | null = null; + + public setup( + core: CoreSetup, + { + __LEGACY: { localApplicationService, getAngularDependencies, ...legacyServices }, + home, + }: DashboardPluginSetupDependencies + ) { + const app: App = { + id: '', + title: 'Dashboards', + mount: async ({ core: contextCore }, params) => { + if (this.startDependencies === null) { + throw new Error('not started yet'); + } + const { + dataStart, + savedObjectsClient, + embeddables, + navigation, + share, + npDataStart, + } = this.startDependencies; + const angularDependencies = await getAngularDependencies(); + const deps: RenderDeps = { + core: contextCore as LegacyCoreStart, + ...legacyServices, + ...angularDependencies, + navigation, + dataStart, + share, + npDataStart, + indexPatterns: dataStart.indexPatterns.indexPatterns, + savedObjectsClient, + chrome: contextCore.chrome, + addBasePath: contextCore.http.basePath.prepend, + uiSettings: contextCore.uiSettings, + savedQueryService: dataStart.search.services.savedQueryService, + embeddables, + dashboardCapabilities: contextCore.application.capabilities.dashboard, + localStorage: new Storage(localStorage), + }; + const { renderApp } = await import('./application'); + return renderApp(params.element, params.appBasePath, deps); + }, + }; + localApplicationService.register({ ...app, id: 'dashboard' }); + localApplicationService.register({ ...app, id: 'dashboards' }); + + home.featureCatalogue.register({ + id: 'dashboard', + title: i18n.translate('kbn.dashboard.featureCatalogue.dashboardTitle', { + defaultMessage: 'Dashboard', + }), + description: i18n.translate('kbn.dashboard.featureCatalogue.dashboardDescription', { + defaultMessage: 'Display and share a collection of visualizations and saved searches.', + }), + icon: 'dashboardApp', + path: `/app/kibana#${DashboardConstants.LANDING_PAGE_PATH}`, + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA, + }); + } + + start( + { savedObjects: { client: savedObjectsClient } }: CoreStart, + { data: dataStart, embeddables, navigation, npData, share }: DashboardPluginStartDependencies + ) { + this.startDependencies = { + dataStart, + npDataStart: npData, + savedObjectsClient, + embeddables, + navigation, + share, + }; + } +} diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/save_modal.test.js b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/save_modal.test.js index 153a049276ceee..aa7e219d759632 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/save_modal.test.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/save_modal.test.js @@ -17,9 +17,16 @@ * under the License. */ + import React from 'react'; import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; +jest.mock('../legacy_imports', () => ({ + SavedObjectSaveModal: () => null +})); + +jest.mock('ui/new_platform'); + import { DashboardSaveModal } from './save_modal'; test('renders DashboardSaveModal', () => { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/save_modal.tsx b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/save_modal.tsx index 47455f04ba8091..0640b2be431be9 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/save_modal.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/save_modal.tsx @@ -19,10 +19,10 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; - -import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal'; import { EuiFormRow, EuiTextArea, EuiSwitch } from '@elastic/eui'; +import { SavedObjectSaveModal } from '../legacy_imports'; + interface SaveOptions { newTitle: string; newDescription: string; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_clone_modal.tsx b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_clone_modal.tsx index c3cd5621b2c888..af1020e01e0c52 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_clone_modal.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_clone_modal.tsx @@ -17,10 +17,10 @@ * under the License. */ -import { I18nContext } from 'ui/i18n'; import React from 'react'; import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; +import { I18nProvider } from '@kbn/i18n/react'; import { DashboardCloneModal } from './clone_modal'; export function showCloneModal( @@ -54,7 +54,7 @@ export function showCloneModal( }; document.body.appendChild(container); const element = ( - + - + ); ReactDOM.render(element, container); } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_options_popover.tsx b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_options_popover.tsx index 8640d7dbc6bdca..7c23e4808fbea3 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_options_popover.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/show_options_popover.tsx @@ -19,9 +19,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { I18nContext } from 'ui/i18n'; - +import { I18nProvider } from '@kbn/i18n/react'; import { EuiWrappingPopover } from '@elastic/eui'; + import { OptionsMenu } from './options'; let isOpen = false; @@ -55,7 +55,7 @@ export function showOptionsPopover({ document.body.appendChild(container); const element = ( - + - + ); ReactDOM.render(element, container); } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/types.ts b/src/legacy/core_plugins/kibana/public/dashboard/types.ts index 3c2c87a502da46..371274401739e0 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/types.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/types.ts @@ -17,9 +17,8 @@ * under the License. */ -import { AppState } from 'ui/state_management/app_state'; -import { AppState as TAppState } from 'ui/state_management/app_state'; import { ViewMode } from 'src/plugins/embeddable/public'; +import { AppState } from './legacy_imports'; import { RawSavedDashboardPanelTo60, RawSavedDashboardPanel610, @@ -153,5 +152,5 @@ export type AddFilterFn = ( operator: string; index: string; }, - appState: TAppState + appState: AppState ) => void; diff --git a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js index a5b55e50eb90e9..bac56f008233c0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js +++ b/src/legacy/core_plugins/kibana/public/discover/__tests__/directives/field_chooser.js @@ -241,7 +241,6 @@ describe('discover field chooser directives', function () { $scope.computeDetails(field); expect(field.details.buckets).to.not.be(undefined); expect(field.details.buckets[0].value).to.be(40.141592); - expect(field.details.buckets[0].display).to.be('40.142'); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/angular/context_app.html b/src/legacy/core_plugins/kibana/public/discover/angular/context_app.html index 68e1d536a91ce2..3e0f8a83291542 100644 --- a/src/legacy/core_plugins/kibana/public/discover/angular/context_app.html +++ b/src/legacy/core_plugins/kibana/public/discover/angular/context_app.html @@ -1,9 +1,16 @@ - +> + +
$injector.invoke( @@ -119,50 +119,53 @@ uiRoutes template: indexTemplate, reloadOnSearch: false, resolve: { - ip: function (Promise, indexPatterns, config, Private) { + savedObjects: function (Promise, indexPatterns, config, Private, $rootScope, kbnUrl, redirectWhenMissing, savedSearches, $route) { const State = Private(StateProvider); - return indexPatterns.getCache().then((savedObjects)=> { - /** - * In making the indexPattern modifiable it was placed in appState. Unfortunately, - * the load order of AppState conflicts with the load order of many other things - * so in order to get the name of the index we should use, and to switch to the - * default if necessary, we parse the appState with a temporary State object and - * then destroy it immediatly after we're done - * - * @type {State} - */ - const state = new State('_a', {}); - - const specified = !!state.index; - const exists = _.findIndex(savedObjects, o => o.id === state.index) > -1; - const id = exists ? state.index : config.get('defaultIndex'); - state.destroy(); + const savedSearchId = $route.current.params.id; + return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl).then(() => { return Promise.props({ - list: savedObjects, - loaded: indexPatterns.get(id), - stateVal: state.index, - stateValFound: specified && exists + ip: indexPatterns.getCache().then((savedObjects) => { + /** + * In making the indexPattern modifiable it was placed in appState. Unfortunately, + * the load order of AppState conflicts with the load order of many other things + * so in order to get the name of the index we should use, and to switch to the + * default if necessary, we parse the appState with a temporary State object and + * then destroy it immediatly after we're done + * + * @type {State} + */ + const state = new State('_a', {}); + + const specified = !!state.index; + const exists = _.findIndex(savedObjects, o => o.id === state.index) > -1; + const id = exists ? state.index : config.get('defaultIndex'); + state.destroy(); + + return Promise.props({ + list: savedObjects, + loaded: indexPatterns.get(id), + stateVal: state.index, + stateValFound: specified && exists + }); + }), + savedSearch: savedSearches.get(savedSearchId) + .then((savedSearch) => { + if (savedSearchId) { + chrome.recentlyAccessed.add( + savedSearch.getFullPath(), + savedSearch.title, + savedSearchId); + } + return savedSearch; + }) + .catch(redirectWhenMissing({ + 'search': '/discover', + 'index-pattern': '/management/kibana/objects/savedSearches/' + $route.current.params.id + })) }); }); }, - savedSearch: function (redirectWhenMissing, savedSearches, $route) { - const savedSearchId = $route.current.params.id; - return savedSearches.get(savedSearchId) - .then((savedSearch) => { - if (savedSearchId) { - chrome.recentlyAccessed.add( - savedSearch.getFullPath(), - savedSearch.title, - savedSearchId); - } - return savedSearch; - }) - .catch(redirectWhenMissing({ - 'search': '/discover', - 'index-pattern': '/management/kibana/objects/savedSearches/' + $route.current.params.id - })); - } } }); @@ -224,7 +227,7 @@ function discoverController( }; // the saved savedSearch - const savedSearch = $route.current.locals.savedSearch; + const savedSearch = $route.current.locals.savedObjects.savedSearch; let abortController; $scope.$on('$destroy', () => { @@ -417,20 +420,6 @@ function discoverController( queryFilter.setFilters(filters); }; - $scope.applyFilters = filters => { - const { timeRangeFilter, restOfFilters } = extractTimeFilter($scope.indexPattern.timeFieldName, filters); - queryFilter.addFilters(restOfFilters); - if (timeRangeFilter) changeTimeFilter(timefilter, timeRangeFilter); - - $scope.state.$newFilters = []; - }; - - $scope.$watch('state.$newFilters', (filters = []) => { - if (filters.length === 1) { - $scope.applyFilters(filters); - } - }); - const getFieldCounts = async () => { // the field counts aren't set until we have the data back, // so we wait for the fetch to be done before proceeding @@ -539,7 +528,7 @@ function discoverController( sampleSize: config.get('discover:sampleSize'), timefield: isDefaultTypeIndexPattern($scope.indexPattern) && $scope.indexPattern.timeFieldName, savedSearch: savedSearch, - indexPatternList: $route.current.locals.ip.list, + indexPatternList: $route.current.locals.savedObjects.ip.list, }; const shouldSearchOnPageLoad = () => { @@ -1055,7 +1044,7 @@ function discoverController( loaded: loadedIndexPattern, stateVal, stateValFound, - } = $route.current.locals.ip; + } = $route.current.locals.savedObjects.ip; const ownIndexPattern = $scope.searchSource.getOwnField('index'); @@ -1103,12 +1092,12 @@ function discoverController( // Block the UI from loading if the user has loaded a rollup index pattern but it isn't // supported. $scope.isUnsupportedIndexPattern = ( - !isDefaultTypeIndexPattern($route.current.locals.ip.loaded) - && !hasSearchStategyForIndexPattern($route.current.locals.ip.loaded) + !isDefaultTypeIndexPattern($route.current.locals.savedObjects.ip.loaded) + && !hasSearchStategyForIndexPattern($route.current.locals.savedObjects.ip.loaded) ); if ($scope.isUnsupportedIndexPattern) { - $scope.unsupportedIndexPatternType = $route.current.locals.ip.loaded.type; + $scope.unsupportedIndexPatternType = $route.current.locals.savedObjects.ip.loaded.type; return; } diff --git a/src/legacy/core_plugins/kibana/public/discover/breadcrumbs.ts b/src/legacy/core_plugins/kibana/public/discover/breadcrumbs.ts index 51e0dcba1cad0b..6c3856932c96c1 100644 --- a/src/legacy/core_plugins/kibana/public/discover/breadcrumbs.ts +++ b/src/legacy/core_plugins/kibana/public/discover/breadcrumbs.ts @@ -34,7 +34,7 @@ export function getSavedSearchBreadcrumbs($route: any) { return [ ...getRootBreadcrumbs(), { - text: $route.current.locals.savedSearch.id, + text: $route.current.locals.savedObjects.savedSearch.id, }, ]; } diff --git a/src/legacy/core_plugins/kibana/public/discover/components/help_menu/help_menu.js b/src/legacy/core_plugins/kibana/public/discover/components/help_menu/help_menu.js deleted file mode 100644 index ad68e55e71622c..00000000000000 --- a/src/legacy/core_plugins/kibana/public/discover/components/help_menu/help_menu.js +++ /dev/null @@ -1,43 +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, { Fragment, PureComponent } from 'react'; -import { EuiButton, EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { getServices } from '../../kibana_services'; -const { docLinks } = getServices(); - -export class HelpMenu extends PureComponent { - render() { - return ( - - - - - - - - ); - } -} diff --git a/src/legacy/core_plugins/kibana/public/discover/components/help_menu/help_menu_util.js b/src/legacy/core_plugins/kibana/public/discover/components/help_menu/help_menu_util.js index 58a92193de63e8..eb40130137e007 100644 --- a/src/legacy/core_plugins/kibana/public/discover/components/help_menu/help_menu_util.js +++ b/src/legacy/core_plugins/kibana/public/discover/components/help_menu/help_menu_util.js @@ -17,15 +17,20 @@ * under the License. */ -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { HelpMenu } from './help_menu'; +import { i18n } from '@kbn/i18n'; +import { getServices } from '../../kibana_services'; +const { docLinks } = getServices(); export function addHelpMenuToAppChrome(chrome) { - chrome.setHelpExtension(domElement => { - render(, domElement); - return () => { - unmountComponentAtNode(domElement); - }; + chrome.setHelpExtension({ + appName: i18n.translate('kbn.discover.helpMenu.appName', { + defaultMessage: 'Discover', + }), + links: [ + { + linkType: 'documentation', + href: `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/discover.html`, + }, + ], }); } diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index 61d7933464e7f4..02b08d7fa4b612 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -46,6 +46,7 @@ import * as docViewsRegistry from 'ui/registry/doc_views'; const services = { // new plattform + core: npStart.core, addBasePath: npStart.core.http.basePath.prepend, capabilities: npStart.core.application.capabilities, chrome: npStart.core.chrome, @@ -108,6 +109,7 @@ export { getUnhashableStatesProvider } from 'ui/state_management/state_hashing'; export { tabifyAggResponse } from 'ui/agg_response/tabify'; // @ts-ignore export { vislibSeriesResponseHandlerProvider } from 'ui/vis/response_handlers/vislib'; +export { ensureDefaultIndexPattern } from 'ui/legacy_compat'; export { unhashUrl } from 'ui/state_management/state_hashing'; // EXPORT types diff --git a/src/legacy/core_plugins/kibana/public/field_formats/__tests__/_conformance.js b/src/legacy/core_plugins/kibana/public/field_formats/__tests__/_conformance.js deleted file mode 100644 index 1c63d2efc7e0bc..00000000000000 --- a/src/legacy/core_plugins/kibana/public/field_formats/__tests__/_conformance.js +++ /dev/null @@ -1,142 +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 _ from 'lodash'; -import expect from '@kbn/expect'; -import { fieldFormats } from 'ui/registry/field_formats'; -import { npStart } from 'ui/new_platform'; -import { FieldFormat } from '../../../../../../plugins/data/public'; - -const config = npStart.core.uiSettings; - -const formatIds = [ - 'bytes', - 'date', - 'date_nanos', - 'duration', - 'ip', - 'number', - 'percent', - 'color', - 'string', - 'url', - '_source', - 'truncate', - 'boolean', - 'relative_date', - 'static_lookup' -]; - -// eslint-disable-next-line import/no-default-export -export default describe('conformance', function () { - - const getConfig = (...args) => config.get(...args); - - formatIds.forEach(function (id) { - let instance; - let Type; - - beforeEach(function () { - Type = fieldFormats.getType(id); - instance = fieldFormats.getInstance(id); - }); - - describe(id + ' Type', function () { - it('has an id', function () { - expect(Type.id).to.be.a('string'); - }); - - it('has a title', function () { - expect(Type.title).to.be.a('string'); - }); - - it('declares compatible field formats as a string or array', function () { - expect(Type.fieldType).to.be.ok(); - expect(_.isString(Type.fieldType) || Array.isArray(Type.fieldType)).to.be(true); - }); - }); - - describe(id + ' Instance', function () { - it('extends FieldFormat', function () { - expect(instance).to.be.a(FieldFormat); - }); - }); - }); - - it('registers all of the fieldFormats', function () { - expect(_.difference(fieldFormats.raw, formatIds.map(fieldFormats.getType))).to.eql([]); - }); - - describe('Bytes format', basicPatternTests('bytes', require('numeral'))); - describe('Percent Format', basicPatternTests('percent', require('numeral'))); - describe('Date Format', basicPatternTests('date', require('moment'))); - - describe('Number Format', function () { - basicPatternTests('number', require('numeral'))(); - - it('tries to parse strings', function () { - const number = new (fieldFormats.getType('number'))({ pattern: '0.0b' }, getConfig); - expect(number.convert(123.456)).to.be('123.5B'); - expect(number.convert('123.456')).to.be('123.5B'); - }); - - }); - - function basicPatternTests(id, lib) { - const confKey = id === 'date' ? 'dateFormat' : 'format:' + id + ':defaultPattern'; - - return function () { - it('converts using the format:' + id + ':defaultPattern config', function () { - const inst = fieldFormats.getInstance(id); - [ - '0b', - '0 b', - '0.[000] b', - '0.[000]b', - '0.[0]b' - ].forEach(function (pattern) { - const original = config.get(confKey); - const num = _.random(-10000, 10000, true); - config.set(confKey, pattern); - expect(inst.convert(num)).to.be(lib(num).format(pattern)); - config.set(confKey, original); - }); - }); - - it('uses the pattern param if available', function () { - const original = config.get(confKey); - const num = _.random(-10000, 10000, true); - const defFormat = '0b'; - const customFormat = '0.00000%'; - - config.set(confKey, defFormat); - const defInst = fieldFormats.getInstance(id); - - const Type = fieldFormats.getType(id); - const customInst = new Type({ pattern: customFormat }, getConfig); - - expect(defInst.convert(num)).to.not.be(customInst.convert(num)); - expect(defInst.convert(num)).to.be(lib(num).format(defFormat)); - expect(customInst.convert(num)).to.be(lib(num).format(customFormat)); - - config.set(confKey, original); - }); - }; - } -}); diff --git a/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/add_data.test.js.snap b/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/add_data.test.js.snap index bbcb2096b6f64a..35a7216df8bc6c 100644 --- a/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/add_data.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/add_data.test.js.snap @@ -67,10 +67,7 @@ exports[`apmUiEnabled 1`] = ` type="logoAPM" /> } - layout="vertical" - textAlign="center" title="APM" - titleElement="span" /> } - layout="vertical" - textAlign="center" title="Logs" - titleElement="span" /> } - layout="vertical" - textAlign="center" title="Metrics" - titleElement="span" /> } - layout="vertical" - textAlign="center" title="SIEM" - titleElement="span" /> @@ -332,10 +320,7 @@ exports[`isNewKibanaInstance 1`] = ` type="logoLogging" /> } - layout="vertical" - textAlign="center" title="Logs" - titleElement="span" /> } - layout="vertical" - textAlign="center" title="Metrics" - titleElement="span" /> } - layout="vertical" - textAlign="center" title="SIEM" - titleElement="span" /> @@ -560,10 +539,7 @@ exports[`mlEnabled 1`] = ` type="logoAPM" /> } - layout="vertical" - textAlign="center" title="APM" - titleElement="span" /> } - layout="vertical" - textAlign="center" title="Logs" - titleElement="span" /> } - layout="vertical" - textAlign="center" title="Metrics" - titleElement="span" /> } - layout="vertical" - textAlign="center" title="SIEM" - titleElement="span" /> @@ -861,10 +828,7 @@ exports[`render 1`] = ` type="logoLogging" /> } - layout="vertical" - textAlign="center" title="Logs" - titleElement="span" /> } - layout="vertical" - textAlign="center" title="Metrics" - titleElement="span" /> } - layout="vertical" - textAlign="center" title="SIEM" - titleElement="span" /> diff --git a/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/synopsis.test.js.snap b/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/synopsis.test.js.snap index 1970d048be4fd7..34cc0eb9265ffa 100644 --- a/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/synopsis.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/home/components/__snapshots__/synopsis.test.js.snap @@ -14,9 +14,7 @@ exports[`props iconType 1`] = ` /> } layout="horizontal" - textAlign="center" title="Great tutorial" - titleElement="span" /> `; @@ -35,9 +33,7 @@ exports[`props iconUrl 1`] = ` /> } layout="horizontal" - textAlign="center" title="Great tutorial" - titleElement="span" /> `; @@ -49,9 +45,7 @@ exports[`props isBeta 1`] = ` description="this is a great tutorial about..." href="link_to_item" layout="horizontal" - textAlign="center" title="Great tutorial" - titleElement="span" /> `; @@ -63,8 +57,6 @@ exports[`render 1`] = ` description="this is a great tutorial about..." href="link_to_item" layout="horizontal" - textAlign="center" title="Great tutorial" - titleElement="span" /> `; diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js index 14fc2ec6ead000..98def2252b75c7 100644 --- a/src/legacy/core_plugins/kibana/public/kibana.js +++ b/src/legacy/core_plugins/kibana/public/kibana.js @@ -31,7 +31,6 @@ import 'uiExports/visTypes'; import 'uiExports/visEditorTypes'; import 'uiExports/visualize'; import 'uiExports/savedObjectTypes'; -import 'uiExports/fieldFormats'; import 'uiExports/fieldFormatEditors'; import 'uiExports/navbarExtensions'; import 'uiExports/contextMenuActions'; diff --git a/src/legacy/core_plugins/kibana/public/management/index.js b/src/legacy/core_plugins/kibana/public/management/index.js index c0949318e92530..83fc8e4db9b558 100644 --- a/src/legacy/core_plugins/kibana/public/management/index.js +++ b/src/legacy/core_plugins/kibana/public/management/index.js @@ -28,7 +28,6 @@ import { I18nContext } from 'ui/i18n'; import { uiModules } from 'ui/modules'; import appTemplate from './app.html'; import landingTemplate from './landing.html'; -import { capabilities } from 'ui/capabilities'; import { management, SidebarNav, MANAGEMENT_BREADCRUMB } from 'ui/management'; import { FeatureCatalogueRegistryProvider, FeatureCatalogueCategory } from 'ui/registry/feature_catalogue'; import { timefilter } from 'ui/timefilter'; @@ -50,13 +49,6 @@ uiRoutes redirectTo: '/management' }); -require('./route_setup/load_default')({ - whenMissingRedirectTo: () => { - const canManageIndexPatterns = capabilities.get().management.kibana.index_patterns; - return canManageIndexPatterns ? '/management/kibana/index_pattern' : '/home'; - } -}); - export function updateLandingPage(version) { const node = document.getElementById(LANDING_ID); if (!node) { diff --git a/src/legacy/core_plugins/kibana/public/management/route_setup/load_default.js b/src/legacy/core_plugins/kibana/public/management/route_setup/load_default.js deleted file mode 100644 index f797acbe8888e7..00000000000000 --- a/src/legacy/core_plugins/kibana/public/management/route_setup/load_default.js +++ /dev/null @@ -1,110 +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 _ from 'lodash'; -import React from 'react'; -import { banners } from 'ui/notify'; -import { NoDefaultIndexPattern } from 'ui/index_patterns'; -import uiRoutes from 'ui/routes'; -import { - EuiCallOut, -} from '@elastic/eui'; -import { clearTimeout } from 'timers'; -import { i18n } from '@kbn/i18n'; - -let bannerId; -let timeoutId; - -function displayBanner() { - clearTimeout(timeoutId); - - // Avoid being hostile to new users who don't have an index pattern setup yet - // give them a friendly info message instead of a terse error message - bannerId = banners.set({ - id: bannerId, // initially undefined, but reused after first set - component: ( - - ) - }); - - // hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around - timeoutId = setTimeout(() => { - banners.remove(bannerId); - timeoutId = undefined; - }, 15000); -} - -// eslint-disable-next-line import/no-default-export -export default function (opts) { - opts = opts || {}; - const whenMissingRedirectTo = opts.whenMissingRedirectTo || null; - - uiRoutes - .addSetupWork(function loadDefaultIndexPattern(Promise, $route, config, indexPatterns) { - const route = _.get($route, 'current.$$route'); - - if (!route.requireDefaultIndex) { - return; - } - - return indexPatterns.getIds() - .then(function (patterns) { - let defaultId = config.get('defaultIndex'); - let defined = !!defaultId; - const exists = _.contains(patterns, defaultId); - - if (defined && !exists) { - config.remove('defaultIndex'); - defaultId = defined = false; - } - - if (!defined) { - // If there is any index pattern created, set the first as default - if (patterns.length >= 1) { - defaultId = patterns[0]; - config.set('defaultIndex', defaultId); - } else { - throw new NoDefaultIndexPattern(); - } - } - }); - }) - .afterWork( - // success - null, - - // failure - function (err, kbnUrl) { - const hasDefault = !(err instanceof NoDefaultIndexPattern); - if (hasDefault || !whenMissingRedirectTo) throw err; // rethrow - - kbnUrl.change(whenMissingRedirectTo()); - - displayBanner(); - } - ); -} diff --git a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html index 0ef3cce832bc7b..bf9ac9b9bbe361 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html +++ b/src/legacy/core_plugins/kibana/public/visualize/editor/editor.html @@ -1,7 +1,7 @@
@@ -33,6 +33,7 @@ show-query-bar is set to "true". -->
savedVisualizations.get($route.current.params)) .then(savedVis => { if (savedVis.vis.type.setup) { return savedVis.vis.type.setup(savedVis) @@ -102,28 +104,33 @@ uiRoutes template: editorTemplate, k7Breadcrumbs: getEditBreadcrumbs, resolve: { - savedVis: function (savedVisualizations, redirectWhenMissing, $route) { - return savedVisualizations.get($route.current.params.id) + savedVis: function (savedVisualizations, redirectWhenMissing, $route, $rootScope, kbnUrl) { + return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl) + .then(() => savedVisualizations.get($route.current.params.id)) .then((savedVis) => { chrome.recentlyAccessed.add( savedVis.getFullPath(), savedVis.title, - savedVis.id); + savedVis.id + ); return savedVis; }) .then(savedVis => { if (savedVis.vis.type.setup) { - return savedVis.vis.type.setup(savedVis) - .catch(() => savedVis); + return savedVis.vis.type.setup(savedVis).catch(() => savedVis); } return savedVis; }) - .catch(redirectWhenMissing({ - 'visualization': '/visualize', - 'search': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, - 'index-pattern': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, - 'index-pattern-field': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id - })); + .catch( + redirectWhenMissing({ + visualization: '/visualize', + search: '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, + 'index-pattern': + '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, + 'index-pattern-field': + '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, + }) + ); } } }); @@ -418,6 +425,12 @@ function VisEditor( next: updateTimeRange })); + subscriptions.add(chrome.getIsVisible$().subscribe(isVisible => { + $scope.$evalAsync(() => { + $scope.isVisible = isVisible; + }); + })); + // update the searchSource when query updates $scope.fetch = function () { $state.save(); diff --git a/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu.js b/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu.js deleted file mode 100644 index 40a1b79ea35203..00000000000000 --- a/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu.js +++ /dev/null @@ -1,45 +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, { Fragment, PureComponent } from 'react'; -import { EuiButton, EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { getServices } from '../kibana_services'; - -const { docLinks } = getServices(); - -export class HelpMenu extends PureComponent { - render() { - return ( - - - - - - - - ); - } -} diff --git a/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu_util.js b/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu_util.js index 58a92193de63e8..d27003f39d4c08 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu_util.js +++ b/src/legacy/core_plugins/kibana/public/visualize/help_menu/help_menu_util.js @@ -17,15 +17,20 @@ * under the License. */ -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { HelpMenu } from './help_menu'; +import { i18n } from '@kbn/i18n'; +import { getServices } from '../kibana_services'; +const { docLinks } = getServices(); export function addHelpMenuToAppChrome(chrome) { - chrome.setHelpExtension(domElement => { - render(, domElement); - return () => { - unmountComponentAtNode(domElement); - }; + chrome.setHelpExtension({ + appName: i18n.translate('kbn.visualize.helpMenu.appName', { + defaultMessage: 'Visualize', + }), + links: [ + { + linkType: 'documentation', + href: `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/visualize.html`, + }, + ], }); } diff --git a/src/legacy/core_plugins/kibana/public/visualize/index.js b/src/legacy/core_plugins/kibana/public/visualize/index.js index 592a355a71b0d7..57707f63213768 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/index.js +++ b/src/legacy/core_plugins/kibana/public/visualize/index.js @@ -17,6 +17,7 @@ * under the License. */ +import { ensureDefaultIndexPattern } from 'ui/legacy_compat'; import './editor/editor'; import { i18n } from '@kbn/i18n'; import './saved_visualizations/_saved_vis'; @@ -32,7 +33,6 @@ const { FeatureCatalogueRegistryProvider, uiRoutes } = getServices(); uiRoutes .defaults(/visualize/, { - requireDefaultIndex: true, requireUICapability: 'visualize.show', badge: uiCapabilities => { if (uiCapabilities.visualize.save) { @@ -57,6 +57,7 @@ uiRoutes controllerAs: 'listingController', resolve: { createNewVis: () => false, + hasDefaultIndex: ($rootScope, kbnUrl) => ensureDefaultIndexPattern(getServices().core, getServices().data, $rootScope, kbnUrl) }, }) .when(VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, { @@ -66,6 +67,7 @@ uiRoutes controllerAs: 'listingController', resolve: { createNewVis: () => true, + hasDefaultIndex: ($rootScope, kbnUrl) => ensureDefaultIndexPattern(getServices().core, getServices().data, $rootScope, kbnUrl) }, }); diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts index 3be49971cf4c94..e2201cdca9a576 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts @@ -60,6 +60,7 @@ const services = { savedObjectsClient: npStart.core.savedObjects.client, toastNotifications: npStart.core.notifications.toasts, uiSettings: npStart.core.uiSettings, + core: npStart.core, share: npStart.plugins.share, data, diff --git a/src/legacy/core_plugins/kibana/server/tutorials/apm/index_pattern.json b/src/legacy/core_plugins/kibana/server/tutorials/apm/index_pattern.json index f54c2fa35f80d0..69a165c09c2f91 100644 --- a/src/legacy/core_plugins/kibana/server/tutorials/apm/index_pattern.json +++ b/src/legacy/core_plugins/kibana/server/tutorials/apm/index_pattern.json @@ -1,11 +1,11 @@ { "attributes": { - "fieldFormatMap": "{\"client.bytes\":{\"id\":\"bytes\"},\"client.nat.port\":{\"id\":\"string\"},\"client.port\":{\"id\":\"string\"},\"destination.bytes\":{\"id\":\"bytes\"},\"destination.nat.port\":{\"id\":\"string\"},\"destination.port\":{\"id\":\"string\"},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"},\"http.request.body.bytes\":{\"id\":\"bytes\"},\"http.request.bytes\":{\"id\":\"bytes\"},\"http.response.body.bytes\":{\"id\":\"bytes\"},\"http.response.bytes\":{\"id\":\"bytes\"},\"http.response.status_code\":{\"id\":\"string\"},\"network.bytes\":{\"id\":\"bytes\"},\"process.pgid\":{\"id\":\"string\"},\"process.pid\":{\"id\":\"string\"},\"process.ppid\":{\"id\":\"string\"},\"process.thread.id\":{\"id\":\"string\"},\"server.bytes\":{\"id\":\"bytes\"},\"server.nat.port\":{\"id\":\"string\"},\"server.port\":{\"id\":\"string\"},\"source.bytes\":{\"id\":\"bytes\"},\"source.nat.port\":{\"id\":\"string\"},\"source.port\":{\"id\":\"string\"},\"system.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.memory.actual.free\":{\"id\":\"bytes\"},\"system.memory.total\":{\"id\":\"bytes\"},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\"},\"system.process.memory.size\":{\"id\":\"bytes\"},\"url.port\":{\"id\":\"string\"},\"view spans\":{\"id\":\"url\",\"params\":{\"labelTemplate\":\"View Spans\"}}}", - "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", + "fieldFormatMap": "{\"client.bytes\":{\"id\":\"bytes\"},\"client.nat.port\":{\"id\":\"string\"},\"client.port\":{\"id\":\"string\"},\"destination.bytes\":{\"id\":\"bytes\"},\"destination.nat.port\":{\"id\":\"string\"},\"destination.port\":{\"id\":\"string\"},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"},\"http.request.body.bytes\":{\"id\":\"bytes\"},\"http.request.bytes\":{\"id\":\"bytes\"},\"http.response.body.bytes\":{\"id\":\"bytes\"},\"http.response.bytes\":{\"id\":\"bytes\"},\"http.response.status_code\":{\"id\":\"string\"},\"log.syslog.facility.code\":{\"id\":\"string\"},\"log.syslog.priority\":{\"id\":\"string\"},\"network.bytes\":{\"id\":\"bytes\"},\"package.size\":{\"id\":\"string\"},\"process.pgid\":{\"id\":\"string\"},\"process.pid\":{\"id\":\"string\"},\"process.ppid\":{\"id\":\"string\"},\"process.thread.id\":{\"id\":\"string\"},\"server.bytes\":{\"id\":\"bytes\"},\"server.nat.port\":{\"id\":\"string\"},\"server.port\":{\"id\":\"string\"},\"source.bytes\":{\"id\":\"bytes\"},\"source.nat.port\":{\"id\":\"string\"},\"source.port\":{\"id\":\"string\"},\"system.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.memory.actual.free\":{\"id\":\"bytes\"},\"system.memory.total\":{\"id\":\"bytes\"},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\"},\"system.process.memory.size\":{\"id\":\"bytes\"},\"url.port\":{\"id\":\"string\"},\"view spans\":{\"id\":\"url\",\"params\":{\"labelTemplate\":\"View Spans\"}}}", + "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", "sourceFilters": "[{\"value\":\"sourcemap.sourcemap\"}]", "timeFieldName": "@timestamp" }, "id": "apm-*", "type": "index-pattern", "version": "1" -} +} \ No newline at end of file diff --git a/src/legacy/core_plugins/status_page/public/components/__snapshots__/metric_tiles.test.js.snap b/src/legacy/core_plugins/status_page/public/components/__snapshots__/metric_tiles.test.js.snap index ce24ff89776c83..b88210758a00d8 100644 --- a/src/legacy/core_plugins/status_page/public/components/__snapshots__/metric_tiles.test.js.snap +++ b/src/legacy/core_plugins/status_page/public/components/__snapshots__/metric_tiles.test.js.snap @@ -4,9 +4,7 @@ exports[`byte metric 1`] = ` `; @@ -14,9 +12,7 @@ exports[`float metric 1`] = ` `; @@ -24,9 +20,7 @@ exports[`general metric 1`] = ` `; @@ -34,8 +28,6 @@ exports[`millisecond metric 1`] = ` `; diff --git a/src/legacy/core_plugins/timelion/public/app.js b/src/legacy/core_plugins/timelion/public/app.js index 5bc5355d7c0612..04edfc5b611418 100644 --- a/src/legacy/core_plugins/timelion/public/app.js +++ b/src/legacy/core_plugins/timelion/public/app.js @@ -30,8 +30,6 @@ import { timefilter } from 'ui/timefilter'; import { npStart } from 'ui/new_platform'; import { getSavedSheetBreadcrumbs, getCreateBreadcrumbs } from './breadcrumbs'; -// import the uiExports that we want to "use" -import 'uiExports/fieldFormats'; import 'uiExports/savedObjectTypes'; require('ui/autoload/all'); diff --git a/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis_controller.js b/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis_controller.js index 8dd2b093c6f91c..9f58d00d382715 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis_controller.js +++ b/src/legacy/core_plugins/vis_type_metric/public/__tests__/metric_vis_controller.js @@ -51,7 +51,7 @@ describe('metric_vis - controller', function () { expect(metrics.length).to.be(1); expect(metrics[0].label).to.be('Count'); - expect(metrics[0].value).to.be(4301021); + expect(metrics[0].value).to.be('4301021'); }); it('should support multi-value metrics', function () { @@ -66,8 +66,8 @@ describe('metric_vis - controller', function () { expect(metrics.length).to.be(2); expect(metrics[0].label).to.be('1st percentile of bytes'); - expect(metrics[0].value).to.be(182); + expect(metrics[0].value).to.be('182'); expect(metrics[1].label).to.be('99th percentile of bytes'); - expect(metrics[1].value).to.be(445842.4634666484); + expect(metrics[1].value).to.be('445842.4634666484'); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.js index 0459d11c74ef0d..1192b678ae1488 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/tick_formatter.js @@ -19,10 +19,14 @@ import handlebars from 'handlebars/dist/handlebars'; import { isNumber } from 'lodash'; -import { fieldFormats } from 'ui/registry/field_formats'; +import { npStart } from 'ui/new_platform'; import { inputFormats, outputFormats, isDuration } from '../lib/durations'; + + export const createTickFormatter = (format = '0,0.[00]', template, getConfig = null) => { + const fieldFormats = npStart.plugins.data.fieldFormats; + if (!template) template = '{{value}}'; const render = handlebars.compile(template, { knownHelpersOnly: true }); let formatter; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/table/vis.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/table/vis.js index 8af175d116556e..10fc34fccd2cc6 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/table/vis.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/table/vis.js @@ -20,18 +20,17 @@ import _, { isArray, last, get } from 'lodash'; import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { fieldFormats } from 'ui/registry/field_formats'; +import { npStart } from 'ui/new_platform'; import { createTickFormatter } from '../../lib/tick_formatter'; import { calculateLabel } from '../../../../common/calculate_label'; import { isSortable } from './is_sortable'; import { EuiToolTip, EuiIcon } from '@elastic/eui'; import { replaceVars } from '../../lib/replace_vars'; +import { FIELD_FORMAT_IDS } from '../../../../../../../plugins/data/public'; import { FormattedMessage } from '@kbn/i18n/react'; import { METRIC_TYPES } from '../../../../common/metric_types'; -const DateFormat = fieldFormats.getType('date'); - function getColor(rules, colorKey, value) { let color; if (rules) { @@ -49,6 +48,10 @@ function getColor(rules, colorKey, value) { export class TableVis extends Component { constructor(props) { super(props); + + const fieldFormats = npStart.plugins.data.fieldFormats; + const DateFormat = fieldFormats.getType(FIELD_FORMAT_IDS.DATE); + this.dateFormatter = new DateFormat({}, this.props.getConfig); } diff --git a/src/legacy/plugin_discovery/types.ts b/src/legacy/plugin_discovery/types.ts index 97d3a4352c2f8c..d987260b099bd1 100644 --- a/src/legacy/plugin_discovery/types.ts +++ b/src/legacy/plugin_discovery/types.ts @@ -62,6 +62,8 @@ export interface LegacyPluginOptions { }>; apps: any; hacks: string[]; + visualize: string[]; + devTools: string[]; styleSheetPaths: string; injectDefaultVars: (server: Server) => Record; noParse: string[]; diff --git a/src/legacy/ui/field_formats/mixin/field_formats_mixin.ts b/src/legacy/ui/field_formats/mixin/field_formats_mixin.ts index 66466b96abe837..370c3127062423 100644 --- a/src/legacy/ui/field_formats/mixin/field_formats_mixin.ts +++ b/src/legacy/ui/field_formats/mixin/field_formats_mixin.ts @@ -20,10 +20,10 @@ import { has } from 'lodash'; import { Legacy } from 'kibana'; import { FieldFormatsService } from './field_formats_service'; -import { FieldFormat } from '../../../../plugins/data/public'; +import { IFieldFormatType } from '../../../../plugins/data/public'; export function fieldFormatsMixin(kbnServer: any, server: Legacy.Server) { - const fieldFormatClasses: Array = []; + const fieldFormatClasses: IFieldFormatType[] = []; // for use outside of the request context, for special cases server.decorate('server', 'fieldFormatServiceFactory', async function(uiSettings) { diff --git a/src/legacy/ui/field_formats/mixin/field_formats_service.ts b/src/legacy/ui/field_formats/mixin/field_formats_service.ts index c0800fcd4162bf..c5bc25333985b5 100644 --- a/src/legacy/ui/field_formats/mixin/field_formats_service.ts +++ b/src/legacy/ui/field_formats/mixin/field_formats_service.ts @@ -18,7 +18,7 @@ */ import { indexBy, Dictionary } from 'lodash'; -import { FieldFormat } from '../../../../plugins/data/public'; +import { FieldFormat, IFieldFormatType } from '../../../../plugins/data/common'; interface FieldFormatConfig { id: string; @@ -27,9 +27,9 @@ interface FieldFormatConfig { export class FieldFormatsService { getConfig: any; - _fieldFormats: Dictionary; + _fieldFormats: Dictionary; - constructor(fieldFormatClasses: Array, getConfig: Function) { + constructor(fieldFormatClasses: IFieldFormatType[], getConfig: Function) { this._fieldFormats = indexBy(fieldFormatClasses, 'id'); this.getConfig = getConfig; } diff --git a/src/legacy/ui/public/agg_response/hierarchical/build_hierarchical_data.test.js b/src/legacy/ui/public/agg_response/hierarchical/build_hierarchical_data.test.js index ce7d87c228fbd0..49d814c33209c3 100644 --- a/src/legacy/ui/public/agg_response/hierarchical/build_hierarchical_data.test.js +++ b/src/legacy/ui/public/agg_response/hierarchical/build_hierarchical_data.test.js @@ -20,16 +20,7 @@ import { buildHierarchicalData } from './build_hierarchical_data'; import { legacyResponseHandlerProvider } from '../../vis/response_handlers/legacy'; -jest.mock('../../registry/field_formats', () => { - return { fieldFormats: { - getType: id => { - if(id === '1') { return jest.fn(); } - if(id === 'agg_1') { return jest.fn(); } - } - } - }; -} -); +jest.mock('ui/new_platform'); jest.mock('../../chrome', () => ({ getUiSettingsClient: jest.fn() diff --git a/src/legacy/ui/public/agg_types/agg_config.ts b/src/legacy/ui/public/agg_types/agg_config.ts index becfaf8c89e273..de1a6059774e77 100644 --- a/src/legacy/ui/public/agg_types/agg_config.ts +++ b/src/legacy/ui/public/agg_types/agg_config.ts @@ -26,16 +26,14 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; +import { npStart } from 'ui/new_platform'; import { AggType } from './agg_type'; import { FieldParamType } from './param_types/field'; import { AggGroupNames } from '../vis/editors/default/agg_groups'; import { writeParams } from './agg_params'; import { AggConfigs } from './agg_configs'; import { Schema } from '../vis/editors/default/schemas'; -import { ContentType } from '../../../../plugins/data/public'; - -// @ts-ignore -import { fieldFormats } from '../registry/field_formats'; +import { ContentType, KBN_FIELD_TYPES } from '../../../../plugins/data/public'; export interface AggConfigOptions { enabled: boolean; @@ -378,14 +376,16 @@ export class AggConfig { if (format) { return format.getConverterFor(contentType); } + return this.fieldOwnFormatter(contentType, defaultFormat); } fieldOwnFormatter(contentType?: ContentType, defaultFormat?: any) { + const fieldFormats = npStart.plugins.data.fieldFormats; const field = this.getField(); let format = field && field.format; if (!format) format = defaultFormat; - if (!format) format = fieldFormats.getDefaultInstance('string'); + if (!format) format = fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.STRING); return format.getConverterFor(contentType); } diff --git a/src/legacy/ui/public/agg_types/agg_type.test.ts b/src/legacy/ui/public/agg_types/agg_type.test.ts index 1c1453b74fe983..9b34910e81e88b 100644 --- a/src/legacy/ui/public/agg_types/agg_type.test.ts +++ b/src/legacy/ui/public/agg_types/agg_type.test.ts @@ -19,15 +19,10 @@ import { AggType, AggTypeConfig } from './agg_type'; import { AggConfig } from './agg_config'; +import { npStart } from 'ui/new_platform'; jest.mock('ui/new_platform'); -jest.mock('ui/registry/field_formats', () => ({ - fieldFormats: { - getDefaultInstance: jest.fn(() => 'default'), - }, -})); - describe('AggType Class', () => { describe('constructor', () => { it("requires a valid config object as it's first param", () => { @@ -158,6 +153,8 @@ describe('AggType Class', () => { }); it('returns default formatter', () => { + npStart.plugins.data.fieldFormats.getDefaultInstance = jest.fn(() => 'default') as any; + const aggType = new AggType({ name: 'name', title: 'title', diff --git a/src/legacy/ui/public/agg_types/agg_type.ts b/src/legacy/ui/public/agg_types/agg_type.ts index 7be8ec1406d3cf..5216affb3e52dc 100644 --- a/src/legacy/ui/public/agg_types/agg_type.ts +++ b/src/legacy/ui/public/agg_types/agg_type.ts @@ -19,6 +19,7 @@ import { constant, noop, identity } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { npStart } from 'ui/new_platform'; import { AggParam, initParams } from './agg_params'; import { AggConfig } from '../vis'; @@ -27,8 +28,7 @@ import { SearchSource } from '../courier'; import { Adapters } from '../inspector'; import { BaseParamType } from './param_types/base'; -// @ts-ignore -import { FieldFormat, fieldFormats } from '../registry/field_formats'; +import { KBN_FIELD_TYPES, FieldFormat } from '../../../../plugins/data/public'; export interface AggTypeConfig< TAggConfig extends AggConfig = AggConfig, @@ -62,7 +62,9 @@ export interface AggTypeConfig< const getFormat = (agg: AggConfig) => { const field = agg.getField(); - return field ? field.format : fieldFormats.getDefaultInstance('string'); + const fieldFormats = npStart.plugins.data.fieldFormats; + + return field ? field.format : fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.STRING); }; export class AggType { diff --git a/src/legacy/ui/public/agg_types/buckets/date_range.ts b/src/legacy/ui/public/agg_types/buckets/date_range.ts index 908d921d123138..860d76ff2aa7bb 100644 --- a/src/legacy/ui/public/agg_types/buckets/date_range.ts +++ b/src/legacy/ui/public/agg_types/buckets/date_range.ts @@ -23,13 +23,15 @@ import { npStart } from 'ui/new_platform'; import { BUCKET_TYPES } from './bucket_agg_types'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { createFilterDateRange } from './create_filter/date_range'; -import { FieldFormat, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { DateRangesParamEditor } from '../../vis/editors/default/controls/date_ranges'; -// @ts-ignore -import { fieldFormats } from '../../registry/field_formats'; // @ts-ignore import { dateRange } from '../../utils/date_range'; +import { + KBN_FIELD_TYPES, + TEXT_CONTEXT_TYPE, + FieldFormat, +} from '../../../../../plugins/data/public'; const dateRangeTitle = i18n.translate('common.ui.aggTypes.buckets.dateRangeTitle', { defaultMessage: 'Date Range', @@ -48,7 +50,12 @@ export const dateRangeBucketAgg = new BucketAggType({ return { from, to }; }, getFormat(agg) { - const formatter = agg.fieldOwnFormatter('text', fieldFormats.getDefaultInstance('date')); + const fieldFormats = npStart.plugins.data.fieldFormats; + + const formatter = agg.fieldOwnFormatter( + TEXT_CONTEXT_TYPE, + fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.DATE) + ); const DateRangeFormat = FieldFormat.from(function(range: DateRangeKey) { return dateRange.toString(range, formatter); }); diff --git a/src/legacy/ui/public/agg_types/buckets/ip_range.ts b/src/legacy/ui/public/agg_types/buckets/ip_range.ts index 7ef415ff8d0c40..35155a482734ce 100644 --- a/src/legacy/ui/public/agg_types/buckets/ip_range.ts +++ b/src/legacy/ui/public/agg_types/buckets/ip_range.ts @@ -19,17 +19,20 @@ import { noop, map, omit, isNull } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { npStart } from 'ui/new_platform'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { IpRangeTypeParamEditor } from '../../vis/editors/default/controls/ip_range_type'; import { IpRangesParamEditor } from '../../vis/editors/default/controls/ip_ranges'; -// @ts-ignore -import { fieldFormats } from '../../registry/field_formats'; -import { FieldFormat, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { ipRange } from '../../utils/ip_range'; import { BUCKET_TYPES } from './bucket_agg_types'; // @ts-ignore import { createFilterIpRange } from './create_filter/ip_range'; +import { + KBN_FIELD_TYPES, + TEXT_CONTEXT_TYPE, + FieldFormat, +} from '../../../../../plugins/data/public'; const ipRangeTitle = i18n.translate('common.ui.aggTypes.buckets.ipRangeTitle', { defaultMessage: 'IPv4 Range', @@ -50,7 +53,11 @@ export const ipRangeBucketAgg = new BucketAggType({ return { type: 'range', from: bucket.from, to: bucket.to }; }, getFormat(agg) { - const formatter = agg.fieldOwnFormatter('text', fieldFormats.getDefaultInstance('ip')); + const fieldFormats = npStart.plugins.data.fieldFormats; + const formatter = agg.fieldOwnFormatter( + TEXT_CONTEXT_TYPE, + fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.IP) + ); const IpRangeFormat = FieldFormat.from(function(range: IpRangeKey) { return ipRange.toString(range, formatter); }); diff --git a/src/legacy/ui/public/agg_types/buckets/terms.ts b/src/legacy/ui/public/agg_types/buckets/terms.ts index c0f870c27f10d7..89e33784fb5fb8 100644 --- a/src/legacy/ui/public/agg_types/buckets/terms.ts +++ b/src/legacy/ui/public/agg_types/buckets/terms.ts @@ -41,7 +41,7 @@ import { OtherBucketParamEditor } from '../../vis/editors/default/controls/other import { AggConfigs } from '../agg_configs'; import { Adapters } from '../../../../../plugins/inspector/public'; -import { ContentType, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { ContentType, FieldFormat, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; // @ts-ignore import { Schemas } from '../../vis/editors/default/schemas'; @@ -75,7 +75,7 @@ export const termsBucketAgg = new BucketAggType({ const params = agg.params; return agg.getFieldDisplayName() + ': ' + params.order.text; }, - getFormat(bucket) { + getFormat(bucket): FieldFormat { return { getConverterFor: (type: ContentType) => { return (val: any) => { @@ -91,10 +91,11 @@ export const termsBucketAgg = new BucketAggType({ basePath: chrome.getBasePath(), }; const converter = bucket.params.field.format.getConverterFor(type); + return converter(val, undefined, undefined, parsedUrl); }; }, - }; + } as FieldFormat; }, createFilter: createFilterTerms, postFlightRequest: async ( diff --git a/src/legacy/ui/public/agg_types/metrics/cardinality.ts b/src/legacy/ui/public/agg_types/metrics/cardinality.ts index 221e1c6d6b0832..301ae2c80116c0 100644 --- a/src/legacy/ui/public/agg_types/metrics/cardinality.ts +++ b/src/legacy/ui/public/agg_types/metrics/cardinality.ts @@ -18,10 +18,10 @@ */ import { i18n } from '@kbn/i18n'; +import { npStart } from 'ui/new_platform'; import { MetricAggType } from './metric_agg_type'; -// @ts-ignore -import { fieldFormats } from '../../registry/field_formats'; import { METRIC_TYPES } from './metric_agg_types'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; const uniqueCountTitle = i18n.translate('common.ui.aggTypes.metrics.uniqueCountTitle', { defaultMessage: 'Unique Count', @@ -37,7 +37,9 @@ export const cardinalityMetricAgg = new MetricAggType({ }); }, getFormat() { - return fieldFormats.getDefaultInstance('number'); + const fieldFormats = npStart.plugins.data.fieldFormats; + + return fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); }, params: [ { diff --git a/src/legacy/ui/public/agg_types/metrics/count.ts b/src/legacy/ui/public/agg_types/metrics/count.ts index 12964c8873e97f..b5b844e8658d65 100644 --- a/src/legacy/ui/public/agg_types/metrics/count.ts +++ b/src/legacy/ui/public/agg_types/metrics/count.ts @@ -18,12 +18,11 @@ */ import { i18n } from '@kbn/i18n'; +import { npStart } from 'ui/new_platform'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -// @ts-ignore -import { fieldFormats } from '../../registry/field_formats'; - export const countMetricAgg = new MetricAggType({ name: METRIC_TYPES.COUNT, title: i18n.translate('common.ui.aggTypes.metrics.countTitle', { @@ -36,7 +35,9 @@ export const countMetricAgg = new MetricAggType({ }); }, getFormat() { - return fieldFormats.getDefaultInstance('number'); + const fieldFormats = npStart.plugins.data.fieldFormats; + + return fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); }, getValue(agg, bucket) { return bucket.doc_count; diff --git a/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts b/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts index 7428bd6caa22d1..a466b9f8526078 100644 --- a/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts +++ b/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { npStart } from 'ui/new_platform'; import { AggType, AggTypeConfig } from '../agg_type'; import { AggParamType } from '../param_types/agg'; import { AggConfig } from '../agg_config'; @@ -72,8 +73,9 @@ export class MetricAggType< this.getFormat = config.getFormat || (agg => { + const registeredFormats = npStart.plugins.data.fieldFormats; const field = agg.getField(); - return field ? field.format : fieldFormats.getDefaultInstance('number'); + return field ? field.format : registeredFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); }); this.subtype = diff --git a/src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts b/src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts index 4fabe137f1bc81..ead5122278b5a2 100644 --- a/src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts +++ b/src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts @@ -18,24 +18,27 @@ */ import { i18n } from '@kbn/i18n'; +import { npStart } from 'ui/new_platform'; import { PercentileRanksEditor } from '../../vis/editors/default/controls/percentile_ranks'; import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; import { getPercentileValue } from './percentiles_get_value'; import { METRIC_TYPES } from './metric_agg_types'; -// @ts-ignore -import { fieldFormats } from '../../registry/field_formats'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; +import { FIELD_FORMAT_IDS, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; // required by the values editor export type IPercentileRanksAggConfig = IResponseAggConfig; +const getFieldFormats = () => npStart.plugins.data.fieldFormats; + const valueProps = { makeLabel(this: IPercentileRanksAggConfig) { + const fieldFormats = getFieldFormats(); const field = this.getField(); - const format = (field && field.format) || fieldFormats.getDefaultInstance('number'); + const format = + (field && field.format) || fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); const customLabel = this.getParam('customLabel'); const label = customLabel || this.getFieldDisplayName(); @@ -81,7 +84,11 @@ export const percentileRanksMetricAgg = new MetricAggType new ValueAggConfig(value)); }, getFormat() { - return fieldFormats.getInstance('percent') || fieldFormats.getDefaultInstance('number'); + const fieldFormats = getFieldFormats(); + return ( + fieldFormats.getInstance(FIELD_FORMAT_IDS.PERCENT) || + fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER) + ); }, getValue(agg, bucket) { return getPercentileValue(agg, bucket) / 100; diff --git a/src/legacy/ui/public/chrome/api/angular.js b/src/legacy/ui/public/chrome/api/angular.js index e6457fec936330..73d50a83e11a5a 100644 --- a/src/legacy/ui/public/chrome/api/angular.js +++ b/src/legacy/ui/public/chrome/api/angular.js @@ -21,13 +21,15 @@ import { uiModules } from '../../modules'; import { directivesProvider } from '../directives'; import { registerSubUrlHooks } from './sub_url_hooks'; +import { start as data } from '../../../../core_plugins/data/public/legacy'; import { configureAppAngularModule } from 'ui/legacy_compat'; +import { npStart } from '../../new_platform/new_platform'; export function initAngularApi(chrome, internals) { chrome.setupAngular = function () { const kibana = uiModules.get('kibana'); - configureAppAngularModule(kibana); + configureAppAngularModule(kibana, npStart.core, data, false); kibana.value('chrome', chrome); diff --git a/src/legacy/ui/public/field_editor/field_editor.js b/src/legacy/ui/public/field_editor/field_editor.js index 896fb8fc5ddd81..f3c5990caae64a 100644 --- a/src/legacy/ui/public/field_editor/field_editor.js +++ b/src/legacy/ui/public/field_editor/field_editor.js @@ -27,10 +27,6 @@ import { getSupportedScriptingLanguages, } from 'ui/scripting_languages'; -import { - fieldFormats -} from 'ui/registry/field_formats'; - import { getDocLink } from 'ui/documentation_links'; @@ -39,6 +35,8 @@ import { toastNotifications } from 'ui/notify'; +import { npStart } from 'ui/new_platform'; + import { EuiBasicTable, EuiButton, @@ -84,7 +82,10 @@ import { FormattedMessage } from '@kbn/i18n/react'; // This loads Ace editor's "groovy" mode, used below to highlight the script. import 'brace/mode/groovy'; +const getFieldFormats = () => npStart.plugins.data.fieldFormats; + export class FieldEditor extends PureComponent { + static propTypes = { indexPattern: PropTypes.object.isRequired, field: PropTypes.object.isRequired, @@ -141,9 +142,10 @@ export class FieldEditor extends PureComponent { const fieldTypes = get(FIELD_TYPES_BY_LANG, field.lang, DEFAULT_FIELD_TYPES); field.type = fieldTypes.includes(field.type) ? field.type : fieldTypes[0]; - const DefaultFieldFormat = fieldFormats.getDefaultType(field.type, field.esTypes); + const fieldFormats = getFieldFormats(); + const fieldTypeFormats = [ - getDefaultFormat(DefaultFieldFormat), + getDefaultFormat(fieldFormats.getDefaultType(field.type, field.esTypes)), ...fieldFormats.getByFieldType(field.type), ]; @@ -169,12 +171,14 @@ export class FieldEditor extends PureComponent { onTypeChange = (type) => { const { getConfig } = this.props.helpers; const { field } = this.state; + const fieldFormats = getFieldFormats(); const DefaultFieldFormat = fieldFormats.getDefaultType(type); + field.type = type; const fieldTypeFormats = [ getDefaultFormat(DefaultFieldFormat), - ...fieldFormats.getByFieldType(field.type), + ...getFieldFormats().getByFieldType(field.type), ]; const FieldFormat = fieldTypeFormats[0]; @@ -202,6 +206,7 @@ export class FieldEditor extends PureComponent { const { getConfig } = this.props.helpers; const { field, fieldTypeFormats } = this.state; const FieldFormat = fieldTypeFormats.find((format) => format.id === formatId) || fieldTypeFormats[0]; + field.format = new FieldFormat(params, getConfig); this.setState({ @@ -684,6 +689,7 @@ export class FieldEditor extends PureComponent { } saveField = async () => { + const fieldFormat = this.state.field.format; const field = this.state.field.toActualField(); const { indexPattern } = this.props; const { fieldFormatId } = this.state; @@ -721,7 +727,7 @@ export class FieldEditor extends PureComponent { if (!fieldFormatId) { indexPattern.fieldFormatMap[field.name] = undefined; } else { - indexPattern.fieldFormatMap[field.name] = field.format; + indexPattern.fieldFormatMap[field.name] = fieldFormat; } return indexPattern.save() diff --git a/src/legacy/ui/public/field_editor/field_editor.test.js b/src/legacy/ui/public/field_editor/field_editor.test.js index 34503238f437d7..72eebee960b527 100644 --- a/src/legacy/ui/public/field_editor/field_editor.test.js +++ b/src/legacy/ui/public/field_editor/field_editor.test.js @@ -20,9 +20,12 @@ jest.mock('ui/kfetch', () => ({})); import React from 'react'; + +import { npStart } from 'ui/new_platform'; import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; jest.mock('brace/mode/groovy', () => ({})); +jest.mock('ui/new_platform'); import { FieldEditor } from './field_editor'; @@ -46,6 +49,10 @@ jest.mock('@elastic/eui', () => ({ EuiSpacer: 'eui-spacer', EuiText: 'eui-text', EuiTextArea: 'eui-textArea', + htmlIdGenerator: () => 42, + palettes: { + euiPaletteColorBlind: { colors: ['red'] } + } })); jest.mock('ui/scripting_languages', () => ({ @@ -54,26 +61,6 @@ jest.mock('ui/scripting_languages', () => ({ getDeprecatedScriptingLanguages: () => ['testlang'], })); -jest.mock('ui/registry/field_formats', () => { - class Format { - static id = 'test_format'; static title = 'Test format'; - params() {} - } - - return { - fieldFormats: { - getDefaultType: () => { - return Format; - }, - getByFieldType: (fieldType) => { - if(fieldType === 'number') { - return [Format]; - } - } - }, - }; -}); - jest.mock('ui/documentation_links', () => ({ getDocLink: (doc) => `(docLink for ${doc})`, })); @@ -133,6 +120,13 @@ describe('FieldEditor', () => { indexPattern = { fields, }; + + npStart.plugins.data.fieldFormats.getDefaultType = jest.fn(() => Format); + npStart.plugins.data.fieldFormats.getByFieldType = jest.fn((fieldType) => { + if(fieldType === 'number') { + return [Format]; + } + }); }); it('should render create new scripted field correctly', async () => { diff --git a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js index 9c4cee6b05db04..a1d48caf3f4898 100644 --- a/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js +++ b/src/legacy/ui/public/kbn_top_nav/kbn_top_nav.js @@ -75,8 +75,7 @@ export function createTopNavDirective() { module.directive('kbnTopNav', createTopNavDirective); -export function createTopNavHelper(reactDirective) { - const { TopNavMenu } = navigation.ui; +export const createTopNavHelper = ({ TopNavMenu }) => (reactDirective) => { return reactDirective( wrapInI18nContext(TopNavMenu), [ @@ -116,6 +115,6 @@ export function createTopNavHelper(reactDirective) { 'showAutoRefreshOnly', ], ); -} +}; -module.directive('kbnTopNavHelper', createTopNavHelper); +module.directive('kbnTopNavHelper', createTopNavHelper(navigation.ui)); diff --git a/src/legacy/ui/public/legacy_compat/angular_config.tsx b/src/legacy/ui/public/legacy_compat/angular_config.tsx index 788718e8484308..6e9f5c85aa1b2a 100644 --- a/src/legacy/ui/public/legacy_compat/angular_config.tsx +++ b/src/legacy/ui/public/legacy_compat/angular_config.tsx @@ -28,7 +28,7 @@ import { IRootScopeService, } from 'angular'; import $ from 'jquery'; -import { cloneDeep, forOwn, set } from 'lodash'; +import _, { cloneDeep, forOwn, get, set } from 'lodash'; import React, { Fragment } from 'react'; import * as Rx from 'rxjs'; @@ -37,27 +37,43 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { CoreStart, LegacyCoreStart } from 'kibana/public'; import { fatalError } from 'ui/notify'; -import { capabilities } from 'ui/capabilities'; +import { RouteConfiguration } from 'ui/routes/route_manager'; // @ts-ignore import { modifyUrl } from 'ui/url'; import { toMountPoint } from '../../../../plugins/kibana_react/public'; // @ts-ignore import { UrlOverflowService } from '../error_url_overflow'; -import { npStart } from '../new_platform'; -import { toastNotifications } from '../notify'; // @ts-ignore import { isSystemApiRequest } from '../system_api'; const URL_LIMIT_WARN_WITHIN = 1000; -function isDummyWrapperRoute($route: any) { +/** + * Detects whether a given angular route is a dummy route that doesn't + * require any action. There are two ways this can happen: + * If `outerAngularWrapperRoute` is set on the route config object, + * it means the local application service set up this route on the outer angular + * and the internal routes will handle the hooks. + * + * If angular did not detect a route and it is the local angular, we are currently + * navigating away from a URL controlled by a local angular router and the + * application will get unmounted. In this case the outer router will handle + * the hooks. + * @param $route Injected $route dependency + * @param isLocalAngular Flag whether this is the local angular router + */ +function isDummyRoute($route: any, isLocalAngular: boolean) { return ( - $route.current && $route.current.$$route && $route.current.$$route.outerAngularWrapperRoute + ($route.current && $route.current.$$route && $route.current.$$route.outerAngularWrapperRoute) || + (!$route.current && isLocalAngular) ); } -export const configureAppAngularModule = (angularModule: IModule) => { - const newPlatform = npStart.core; +export const configureAppAngularModule = ( + angularModule: IModule, + newPlatform: LegacyCoreStart, + isLocalAngular: boolean +) => { const legacyMetadata = newPlatform.injectedMetadata.getLegacyMetadata(); forOwn(newPlatform.injectedMetadata.getInjectedVars(), (val, name) => { @@ -73,15 +89,16 @@ export const configureAppAngularModule = (angularModule: IModule) => { .value('buildSha', legacyMetadata.buildSha) .value('serverName', legacyMetadata.serverName) .value('esUrl', getEsUrl(newPlatform)) - .value('uiCapabilities', capabilities.get()) + .value('uiCapabilities', newPlatform.application.capabilities) .config(setupCompileProvider(newPlatform)) .config(setupLocationProvider(newPlatform)) .config($setupXsrfRequestInterceptor(newPlatform)) .run(capture$httpLoadingCount(newPlatform)) - .run($setupBreadcrumbsAutoClear(newPlatform)) - .run($setupBadgeAutoClear(newPlatform)) - .run($setupHelpExtensionAutoClear(newPlatform)) - .run($setupUrlOverflowHandling(newPlatform)); + .run($setupBreadcrumbsAutoClear(newPlatform, isLocalAngular)) + .run($setupBadgeAutoClear(newPlatform, isLocalAngular)) + .run($setupHelpExtensionAutoClear(newPlatform, isLocalAngular)) + .run($setupUrlOverflowHandling(newPlatform, isLocalAngular)) + .run($setupUICapabilityRedirect(newPlatform)); }; const getEsUrl = (newPlatform: CoreStart) => { @@ -168,12 +185,42 @@ const capture$httpLoadingCount = (newPlatform: CoreStart) => ( ); }; +/** + * integrates with angular to automatically redirect to home if required + * capability is not met + */ +const $setupUICapabilityRedirect = (newPlatform: CoreStart) => ( + $rootScope: IRootScopeService, + $injector: any +) => { + const isKibanaAppRoute = window.location.pathname.endsWith('/app/kibana'); + // this feature only works within kibana app for now after everything is + // switched to the application service, this can be changed to handle all + // apps. + if (!isKibanaAppRoute) { + return; + } + $rootScope.$on( + '$routeChangeStart', + (event, { $$route: route }: { $$route?: RouteConfiguration } = {}) => { + if (!route || !route.requireUICapability) { + return; + } + + if (!get(newPlatform.application.capabilities, route.requireUICapability)) { + $injector.get('kbnUrl').change('/home'); + event.preventDefault(); + } + } + ); +}; + /** * internal angular run function that will be called when angular bootstraps and * lets us integrate with the angular router so that we can automatically clear * the breadcrumbs if we switch to a Kibana app that does not use breadcrumbs correctly */ -const $setupBreadcrumbsAutoClear = (newPlatform: CoreStart) => ( +const $setupBreadcrumbsAutoClear = (newPlatform: CoreStart, isLocalAngular: boolean) => ( $rootScope: IRootScopeService, $injector: any ) => { @@ -195,7 +242,7 @@ const $setupBreadcrumbsAutoClear = (newPlatform: CoreStart) => ( }); $rootScope.$on('$routeChangeSuccess', () => { - if (isDummyWrapperRoute($route)) { + if (isDummyRoute($route, isLocalAngular)) { return; } const current = $route.current || {}; @@ -223,7 +270,7 @@ const $setupBreadcrumbsAutoClear = (newPlatform: CoreStart) => ( * lets us integrate with the angular router so that we can automatically clear * the badge if we switch to a Kibana app that does not use the badge correctly */ -const $setupBadgeAutoClear = (newPlatform: CoreStart) => ( +const $setupBadgeAutoClear = (newPlatform: CoreStart, isLocalAngular: boolean) => ( $rootScope: IRootScopeService, $injector: any ) => { @@ -237,7 +284,7 @@ const $setupBadgeAutoClear = (newPlatform: CoreStart) => ( }); $rootScope.$on('$routeChangeSuccess', () => { - if (isDummyWrapperRoute($route)) { + if (isDummyRoute($route, isLocalAngular)) { return; } const current = $route.current || {}; @@ -266,7 +313,7 @@ const $setupBadgeAutoClear = (newPlatform: CoreStart) => ( * the helpExtension if we switch to a Kibana app that does not set its own * helpExtension */ -const $setupHelpExtensionAutoClear = (newPlatform: CoreStart) => ( +const $setupHelpExtensionAutoClear = (newPlatform: CoreStart, isLocalAngular: boolean) => ( $rootScope: IRootScopeService, $injector: any ) => { @@ -284,14 +331,14 @@ const $setupHelpExtensionAutoClear = (newPlatform: CoreStart) => ( const $route = $injector.has('$route') ? $injector.get('$route') : {}; $rootScope.$on('$routeChangeStart', () => { - if (isDummyWrapperRoute($route)) { + if (isDummyRoute($route, isLocalAngular)) { return; } helpExtensionSetSinceRouteChange = false; }); $rootScope.$on('$routeChangeSuccess', () => { - if (isDummyWrapperRoute($route)) { + if (isDummyRoute($route, isLocalAngular)) { return; } const current = $route.current || {}; @@ -304,7 +351,7 @@ const $setupHelpExtensionAutoClear = (newPlatform: CoreStart) => ( }); }; -const $setupUrlOverflowHandling = (newPlatform: CoreStart) => ( +const $setupUrlOverflowHandling = (newPlatform: CoreStart, isLocalAngular: boolean) => ( $location: ILocationService, $rootScope: IRootScopeService, $injector: auto.IInjectorService @@ -312,7 +359,7 @@ const $setupUrlOverflowHandling = (newPlatform: CoreStart) => ( const $route = $injector.has('$route') ? $injector.get('$route') : {}; const urlOverflow = new UrlOverflowService(); const check = () => { - if (isDummyWrapperRoute($route)) { + if (isDummyRoute($route, isLocalAngular)) { return; } // disable long url checks when storing state in session storage @@ -326,7 +373,7 @@ const $setupUrlOverflowHandling = (newPlatform: CoreStart) => ( try { if (urlOverflow.check($location.absUrl()) <= URL_LIMIT_WARN_WITHIN) { - toastNotifications.addWarning({ + newPlatform.notifications.toasts.addWarning({ title: i18n.translate('common.ui.chrome.bigUrlWarningNotificationTitle', { defaultMessage: 'The URL is big and Kibana might stop working', }), diff --git a/src/legacy/ui/public/legacy_compat/ensure_default_index_pattern.tsx b/src/legacy/ui/public/legacy_compat/ensure_default_index_pattern.tsx new file mode 100644 index 00000000000000..98e95865d7325a --- /dev/null +++ b/src/legacy/ui/public/legacy_compat/ensure_default_index_pattern.tsx @@ -0,0 +1,105 @@ +/* + * 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 { contains } from 'lodash'; +import { IRootScopeService } from 'angular'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { i18n } from '@kbn/i18n'; +import { I18nProvider } from '@kbn/i18n/react'; +import { EuiCallOut } from '@elastic/eui'; +import { CoreStart } from 'kibana/public'; +import { DataStart } from '../../../core_plugins/data/public'; + +let bannerId: string; +let timeoutId: NodeJS.Timeout | undefined; + +/** + * Checks whether a default index pattern is set and exists and defines + * one otherwise. + * + * If there are no index patterns, redirect to management page and show + * banner. In this case the promise returned from this function will never + * resolve to wait for the URL change to happen. + */ +export async function ensureDefaultIndexPattern( + newPlatform: CoreStart, + data: DataStart, + $rootScope: IRootScopeService, + kbnUrl: any +) { + const patterns = await data.indexPatterns.indexPatterns.getIds(); + let defaultId = newPlatform.uiSettings.get('defaultIndex'); + let defined = !!defaultId; + const exists = contains(patterns, defaultId); + + if (defined && !exists) { + newPlatform.uiSettings.remove('defaultIndex'); + defaultId = defined = false; + } + + if (defined) { + return; + } + + // If there is any index pattern created, set the first as default + if (patterns.length >= 1) { + defaultId = patterns[0]; + newPlatform.uiSettings.set('defaultIndex', defaultId); + } else { + const canManageIndexPatterns = + newPlatform.application.capabilities.management.kibana.index_patterns; + const redirectTarget = canManageIndexPatterns ? '/management/kibana/index_pattern' : '/home'; + + if (timeoutId) { + clearTimeout(timeoutId); + } + + // Avoid being hostile to new users who don't have an index pattern setup yet + // give them a friendly info message instead of a terse error message + bannerId = newPlatform.overlays.banners.replace(bannerId, (element: HTMLElement) => { + ReactDOM.render( + + + , + element + ); + return () => ReactDOM.unmountComponentAtNode(element); + }); + + // hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around + timeoutId = setTimeout(() => { + newPlatform.overlays.banners.remove(bannerId); + timeoutId = undefined; + }, 15000); + + kbnUrl.change(redirectTarget); + $rootScope.$digest(); + + // return never-resolving promise to stop resolving and wait for the url change + return new Promise(() => {}); + } +} diff --git a/src/legacy/ui/public/legacy_compat/index.ts b/src/legacy/ui/public/legacy_compat/index.ts index b29056954051ba..ea8932114118e6 100644 --- a/src/legacy/ui/public/legacy_compat/index.ts +++ b/src/legacy/ui/public/legacy_compat/index.ts @@ -18,3 +18,4 @@ */ export { configureAppAngularModule } from './angular_config'; +export { ensureDefaultIndexPattern } from './ensure_default_index_pattern'; diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 5c269c7b019aa2..8703e8750fda48 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -18,6 +18,7 @@ */ import sinon from 'sinon'; +import { getFieldFormatsRegistry } from '../../../../test_utils/public/stub_field_formats'; const mockObservable = () => { return { @@ -25,9 +26,24 @@ const mockObservable = () => { }; }; +export const mockUiSettings = { + get: (item) => { + return mockUiSettings[item]; + }, + getUpdate$: () => ({ + subscribe: sinon.fake(), + }), + 'query:allowLeadingWildcards': true, + 'query:queryString:options': {}, + 'courier:ignoreFilterIfFieldNotInIndex': true, + 'dateFormat:tz': 'Browser', + 'format:defaultTypeMap': {}, +}; + export const npSetup = { core: { - chrome: {} + chrome: {}, + uiSettings: mockUiSettings, }, plugins: { embeddable: { @@ -61,6 +77,7 @@ export const npSetup = { history: sinon.fake(), } }, + fieldFormats: getFieldFormatsRegistry(mockUiSettings), }, share: { register: () => {}, @@ -81,6 +98,9 @@ export const npSetup = { registerAction: sinon.fake(), registerTrigger: sinon.fake(), }, + feature_catalogue: { + register: sinon.fake(), + }, }, }; @@ -164,6 +184,7 @@ export const npStart = { history: sinon.fake(), }, }, + fieldFormats: getFieldFormatsRegistry(mockUiSettings), }, share: { toggleShareContextMenu: () => {}, @@ -185,6 +206,9 @@ export const npStart = { getTriggerActions: sinon.fake(), getTriggerCompatibleActions: sinon.fake(), }, + feature_catalogue: { + register: sinon.fake(), + }, }, }; diff --git a/src/legacy/ui/public/registry/field_formats.js b/src/legacy/ui/public/registry/field_formats.js deleted file mode 100644 index 9e03ef2e3cdd9a..00000000000000 --- a/src/legacy/ui/public/registry/field_formats.js +++ /dev/null @@ -1,186 +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 { memoize, forOwn, isFunction } from 'lodash'; -import { npStart } from 'ui/new_platform'; - -class FieldFormatRegistry { - constructor() { - this.fieldFormats = new Map(); - this._uiSettings = npStart.core.uiSettings; - this.getConfig = (...args) => this._uiSettings.get(...args); - this._defaultMap = []; - this.init(); - } - - init() { - this.parseDefaultTypeMap(this._uiSettings.get('format:defaultTypeMap')); - - this._uiSettings.getUpdate$().subscribe(({ key, newValue }) => { - if (key === 'format:defaultTypeMap') { - this.parseDefaultTypeMap(newValue); - } - }); - } - - /** - * Get the id of the default type for this field type - * using the format:defaultTypeMap config map - * - * @param {String} fieldType - the field type - * @param {String[]} esTypes - Array of ES data types - * @return {Object} - */ - getDefaultConfig = (fieldType, esTypes) => { - const type = this.getDefaultTypeName(fieldType, esTypes); - return this._defaultMap[type] || this._defaultMap._default_; - }; - - /** - * Get a FieldFormat type (class) by it's id. - * - * @param {String} formatId - the format id - * @return {Function} - */ - getType = (formatId) => { - return this.fieldFormats.get(formatId); - }; - /** - * Get the default FieldFormat type (class) for - * a field type, using the format:defaultTypeMap. - * used by the field editor - * - * @param {String} fieldType - * @param {String} esTypes - Array of ES data types - * @return {Function} - */ - getDefaultType = (fieldType, esTypes) => { - const config = this.getDefaultConfig(fieldType, esTypes); - return this.getType(config.id); - }; - - /** - * Get the name of the default type for ES types like date_nanos - * using the format:defaultTypeMap config map - * - * @param {String[]} esTypes - Array of ES data types - * @return {String|undefined} - */ - getTypeNameByEsTypes = (esTypes) => { - if(!Array.isArray(esTypes)) { - return; - } - return esTypes.find(type => this._defaultMap[type] && this._defaultMap[type].es); - }; - /** - * Get the default FieldFormat type name for - * a field type, using the format:defaultTypeMap. - * - * @param {String} fieldType - * @param {String[]} esTypes - * @return {string} - */ - getDefaultTypeName = (fieldType, esTypes) => { - return this.getTypeNameByEsTypes(esTypes) || fieldType; - }; - - /** - * Get the singleton instance of the FieldFormat type by it's id. - * - * @param {String} formatId - * @return {FieldFormat} - */ - getInstance = memoize(function (formatId) { - const FieldFormat = this.getType(formatId); - if (!FieldFormat) { - throw new Error(`Field Format '${formatId}' not found!`); - } - return new FieldFormat(null, this.getConfig); - }); - - /** - * Get the default fieldFormat instance for a field format. - * - * @param {String} fieldType - * @param {String[]} esTypes - * @return {FieldFormat} - */ - getDefaultInstancePlain(fieldType, esTypes) { - const conf = this.getDefaultConfig(fieldType, esTypes); - - const FieldFormat = this.getType(conf.id); - return new FieldFormat(conf.params, this.getConfig); - } - /** - * Returns a cache key built by the given variables for caching in memoized - * Where esType contains fieldType, fieldType is returned - * -> kibana types have a higher priority in that case - * -> would lead to failing tests that match e.g. date format with/without esTypes - * https://lodash.com/docs#memoize - * - * @param {String} fieldType - * @param {String[]} esTypes - * @return {string} - */ - getDefaultInstanceCacheResolver(fieldType, esTypes) { - return Array.isArray(esTypes) && esTypes.indexOf(fieldType) === -1 - ? [fieldType, ...esTypes].join('-') - : fieldType; - } - - /** - * Get filtered list of field formats by format type - * - * @param {String} fieldType - * @return {FieldFormat[]} - */ - - getByFieldType(fieldType) { - return [ ...this.fieldFormats.values()] - .filter(format => format.fieldType.indexOf(fieldType) !== -1); - } - - /** - * Get the default fieldFormat instance for a field format. - * It's a memoized function that builds and reads a cache - * - * @param {String} fieldType - * @param {String[]} esTypes - * @return {FieldFormat} - */ - getDefaultInstance = memoize(this.getDefaultInstancePlain, this.getDefaultInstanceCacheResolver); - - parseDefaultTypeMap(value) { - this._defaultMap = value; - forOwn(this, function (fn) { - if (isFunction(fn) && fn.cache) { - // clear all memoize caches - fn.cache = new memoize.Cache(); - } - }); - } - - register = derivedFieldFormat => { - this.fieldFormats.set(derivedFieldFormat.id, derivedFieldFormat); - - return this; - }; -} - -export const fieldFormats = new FieldFormatRegistry(); diff --git a/src/legacy/ui/public/routes/__tests__/_route_manager.js b/src/legacy/ui/public/routes/__tests__/_route_manager.js index d6d4c869b4b7ea..450bb51f0b0c62 100644 --- a/src/legacy/ui/public/routes/__tests__/_route_manager.js +++ b/src/legacy/ui/public/routes/__tests__/_route_manager.js @@ -119,18 +119,6 @@ describe('routes/route_manager', function () { expect($rp.when.secondCall.args[1]).to.have.property('reloadOnSearch', false); expect($rp.when.lastCall.args[1]).to.have.property('reloadOnSearch', true); }); - - it('sets route.requireDefaultIndex to false by default', function () { - routes.when('/nothing-set'); - routes.when('/no-index-required', { requireDefaultIndex: false }); - routes.when('/index-required', { requireDefaultIndex: true }); - routes.config($rp); - - expect($rp.when.callCount).to.be(3); - expect($rp.when.firstCall.args[1]).to.have.property('requireDefaultIndex', false); - expect($rp.when.secondCall.args[1]).to.have.property('requireDefaultIndex', false); - expect($rp.when.lastCall.args[1]).to.have.property('requireDefaultIndex', true); - }); }); describe('#defaults()', () => { diff --git a/src/legacy/ui/public/routes/route_manager.d.ts b/src/legacy/ui/public/routes/route_manager.d.ts index 56203354f3c203..a5261a7c8ee3a8 100644 --- a/src/legacy/ui/public/routes/route_manager.d.ts +++ b/src/legacy/ui/public/routes/route_manager.d.ts @@ -23,7 +23,7 @@ import { ChromeBreadcrumb } from '../../../../core/public'; -interface RouteConfiguration { +export interface RouteConfiguration { controller?: string | ((...args: any[]) => void); redirectTo?: string; resolveRedirectTo?: (...args: any[]) => void; diff --git a/src/legacy/ui/public/routes/route_manager.js b/src/legacy/ui/public/routes/route_manager.js index ba48984bb45b9d..6444ef66fbe474 100644 --- a/src/legacy/ui/public/routes/route_manager.js +++ b/src/legacy/ui/public/routes/route_manager.js @@ -46,10 +46,6 @@ export default function RouteManager() { route.reloadOnSearch = false; } - if (route.requireDefaultIndex == null) { - route.requireDefaultIndex = false; - } - wrapRouteWithPrep(route, setup); $routeProvider.when(path, route); }); diff --git a/src/legacy/ui/public/saved_objects/__tests__/saved_object.js b/src/legacy/ui/public/saved_objects/__tests__/saved_object.js index ff53d48de758a4..56124a047ba6d0 100644 --- a/src/legacy/ui/public/saved_objects/__tests__/saved_object.js +++ b/src/legacy/ui/public/saved_objects/__tests__/saved_object.js @@ -26,6 +26,7 @@ import { SavedObjectProvider } from '../saved_object'; import StubIndexPattern from 'test_utils/stub_index_pattern'; import { SavedObjectsClientProvider } from '../saved_objects_client_provider'; import { InvalidJSONProperty } from '../../../../../plugins/kibana_utils/public'; +import { mockUiSettings } from '../../new_platform/new_platform.karma_mock'; const getConfig = cfg => cfg; @@ -337,7 +338,7 @@ describe('Saved Object', function () { type: 'dashboard', }); }); - const indexPattern = new StubIndexPattern('my-index', getConfig, null, []); + const indexPattern = new StubIndexPattern('my-index', getConfig, null, [], mockUiSettings); indexPattern.title = indexPattern.id; savedObject.searchSource.setField('index', indexPattern); return savedObject @@ -725,7 +726,7 @@ describe('Saved Object', function () { const savedObject = new SavedObject(config); sinon.stub(savedObject, 'hydrateIndexPattern').callsFake(() => { - const indexPattern = new StubIndexPattern(indexPatternId, getConfig, null, []); + const indexPattern = new StubIndexPattern(indexPatternId, getConfig, null, [], mockUiSettings); indexPattern.title = indexPattern.id; savedObject.searchSource.setField('index', indexPattern); return Promise.resolve(indexPattern); diff --git a/src/legacy/ui/public/state_management/state.js b/src/legacy/ui/public/state_management/state.js index b7623ab0fc5a51..8d55a6929a617a 100644 --- a/src/legacy/ui/public/state_management/state.js +++ b/src/legacy/ui/public/state_management/state.js @@ -42,9 +42,14 @@ import { isStateHash, } from './state_storage'; -export function StateProvider(Private, $rootScope, $location, stateManagementConfig, config, kbnUrl) { +export function StateProvider(Private, $rootScope, $location, stateManagementConfig, config, kbnUrl, $injector) { const Events = Private(EventsProvider); + const isDummyRoute = () => + $injector.has('$route') && + $injector.get('$route').current && + $injector.get('$route').current.outerAngularWrapperRoute; + createLegacyClass(State).inherits(Events); function State( urlParam, @@ -137,7 +142,7 @@ export function StateProvider(Private, $rootScope, $location, stateManagementCon let stash = this._readFromURL(); - // nothing to read from the url? save if ordered to persist + // nothing to read from the url? save if ordered to persist, but only if it's not on a wrapper route if (stash === null) { if (this._persistAcrossApps) { return this.save(); @@ -150,7 +155,7 @@ export function StateProvider(Private, $rootScope, $location, stateManagementCon // apply diff to state from stash, will change state in place via side effect const diffResults = applyDiff(this, stash); - if (diffResults.keys.length) { + if (!isDummyRoute() && diffResults.keys.length) { this.emit('fetch_with_changes', diffResults.keys); } }; @@ -164,6 +169,10 @@ export function StateProvider(Private, $rootScope, $location, stateManagementCon return; } + if (isDummyRoute()) { + return; + } + let stash = this._readFromURL(); const state = this.toObject(); replace = replace || false; diff --git a/src/legacy/ui/public/time_buckets/time_buckets.js b/src/legacy/ui/public/time_buckets/time_buckets.js index bcd5cd161edd3a..c875d7820373ea 100644 --- a/src/legacy/ui/public/time_buckets/time_buckets.js +++ b/src/legacy/ui/public/time_buckets/time_buckets.js @@ -20,13 +20,14 @@ import _ from 'lodash'; import moment from 'moment'; import chrome from '../chrome'; +import { npStart } from 'ui/new_platform'; import { parseInterval } from '../utils/parse_interval'; import { calcAutoIntervalLessThan, calcAutoIntervalNear } from './calc_auto_interval'; import { convertDurationToNormalizedEsInterval, convertIntervalToEsInterval, } from './calc_es_interval'; -import { fieldFormats } from '../registry/field_formats'; +import { FIELD_FORMAT_IDS } from '../../../../plugins/data/public'; const config = chrome.getUiSettingsClient(); @@ -311,7 +312,9 @@ TimeBuckets.prototype.getScaledDateFormat = function () { }; TimeBuckets.prototype.getScaledDateFormatter = function () { - const DateFieldFormat = fieldFormats.getType('date'); + const fieldFormats = npStart.plugins.data.fieldFormats; + const DateFieldFormat = fieldFormats.getType(FIELD_FORMAT_IDS.DATE); + return new DateFieldFormat({ pattern: this.getScaledDateFormat() }, getConfig); diff --git a/src/legacy/ui/public/timefilter/setup_router.test.js b/src/legacy/ui/public/timefilter/setup_router.test.js index 4bc797e5eff00e..f229937c3b435b 100644 --- a/src/legacy/ui/public/timefilter/setup_router.test.js +++ b/src/legacy/ui/public/timefilter/setup_router.test.js @@ -42,9 +42,14 @@ describe('registerTimefilterWithGlobalState()', () => { } }; + const rootScope = { + $on: jest.fn() + }; + registerTimefilterWithGlobalState( timefilter, - globalState + globalState, + rootScope, ); expect(setTime.mock.calls.length).toBe(2); diff --git a/src/legacy/ui/public/timefilter/setup_router.ts b/src/legacy/ui/public/timefilter/setup_router.ts index 0a73378f99cd7d..64105b016fb44c 100644 --- a/src/legacy/ui/public/timefilter/setup_router.ts +++ b/src/legacy/ui/public/timefilter/setup_router.ts @@ -23,6 +23,7 @@ import moment from 'moment'; import { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; import chrome from 'ui/chrome'; import { RefreshInterval, TimeRange, TimefilterContract } from 'src/plugins/data/public'; +import { Subscription } from 'rxjs'; // TODO // remove everything underneath once globalState is no longer an angular service @@ -40,49 +41,62 @@ export function getTimefilterConfig() { }; } -// Currently some parts of Kibana (index patterns, timefilter) rely on addSetupWork in the uiRouter -// and require it to be executed to properly function. -// This function is exposed for applications that do not use uiRoutes like APM -// Kibana issue https://github.com/elastic/kibana/issues/19110 tracks the removal of this dependency on uiRouter -export const registerTimefilterWithGlobalState = _.once( - (timefilter: TimefilterContract, globalState: any, $rootScope: IScope) => { - // settings have to be re-fetched here, to make sure that settings changed by overrideLocalDefault are taken into account. - const config = getTimefilterConfig(); - timefilter.setTime(_.defaults(globalState.time || {}, config.timeDefaults)); - timefilter.setRefreshInterval( - _.defaults(globalState.refreshInterval || {}, config.refreshIntervalDefaults) - ); +export const registerTimefilterWithGlobalStateFactory = ( + timefilter: TimefilterContract, + globalState: any, + $rootScope: IScope +) => { + // settings have to be re-fetched here, to make sure that settings changed by overrideLocalDefault are taken into account. + const config = getTimefilterConfig(); + timefilter.setTime(_.defaults(globalState.time || {}, config.timeDefaults)); + timefilter.setRefreshInterval( + _.defaults(globalState.refreshInterval || {}, config.refreshIntervalDefaults) + ); - globalState.on('fetch_with_changes', () => { - // clone and default to {} in one - const newTime: TimeRange = _.defaults({}, globalState.time, config.timeDefaults); - const newRefreshInterval: RefreshInterval = _.defaults( - {}, - globalState.refreshInterval, - config.refreshIntervalDefaults - ); + globalState.on('fetch_with_changes', () => { + // clone and default to {} in one + const newTime: TimeRange = _.defaults({}, globalState.time, config.timeDefaults); + const newRefreshInterval: RefreshInterval = _.defaults( + {}, + globalState.refreshInterval, + config.refreshIntervalDefaults + ); - if (newTime) { - if (newTime.to) newTime.to = convertISO8601(newTime.to); - if (newTime.from) newTime.from = convertISO8601(newTime.from); - } + if (newTime) { + if (newTime.to) newTime.to = convertISO8601(newTime.to); + if (newTime.from) newTime.from = convertISO8601(newTime.from); + } - timefilter.setTime(newTime); - timefilter.setRefreshInterval(newRefreshInterval); - }); + timefilter.setTime(newTime); + timefilter.setRefreshInterval(newRefreshInterval); + }); - const updateGlobalStateWithTime = () => { - globalState.time = timefilter.getTime(); - globalState.refreshInterval = timefilter.getRefreshInterval(); - globalState.save(); - }; + const updateGlobalStateWithTime = () => { + globalState.time = timefilter.getTime(); + globalState.refreshInterval = timefilter.getRefreshInterval(); + globalState.save(); + }; + const subscriptions = new Subscription(); + subscriptions.add( subscribeWithScope($rootScope, timefilter.getRefreshIntervalUpdate$(), { next: updateGlobalStateWithTime, - }); + }) + ); + subscriptions.add( subscribeWithScope($rootScope, timefilter.getTimeUpdate$(), { next: updateGlobalStateWithTime, - }); - } -); + }) + ); + + $rootScope.$on('$destroy', () => { + subscriptions.unsubscribe(); + }); +}; + +// Currently some parts of Kibana (index patterns, timefilter) rely on addSetupWork in the uiRouter +// and require it to be executed to properly function. +// This function is exposed for applications that do not use uiRoutes like APM +// Kibana issue https://github.com/elastic/kibana/issues/19110 tracks the removal of this dependency on uiRouter +export const registerTimefilterWithGlobalState = _.once(registerTimefilterWithGlobalStateFactory); diff --git a/src/legacy/ui/public/vis/__tests__/_agg_config.js b/src/legacy/ui/public/vis/__tests__/_agg_config.js index 2e2e0c31bdb8af..9b0398cf8853e1 100644 --- a/src/legacy/ui/public/vis/__tests__/_agg_config.js +++ b/src/legacy/ui/public/vis/__tests__/_agg_config.js @@ -24,7 +24,6 @@ import { Vis } from '..'; import { AggType } from '../../agg_types/agg_type'; import { AggConfig } from '../../agg_types/agg_config'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { fieldFormats } from '../../registry/field_formats'; describe('AggConfig', function () { @@ -439,12 +438,15 @@ describe('AggConfig', function () { } ] }); - expect(vis.aggs.aggs[0].fieldFormatter()).to.be(fieldFormats.getDefaultInstance('number').getConverterFor()); + + const fieldFormatter = vis.aggs.aggs[0].fieldFormatter(); + + expect(fieldFormatter).to.be.defined; + expect(fieldFormatter('text')).to.be('text'); }); }); describe('#fieldFormatter - no custom getFormat handler', function () { - const visStateAggWithoutCustomGetFormat = { aggs: [ { @@ -467,13 +469,17 @@ describe('AggConfig', function () { it('returns the string format if the field does not have a format', function () { const agg = vis.aggs.aggs[0]; agg.params.field = { type: 'number', format: null }; - expect(agg.fieldFormatter()).to.be(fieldFormats.getDefaultInstance('string').getConverterFor()); + const fieldFormatter = agg.fieldFormatter(); + expect(fieldFormatter).to.be.defined; + expect(fieldFormatter('text')).to.be('text'); }); it('returns the string format if their is no field', function () { const agg = vis.aggs.aggs[0]; delete agg.params.field; - expect(agg.fieldFormatter()).to.be(fieldFormats.getDefaultInstance('string').getConverterFor()); + const fieldFormatter = agg.fieldFormatter(); + expect(fieldFormatter).to.be.defined; + expect(fieldFormatter('text')).to.be('text'); }); it('returns the html converter if "html" is passed in', function () { diff --git a/src/legacy/ui/public/vis/vis_filters/vis_filters.js b/src/legacy/ui/public/vis/vis_filters/vis_filters.js index e879d040125f11..18d633e1b5fb2f 100644 --- a/src/legacy/ui/public/vis/vis_filters/vis_filters.js +++ b/src/legacy/ui/public/vis/vis_filters/vis_filters.js @@ -115,7 +115,6 @@ const VisFiltersProvider = (getAppState, $timeout) => { } }; - return { pushFilters, }; diff --git a/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts b/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts index 6598da76f60ba1..f49e0f08e87324 100644 --- a/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts +++ b/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts @@ -20,14 +20,13 @@ import { i18n } from '@kbn/i18n'; import { identity } from 'lodash'; import { AggConfig, Vis } from 'ui/vis'; +import { npStart } from 'ui/new_platform'; import { SerializedFieldFormat } from 'src/plugins/expressions/public'; -import { FieldFormat } from '../../../../../../plugins/data/public'; +import { IFieldFormatId, FieldFormat } from '../../../../../../plugins/data/public'; import { tabifyGetColumns } from '../../../agg_response/tabify/_get_columns'; import chrome from '../../../chrome'; -// @ts-ignore -import { fieldFormats } from '../../../registry/field_formats'; import { dateRange } from '../../../utils/date_range'; import { ipRange } from '../../../utils/ip_range'; import { DateRangeKey } from '../../../agg_types/buckets/date_range'; @@ -45,18 +44,22 @@ function isTermsFieldFormat( return serializedFieldFormat.id === 'terms'; } -const config = chrome.getUiSettingsClient(); +const getConfig = (key: string, defaultOverride?: any): any => + npStart.core.uiSettings.get(key, defaultOverride); +const DefaultFieldFormat = FieldFormat.from(identity); -const getConfig = (...args: any[]): any => config.get(...args); -const getDefaultFieldFormat = () => ({ convert: identity }); +const getFieldFormat = (id?: IFieldFormatId, params: object = {}): FieldFormat => { + const fieldFormats = npStart.plugins.data.fieldFormats; -const getFieldFormat = (id: string | undefined, params: object = {}) => { - const Format = fieldFormats.getType(id); - if (Format) { - return new Format(params, getConfig); - } else { - return getDefaultFieldFormat(); + if (id) { + const Format = fieldFormats.getType(id); + + if (Format) { + return new Format(params, getConfig); + } } + + return new DefaultFieldFormat(); }; export const createFormat = (agg: AggConfig): SerializedFieldFormat => { @@ -93,9 +96,9 @@ export const createFormat = (agg: AggConfig): SerializedFieldFormat => { export type FormatFactory = (mapping?: SerializedFieldFormat) => FieldFormat; -export const getFormat: FormatFactory = (mapping = {}) => { +export const getFormat: FormatFactory = mapping => { if (!mapping) { - return getDefaultFieldFormat(); + return new DefaultFieldFormat(); } const { id } = mapping; if (id === 'range') { @@ -145,6 +148,7 @@ export const getFormat: FormatFactory = (mapping = {}) => { pathname: window.location.pathname, basePath: chrome.getBasePath(), }; + // @ts-ignore return format.convert(val, undefined, undefined, parsedUrl); }; }, @@ -161,9 +165,10 @@ export const getFormat: FormatFactory = (mapping = {}) => { pathname: window.location.pathname, basePath: chrome.getBasePath(), }; + // @ts-ignore return format.convert(val, type, undefined, parsedUrl); }, - }; + } as FieldFormat; } else { return getFieldFormat(id, mapping.params); } diff --git a/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_dashboard_viewport.scss b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_dashboard_viewport.scss index 7cbe1351158776..9575908146d1d8 100644 --- a/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_dashboard_viewport.scss +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_dashboard_viewport.scss @@ -1,8 +1,10 @@ .dshDashboardViewport { + height: 100%; width: 100%; background-color: $euiColorEmptyShade; } .dshDashboardViewport-withMargins { width: 100%; + height: 100%; } diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/exists_filter.ts b/src/plugins/data/common/es_query/filters/stubs/exists_filter.ts similarity index 93% rename from src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/exists_filter.ts rename to src/plugins/data/common/es_query/filters/stubs/exists_filter.ts index 5af97818f9bfbd..13b8189b6e22f7 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/exists_filter.ts +++ b/src/plugins/data/common/es_query/filters/stubs/exists_filter.ts @@ -17,7 +17,7 @@ * under the License. */ -import { esFilters } from '../../../../../../../../../plugins/data/public'; +import { esFilters } from '../../..'; export const existsFilter: esFilters.ExistsFilter = { meta: { diff --git a/src/legacy/ui/public/registry/field_formats.d.ts b/src/plugins/data/common/es_query/filters/stubs/index.ts similarity index 85% rename from src/legacy/ui/public/registry/field_formats.d.ts rename to src/plugins/data/common/es_query/filters/stubs/index.ts index 79eec7a5a4e742..4f4c11f2e5ab74 100644 --- a/src/legacy/ui/public/registry/field_formats.d.ts +++ b/src/plugins/data/common/es_query/filters/stubs/index.ts @@ -17,4 +17,7 @@ * under the License. */ -export type FieldFormat = any; +export * from './exists_filter'; +export * from './phrase_filter'; +export * from './phrases_filter'; +export * from './range_filter'; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrase_filter.ts b/src/plugins/data/common/es_query/filters/stubs/phrase_filter.ts similarity index 93% rename from src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrase_filter.ts rename to src/plugins/data/common/es_query/filters/stubs/phrase_filter.ts index b6c8b9905e6b33..7456e056a02b17 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrase_filter.ts +++ b/src/plugins/data/common/es_query/filters/stubs/phrase_filter.ts @@ -17,7 +17,7 @@ * under the License. */ -import { esFilters } from '../../../../../../../../../plugins/data/public'; +import { esFilters } from '../../..'; export const phraseFilter: esFilters.PhraseFilter = { meta: { diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrases_filter.ts b/src/plugins/data/common/es_query/filters/stubs/phrases_filter.ts similarity index 93% rename from src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrases_filter.ts rename to src/plugins/data/common/es_query/filters/stubs/phrases_filter.ts index 2e2ba4f798bddf..4bd70b85e186ae 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/phrases_filter.ts +++ b/src/plugins/data/common/es_query/filters/stubs/phrases_filter.ts @@ -17,7 +17,7 @@ * under the License. */ -import { esFilters } from '../../../../../../../../../plugins/data/public'; +import { esFilters } from '../../..'; export const phrasesFilter: esFilters.PhrasesFilter = { meta: { diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/range_filter.ts b/src/plugins/data/common/es_query/filters/stubs/range_filter.ts similarity index 93% rename from src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/range_filter.ts rename to src/plugins/data/common/es_query/filters/stubs/range_filter.ts index c6438e30ecec61..5a6d245e2ef6ff 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/fixtures/range_filter.ts +++ b/src/plugins/data/common/es_query/filters/stubs/range_filter.ts @@ -17,7 +17,7 @@ * under the License. */ -import { esFilters } from '../../../../../../../../../plugins/data/public'; +import { esFilters } from '../../..'; export const rangeFilter: esFilters.RangeFilter = { meta: { diff --git a/src/plugins/data/common/field_formats/converters/boolean.test.ts b/src/plugins/data/common/field_formats/converters/boolean.test.ts index 2a548a6c1b1797..3650df65176115 100644 --- a/src/plugins/data/common/field_formats/converters/boolean.test.ts +++ b/src/plugins/data/common/field_formats/converters/boolean.test.ts @@ -23,7 +23,7 @@ describe('Boolean Format', () => { let boolean: Record; beforeEach(() => { - boolean = new BoolFormat(); + boolean = new BoolFormat({}, jest.fn()); }); [ diff --git a/src/plugins/data/common/field_formats/converters/boolean.ts b/src/plugins/data/common/field_formats/converters/boolean.ts index 96e353592d6760..6cc6c71465d50b 100644 --- a/src/plugins/data/common/field_formats/converters/boolean.ts +++ b/src/plugins/data/common/field_formats/converters/boolean.ts @@ -19,11 +19,11 @@ import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert } from '../types'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; import { asPrettyString } from '../utils'; export class BoolFormat extends FieldFormat { - static id = 'boolean'; + static id = FIELD_FORMAT_IDS.BOOLEAN; static title = 'Boolean'; static fieldType = [KBN_FIELD_TYPES.BOOLEAN, KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING]; diff --git a/src/plugins/data/common/field_formats/converters/bytes.ts b/src/plugins/data/common/field_formats/converters/bytes.ts index 146f7afe74ccb4..6c6df5eb7367d8 100644 --- a/src/plugins/data/common/field_formats/converters/bytes.ts +++ b/src/plugins/data/common/field_formats/converters/bytes.ts @@ -18,9 +18,10 @@ */ import { NumeralFormat } from './numeral'; +import { FIELD_FORMAT_IDS } from '../types'; export class BytesFormat extends NumeralFormat { - static id = 'bytes'; + static id = FIELD_FORMAT_IDS.BYTES; static title = 'Bytes'; id = BytesFormat.id; diff --git a/src/plugins/data/common/field_formats/converters/color.test.ts b/src/plugins/data/common/field_formats/converters/color.test.ts index b7fcbf61227ebe..f7aa26d449c7bb 100644 --- a/src/plugins/data/common/field_formats/converters/color.test.ts +++ b/src/plugins/data/common/field_formats/converters/color.test.ts @@ -23,16 +23,19 @@ import { HTML_CONTEXT_TYPE } from '../content_types'; describe('Color Format', () => { describe('field is a number', () => { test('should add colors if the value is in range', () => { - const colorer = new ColorFormat({ - fieldType: 'number', - colors: [ - { - range: '100:150', - text: 'blue', - background: 'yellow', - }, - ], - }); + const colorer = new ColorFormat( + { + fieldType: 'number', + colors: [ + { + range: '100:150', + text: 'blue', + background: 'yellow', + }, + ], + }, + jest.fn() + ); expect(colorer.convert(99, HTML_CONTEXT_TYPE)).toBe('99'); expect(colorer.convert(100, HTML_CONTEXT_TYPE)).toBe( @@ -45,16 +48,19 @@ describe('Color Format', () => { }); test('should not convert invalid ranges', () => { - const colorer = new ColorFormat({ - fieldType: 'number', - colors: [ - { - range: '100150', - text: 'blue', - background: 'yellow', - }, - ], - }); + const colorer = new ColorFormat( + { + fieldType: 'number', + colors: [ + { + range: '100150', + text: 'blue', + background: 'yellow', + }, + ], + }, + jest.fn() + ); expect(colorer.convert(99, HTML_CONTEXT_TYPE)).toBe('99'); }); @@ -62,16 +68,19 @@ describe('Color Format', () => { describe('field is a string', () => { test('should add colors if the regex matches', () => { - const colorer = new ColorFormat({ - fieldType: 'string', - colors: [ - { - regex: 'A.*', - text: 'blue', - background: 'yellow', - }, - ], - }); + const colorer = new ColorFormat( + { + fieldType: 'string', + colors: [ + { + regex: 'A.*', + text: 'blue', + background: 'yellow', + }, + ], + }, + jest.fn() + ); const converter = colorer.getConverterFor(HTML_CONTEXT_TYPE) as Function; expect(converter('B', HTML_CONTEXT_TYPE)).toBe('B'); @@ -97,16 +106,19 @@ describe('Color Format', () => { }); test('returns original value (escaped) when regex is invalid', () => { - const colorer = new ColorFormat({ - fieldType: 'string', - colors: [ - { - regex: 'A.*', - text: 'blue', - background: 'yellow', - }, - ], - }); + const colorer = new ColorFormat( + { + fieldType: 'string', + colors: [ + { + regex: 'A.*', + text: 'blue', + background: 'yellow', + }, + ], + }, + jest.fn() + ); const converter = colorer.getConverterFor(HTML_CONTEXT_TYPE) as Function; expect(converter('<', HTML_CONTEXT_TYPE)).toBe('<'); diff --git a/src/plugins/data/common/field_formats/converters/color.ts b/src/plugins/data/common/field_formats/converters/color.ts index 6ba8bb97332e83..ffc72ba9a2c306 100644 --- a/src/plugins/data/common/field_formats/converters/color.ts +++ b/src/plugins/data/common/field_formats/converters/color.ts @@ -20,14 +20,14 @@ import { findLast, cloneDeep, template, escape } from 'lodash'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { HtmlContextTypeConvert } from '../types'; +import { HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; import { asPrettyString } from '../utils'; import { DEFAULT_CONVERTER_COLOR } from '../constants'; const convertTemplate = template('<%- val %>'); export class ColorFormat extends FieldFormat { - static id = 'color'; + static id = FIELD_FORMAT_IDS.COLOR; static title = 'Color'; static fieldType = [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING]; diff --git a/src/plugins/data/common/field_formats/converters/custom.ts b/src/plugins/data/common/field_formats/converters/custom.ts index 8ab31e5784566d..687870306c8731 100644 --- a/src/plugins/data/common/field_formats/converters/custom.ts +++ b/src/plugins/data/common/field_formats/converters/custom.ts @@ -18,13 +18,11 @@ */ import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert } from '../types'; - -const ID = 'custom'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export const createCustomFieldFormat = (convert: TextContextTypeConvert) => class CustomFieldFormat extends FieldFormat { - static id = ID; + static id = FIELD_FORMAT_IDS.CUSTOM; textConvert = convert; }; diff --git a/src/plugins/data/common/field_formats/converters/date.ts b/src/plugins/data/common/field_formats/converters/date.ts index 0017d5afb0608f..06af64d9c17c2b 100644 --- a/src/plugins/data/common/field_formats/converters/date.ts +++ b/src/plugins/data/common/field_formats/converters/date.ts @@ -19,28 +19,23 @@ import { memoize, noop } from 'lodash'; import moment from 'moment'; -import { FieldFormat, KBN_FIELD_TYPES, TextContextTypeConvert } from '../../index'; +import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; +import { FieldFormat } from '../field_format'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class DateFormat extends FieldFormat { - static id = 'date'; + static id = FIELD_FORMAT_IDS.DATE; static title = 'Date'; static fieldType = KBN_FIELD_TYPES.DATE; - private getConfig: Function; private memoizedConverter: Function = noop; private memoizedPattern: string = ''; private timeZone: string = ''; - constructor(params: Record, getConfig: Function) { - super(params); - - this.getConfig = getConfig; - } - getParamDefaults() { return { - pattern: this.getConfig('dateFormat'), - timezone: this.getConfig('dateFormat:tz'), + pattern: this.getConfig!('dateFormat'), + timezone: this.getConfig!('dateFormat:tz'), }; } diff --git a/src/plugins/data/common/field_formats/converters/date_nanos.ts b/src/plugins/data/common/field_formats/converters/date_nanos.ts index aef47f362bc978..8b0f8b111694ee 100644 --- a/src/plugins/data/common/field_formats/converters/date_nanos.ts +++ b/src/plugins/data/common/field_formats/converters/date_nanos.ts @@ -21,7 +21,7 @@ import moment, { Moment } from 'moment'; import { memoize, noop } from 'lodash'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert } from '../types'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; /** * Analyse the given moment.js format pattern for the fractional sec part (S,SS,SSS...) @@ -69,26 +69,19 @@ export function formatWithNanos( } export class DateNanosFormat extends FieldFormat { - static id = 'date_nanos'; + static id = FIELD_FORMAT_IDS.DATE_NANOS; static title = 'Date Nanos'; static fieldType = KBN_FIELD_TYPES.DATE; - private getConfig: Function; private memoizedConverter: Function = noop; private memoizedPattern: string = ''; private timeZone: string = ''; - constructor(params: Record, getConfig: Function) { - super(params); - - this.getConfig = getConfig; - } - getParamDefaults() { return { - pattern: this.getConfig('dateNanosFormat'), - fallbackPattern: this.getConfig('dateFormat'), - timezone: this.getConfig('dateFormat:tz'), + pattern: this.getConfig!('dateNanosFormat'), + fallbackPattern: this.getConfig!('dateFormat'), + timezone: this.getConfig!('dateFormat:tz'), }; } diff --git a/src/plugins/data/common/field_formats/converters/date_server.ts b/src/plugins/data/common/field_formats/converters/date_server.ts index 7ed2745a256c42..0c214e424f1632 100644 --- a/src/plugins/data/common/field_formats/converters/date_server.ts +++ b/src/plugins/data/common/field_formats/converters/date_server.ts @@ -21,22 +21,20 @@ import { memoize, noop } from 'lodash'; import moment from 'moment-timezone'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert } from '../types'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class DateFormat extends FieldFormat { - static id = 'date'; + static id = FIELD_FORMAT_IDS.DATE; static title = 'Date'; static fieldType = KBN_FIELD_TYPES.DATE; - private getConfig: Function; private memoizedConverter: Function = noop; private memoizedPattern: string = ''; private timeZone: string = ''; constructor(params: Record, getConfig: Function) { - super(params); + super(params, getConfig); - this.getConfig = getConfig; this.memoizedConverter = memoize((val: any) => { if (val == null) { return '-'; @@ -66,8 +64,8 @@ export class DateFormat extends FieldFormat { getParamDefaults() { return { - pattern: this.getConfig('dateFormat'), - timezone: this.getConfig('dateFormat:tz'), + pattern: this.getConfig!('dateFormat'), + timezone: this.getConfig!('dateFormat:tz'), }; } diff --git a/src/plugins/data/common/field_formats/converters/duration.test.ts b/src/plugins/data/common/field_formats/converters/duration.test.ts index b892884475eec8..d6205d54bd702f 100644 --- a/src/plugins/data/common/field_formats/converters/duration.test.ts +++ b/src/plugins/data/common/field_formats/converters/duration.test.ts @@ -142,7 +142,10 @@ describe('Duration Format', () => { test(`should format ${input} ${inputFormat} through ${outputFormat}${ outputPrecision ? `, ${outputPrecision} decimals` : '' }`, () => { - const duration = new DurationFormat({ inputFormat, outputFormat, outputPrecision }); + const duration = new DurationFormat( + { inputFormat, outputFormat, outputPrecision }, + jest.fn() + ); expect(duration.convert(input)).toBe(output); }); }); diff --git a/src/plugins/data/common/field_formats/converters/duration.ts b/src/plugins/data/common/field_formats/converters/duration.ts index 5eed523214ab3f..d02de1a2fd8893 100644 --- a/src/plugins/data/common/field_formats/converters/duration.ts +++ b/src/plugins/data/common/field_formats/converters/duration.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import moment, { unitOfTime, Duration } from 'moment'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert } from '../types'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; const ratioToSeconds: Record = { picoseconds: 0.000000000001, @@ -166,7 +166,7 @@ function parseInputAsDuration(val: number, inputFormat: string) { } export class DurationFormat extends FieldFormat { - static id = 'duration'; + static id = FIELD_FORMAT_IDS.DURATION; static title = 'Duration'; static fieldType = KBN_FIELD_TYPES.NUMBER; static inputFormats = inputFormats; diff --git a/src/plugins/data/common/field_formats/converters/ip.test.ts b/src/plugins/data/common/field_formats/converters/ip.test.ts index cc42d41adc4a13..a9a02d1a43ea80 100644 --- a/src/plugins/data/common/field_formats/converters/ip.test.ts +++ b/src/plugins/data/common/field_formats/converters/ip.test.ts @@ -23,7 +23,7 @@ describe('IP Address Format', () => { let ip: Record; beforeEach(() => { - ip = new IpFormat(); + ip = new IpFormat({}, jest.fn()); }); test('converts a value from a decimal to a string', () => { diff --git a/src/plugins/data/common/field_formats/converters/ip.ts b/src/plugins/data/common/field_formats/converters/ip.ts index 669f7d1b605d76..3e011e8d7dde8f 100644 --- a/src/plugins/data/common/field_formats/converters/ip.ts +++ b/src/plugins/data/common/field_formats/converters/ip.ts @@ -19,10 +19,10 @@ import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert } from '../types'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class IpFormat extends FieldFormat { - static id = 'ip'; + static id = FIELD_FORMAT_IDS.IP; static title = 'IP Address'; static fieldType = KBN_FIELD_TYPES.IP; diff --git a/src/plugins/data/common/field_formats/converters/number.ts b/src/plugins/data/common/field_formats/converters/number.ts index e0c22f5350716f..6969c1551e1ccb 100644 --- a/src/plugins/data/common/field_formats/converters/number.ts +++ b/src/plugins/data/common/field_formats/converters/number.ts @@ -18,9 +18,10 @@ */ import { NumeralFormat } from './numeral'; +import { FIELD_FORMAT_IDS } from '../types'; export class NumberFormat extends NumeralFormat { - static id = 'number'; + static id = FIELD_FORMAT_IDS.NUMBER; static title = 'Number'; id = NumberFormat.id; diff --git a/src/plugins/data/common/field_formats/converters/numeral.ts b/src/plugins/data/common/field_formats/converters/numeral.ts index f7bf7ddfd1701c..d8e46a480294f4 100644 --- a/src/plugins/data/common/field_formats/converters/numeral.ts +++ b/src/plugins/data/common/field_formats/converters/numeral.ts @@ -37,15 +37,8 @@ export abstract class NumeralFormat extends FieldFormat { abstract id: string; abstract title: string; - protected getConfig: Function; - - constructor(params: Record, getConfig: Function) { - super(params); - this.getConfig = getConfig; - } - getParamDefaults = () => ({ - pattern: this.getConfig(`format:${this.id}:defaultPattern`), + pattern: this.getConfig!(`format:${this.id}:defaultPattern`), }); protected getConvertedValue(val: any): string { diff --git a/src/plugins/data/common/field_formats/converters/percent.ts b/src/plugins/data/common/field_formats/converters/percent.ts index f810f123773623..2ae32c7c77f076 100644 --- a/src/plugins/data/common/field_formats/converters/percent.ts +++ b/src/plugins/data/common/field_formats/converters/percent.ts @@ -18,17 +18,17 @@ */ import { NumeralFormat } from './numeral'; -import { TextContextTypeConvert } from '../types'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class PercentFormat extends NumeralFormat { - static id = 'percent'; + static id = FIELD_FORMAT_IDS.PERCENT; static title = 'Percentage'; id = PercentFormat.id; title = PercentFormat.title; getParamDefaults = () => ({ - pattern: this.getConfig('format:percent:defaultPattern'), + pattern: this.getConfig!('format:percent:defaultPattern'), fractional: true, }); diff --git a/src/plugins/data/common/field_formats/converters/relative_date.test.ts b/src/plugins/data/common/field_formats/converters/relative_date.test.ts index bde5aec0a5ab51..6311402a34b462 100644 --- a/src/plugins/data/common/field_formats/converters/relative_date.test.ts +++ b/src/plugins/data/common/field_formats/converters/relative_date.test.ts @@ -24,7 +24,7 @@ describe('Relative Date Format', () => { let convert: Function; beforeEach(() => { - const relativeDate = new RelativeDateFormat({}); + const relativeDate = new RelativeDateFormat({}, jest.fn()); convert = relativeDate.convert.bind(relativeDate); }); diff --git a/src/plugins/data/common/field_formats/converters/relative_date.ts b/src/plugins/data/common/field_formats/converters/relative_date.ts index caab8c3a2d7da7..273b2cef28a030 100644 --- a/src/plugins/data/common/field_formats/converters/relative_date.ts +++ b/src/plugins/data/common/field_formats/converters/relative_date.ts @@ -20,17 +20,13 @@ import moment from 'moment'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert } from '../types'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class RelativeDateFormat extends FieldFormat { - static id = 'relative_date'; + static id = FIELD_FORMAT_IDS.RELATIVE_DATE; static title = 'Relative Date'; static fieldType = KBN_FIELD_TYPES.DATE; - constructor(params: Record) { - super(params); - } - textConvert: TextContextTypeConvert = val => { if (val === null || val === undefined) { return '-'; diff --git a/src/plugins/data/common/field_formats/converters/source.ts b/src/plugins/data/common/field_formats/converters/source.ts index 35eb14ca59ebb2..54977c7e669761 100644 --- a/src/plugins/data/common/field_formats/converters/source.ts +++ b/src/plugins/data/common/field_formats/converters/source.ts @@ -24,7 +24,7 @@ import { noWhiteSpace } from '../../../../../legacy/core_plugins/kibana/common/u import { shortenDottedString } from '../../../../../legacy/core_plugins/kibana/common/utils/shorten_dotted_string'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert, HtmlContextTypeConvert } from '../types'; +import { TextContextTypeConvert, HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; const templateHtml = `
@@ -37,18 +37,10 @@ const templateHtml = ` const doTemplate = template(noWhiteSpace(templateHtml)); export class SourceFormat extends FieldFormat { - static id = '_source'; + static id = FIELD_FORMAT_IDS._SOURCE; static title = '_source'; static fieldType = KBN_FIELD_TYPES._SOURCE; - private getConfig: Function; - - constructor(params: Record, getConfig: Function) { - super(params); - - this.getConfig = getConfig; - } - textConvert: TextContextTypeConvert = value => JSON.stringify(value); htmlConvert: HtmlContextTypeConvert = (value, field, hit) => { @@ -62,7 +54,7 @@ export class SourceFormat extends FieldFormat { const formatted = field.indexPattern.formatHit(hit); const highlightPairs: any[] = []; const sourcePairs: any[] = []; - const isShortDots = this.getConfig('shortDots:enable'); + const isShortDots = this.getConfig!('shortDots:enable'); keys(formatted).forEach(key => { const pairs = highlights[key] ? highlightPairs : sourcePairs; diff --git a/src/plugins/data/common/field_formats/converters/static_lookup.ts b/src/plugins/data/common/field_formats/converters/static_lookup.ts index 29c64fd4f40462..419e7c786640bf 100644 --- a/src/plugins/data/common/field_formats/converters/static_lookup.ts +++ b/src/plugins/data/common/field_formats/converters/static_lookup.ts @@ -19,7 +19,7 @@ import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert } from '../types'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; function convertLookupEntriesToMap(lookupEntries: any[]) { return lookupEntries.reduce( @@ -32,7 +32,7 @@ function convertLookupEntriesToMap(lookupEntries: any[]) { } export class StaticLookupFormat extends FieldFormat { - static id = 'static_lookup'; + static id = FIELD_FORMAT_IDS.STATIC_LOOKUP; static title = 'Static Lookup'; static fieldType = [ KBN_FIELD_TYPES.STRING, diff --git a/src/plugins/data/common/field_formats/converters/string.test.ts b/src/plugins/data/common/field_formats/converters/string.test.ts index bb0fd5cc011b67..b2d96ed5c03080 100644 --- a/src/plugins/data/common/field_formats/converters/string.test.ts +++ b/src/plugins/data/common/field_formats/converters/string.test.ts @@ -21,30 +21,42 @@ import { StringFormat } from './string'; describe('String Format', () => { test('convert a string to lower case', () => { - const string = new StringFormat({ - transform: 'lower', - }); + const string = new StringFormat( + { + transform: 'lower', + }, + jest.fn() + ); expect(string.convert('Kibana')).toBe('kibana'); }); test('convert a string to upper case', () => { - const string = new StringFormat({ - transform: 'upper', - }); + const string = new StringFormat( + { + transform: 'upper', + }, + jest.fn() + ); expect(string.convert('Kibana')).toBe('KIBANA'); }); test('decode a base64 string', () => { - const string = new StringFormat({ - transform: 'base64', - }); + const string = new StringFormat( + { + transform: 'base64', + }, + jest.fn() + ); expect(string.convert('Zm9vYmFy')).toBe('foobar'); }); test('convert a string to title case', () => { - const string = new StringFormat({ - transform: 'title', - }); + const string = new StringFormat( + { + transform: 'title', + }, + jest.fn() + ); expect(string.convert('PLEASE DO NOT SHOUT')).toBe('Please Do Not Shout'); expect(string.convert('Mean, variance and standard_deviation.')).toBe( 'Mean, Variance And Standard_deviation.' @@ -53,24 +65,33 @@ describe('String Format', () => { }); test('convert a string to short case', () => { - const string = new StringFormat({ - transform: 'short', - }); + const string = new StringFormat( + { + transform: 'short', + }, + jest.fn() + ); expect(string.convert('dot.notated.string')).toBe('d.n.string'); }); test('convert a string to unknown transform case', () => { - const string = new StringFormat({ - transform: 'unknown_transform', - }); + const string = new StringFormat( + { + transform: 'unknown_transform', + }, + jest.fn() + ); const value = 'test test test'; expect(string.convert(value)).toBe(value); }); test('decode a URL Param string', () => { - const string = new StringFormat({ - transform: 'urlparam', - }); + const string = new StringFormat( + { + transform: 'urlparam', + }, + jest.fn() + ); expect(string.convert('%EC%95%88%EB%85%95%20%ED%82%A4%EB%B0%94%EB%82%98')).toBe('안녕 키바나'); }); }); diff --git a/src/plugins/data/common/field_formats/converters/string.ts b/src/plugins/data/common/field_formats/converters/string.ts index 82547c0b0dee52..0edd219ca60f94 100644 --- a/src/plugins/data/common/field_formats/converters/string.ts +++ b/src/plugins/data/common/field_formats/converters/string.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import { asPrettyString } from '../index'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert } from '../types'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; // @ts-ignore import { shortenDottedString } from '../../../../../legacy/core_plugins/kibana/common/utils/shorten_dotted_string'; @@ -72,7 +72,7 @@ const TRANSFORM_OPTIONS = [ const DEFAULT_TRANSFORM_OPTION = false; export class StringFormat extends FieldFormat { - static id = 'string'; + static id = FIELD_FORMAT_IDS.STRING; static title = 'String'; static fieldType = [ KBN_FIELD_TYPES.NUMBER, diff --git a/src/plugins/data/common/field_formats/converters/truncate.test.ts b/src/plugins/data/common/field_formats/converters/truncate.test.ts index 7de4bdb3dedfe6..472d9673346d7d 100644 --- a/src/plugins/data/common/field_formats/converters/truncate.test.ts +++ b/src/plugins/data/common/field_formats/converters/truncate.test.ts @@ -21,25 +21,25 @@ import { TruncateFormat } from './truncate'; describe('String TruncateFormat', () => { test('truncate large string', () => { - const truncate = new TruncateFormat({ fieldLength: 4 }); + const truncate = new TruncateFormat({ fieldLength: 4 }, jest.fn()); expect(truncate.convert('This is some text')).toBe('This...'); }); test('does not truncate large string when field length is not a string', () => { - const truncate = new TruncateFormat({ fieldLength: 'not number' }); + const truncate = new TruncateFormat({ fieldLength: 'not number' }, jest.fn()); expect(truncate.convert('This is some text')).toBe('This is some text'); }); test('does not truncate large string when field length is null', () => { - const truncate = new TruncateFormat({ fieldLength: null }); + const truncate = new TruncateFormat({ fieldLength: null }, jest.fn()); expect(truncate.convert('This is some text')).toBe('This is some text'); }); test('does not truncate large string when field length larger than the text', () => { - const truncate = new TruncateFormat({ fieldLength: 100000 }); + const truncate = new TruncateFormat({ fieldLength: 100000 }, jest.fn()); expect(truncate.convert('This is some text')).toBe('This is some text'); }); diff --git a/src/plugins/data/common/field_formats/converters/truncate.ts b/src/plugins/data/common/field_formats/converters/truncate.ts index acccf2a20c69a9..dc25d71ec95d73 100644 --- a/src/plugins/data/common/field_formats/converters/truncate.ts +++ b/src/plugins/data/common/field_formats/converters/truncate.ts @@ -20,12 +20,12 @@ import { trunc } from 'lodash'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert } from '../types'; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; const omission = '...'; export class TruncateFormat extends FieldFormat { - static id = 'truncate'; + static id = FIELD_FORMAT_IDS.TRUNCATE; static title = 'Truncated String'; static fieldType = KBN_FIELD_TYPES.STRING; diff --git a/src/plugins/data/common/field_formats/converters/url.ts b/src/plugins/data/common/field_formats/converters/url.ts index 6c00f11a408dc6..bd68dedf38a678 100644 --- a/src/plugins/data/common/field_formats/converters/url.ts +++ b/src/plugins/data/common/field_formats/converters/url.ts @@ -22,7 +22,7 @@ import { escape, memoize } from 'lodash'; import { getHighlightHtml } from '../utils'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert, HtmlContextTypeConvert } from '../types'; +import { TextContextTypeConvert, HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; const templateMatchRE = /{{([\s\S]+?)}}/g; const whitelistUrlSchemes = ['http://', 'https://']; @@ -50,7 +50,7 @@ const URL_TYPES = [ const DEFAULT_URL_TYPE = 'a'; export class UrlFormat extends FieldFormat { - static id = 'url'; + static id = FIELD_FORMAT_IDS.URL; static title = 'Url'; static fieldType = [ KBN_FIELD_TYPES.NUMBER, diff --git a/src/plugins/data/common/field_formats/field_format.test.ts b/src/plugins/data/common/field_formats/field_format.test.ts index d4789f12bdee9d..2229601994496d 100644 --- a/src/plugins/data/common/field_formats/field_format.test.ts +++ b/src/plugins/data/common/field_formats/field_format.test.ts @@ -32,7 +32,7 @@ const getTestFormat = ( textConvert = textConvert; htmlConvert = htmlConvert; - })(_params); + })(_params, jest.fn()); describe('FieldFormat class', () => { describe('params', () => { diff --git a/src/plugins/data/common/field_formats/field_format.ts b/src/plugins/data/common/field_formats/field_format.ts index e99894ca561676..6b5f665c6e20e5 100644 --- a/src/plugins/data/common/field_formats/field_format.ts +++ b/src/plugins/data/common/field_formats/field_format.ts @@ -19,7 +19,12 @@ import { transform, size, cloneDeep, get, defaults } from 'lodash'; import { createCustomFieldFormat } from './converters/custom'; -import { ContentType, FieldFormatConvert, FieldFormatConvertFunction } from './types'; +import { + ContentType, + FIELD_FORMAT_IDS, + FieldFormatConvert, + FieldFormatConvertFunction, +} from './types'; import { htmlContentTypeSetup, textContentTypeSetup, @@ -68,7 +73,16 @@ export abstract class FieldFormat { */ public type: any = this.constructor; - constructor(public _params: any = {}) {} + private readonly _params: any; + protected getConfig: Function | undefined; + + constructor(_params: any = {}, getConfig?: Function) { + this._params = _params; + + if (getConfig) { + this.getConfig = getConfig; + } + } /** * Convert a raw value to a formatted string @@ -170,7 +184,7 @@ export abstract class FieldFormat { }; } - static from(convertFn: FieldFormatConvertFunction): ReturnType { + static from(convertFn: FieldFormatConvertFunction): IFieldFormatType { return createCustomFieldFormat(convertFn); } @@ -183,3 +197,11 @@ export abstract class FieldFormat { } export type IFieldFormat = PublicMethodsOf; +/** + * @string id type is needed for creating custom converters. + */ +export type IFieldFormatId = FIELD_FORMAT_IDS | string; +export type IFieldFormatType = (new (params?: any, getConfig?: Function) => FieldFormat) & { + id: IFieldFormatId; + fieldType: string | string[]; +}; diff --git a/src/plugins/data/common/field_formats/index.ts b/src/plugins/data/common/field_formats/index.ts index 5d04a69e4dc50e..b751b097b5ed2c 100644 --- a/src/plugins/data/common/field_formats/index.ts +++ b/src/plugins/data/common/field_formats/index.ts @@ -18,7 +18,7 @@ */ export { HTML_CONTEXT_TYPE, TEXT_CONTEXT_TYPE } from './content_types'; -export { FieldFormat } from './field_format'; +export { FieldFormat, IFieldFormatType, IFieldFormatId } from './field_format'; export { getHighlightRequest, asPrettyString, getHighlightHtml } from './utils'; export * from './converters'; export * from './constants'; diff --git a/src/plugins/data/common/field_formats/types.ts b/src/plugins/data/common/field_formats/types.ts index 626bab297392b4..fc8e6e20a1a968 100644 --- a/src/plugins/data/common/field_formats/types.ts +++ b/src/plugins/data/common/field_formats/types.ts @@ -42,3 +42,23 @@ export interface FieldFormatConvert { text: TextContextTypeConvert; html: HtmlContextTypeConvert; } + +/** @public **/ +export enum FIELD_FORMAT_IDS { + _SOURCE = '_source', + BOOLEAN = 'boolean', + BYTES = 'bytes', + COLOR = 'color', + CUSTOM = 'custom', + DATE = 'date', + DATE_NANOS = 'date_nanos', + DURATION = 'duration', + IP = 'ip', + NUMBER = 'number', + PERCENT = 'percent', + RELATIVE_DATE = 'relative_date', + STATIC_LOOKUP = 'static_lookup', + STRING = 'string', + TRUNCATE = 'truncate', + URL = 'url', +} diff --git a/src/plugins/data/common/kbn_field_types/types.ts b/src/plugins/data/common/kbn_field_types/types.ts index 21d58bcd0f78c8..11c62e8f86dce3 100644 --- a/src/plugins/data/common/kbn_field_types/types.ts +++ b/src/plugins/data/common/kbn_field_types/types.ts @@ -27,9 +27,10 @@ export interface KbnFieldTypeOptions { /** @public **/ export enum ES_FIELD_TYPES { - _TYPE = '_type', _ID = '_id', + _INDEX = '_index', _SOURCE = '_source', + _TYPE = '_type', STRING = 'string', TEXT = 'text', diff --git a/src/plugins/data/public/field_formats_provider/field_formats.ts b/src/plugins/data/public/field_formats_provider/field_formats.ts new file mode 100644 index 00000000000000..f46994c209dedc --- /dev/null +++ b/src/plugins/data/public/field_formats_provider/field_formats.ts @@ -0,0 +1,226 @@ +/* + * 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 { forOwn, isFunction, memoize } from 'lodash'; +import { UiSettingsClientContract } from 'kibana/public'; +import { + ES_FIELD_TYPES, + KBN_FIELD_TYPES, + FIELD_FORMAT_IDS, + IFieldFormatType, + IFieldFormatId, + FieldFormat, +} from '../../common'; +import { FieldType } from './types'; + +export class FieldFormatRegisty { + private fieldFormats: Map; + private uiSettings!: UiSettingsClientContract; + private defaultMap: Record; + + constructor() { + this.fieldFormats = new Map(); + this.defaultMap = {}; + } + + getConfig = (key: string, override?: any) => this.uiSettings.get(key, override); + + init(uiSettings: UiSettingsClientContract) { + this.uiSettings = uiSettings; + + this.parseDefaultTypeMap(this.uiSettings.get('format:defaultTypeMap')); + + this.uiSettings.getUpdate$().subscribe(({ key, newValue }) => { + if (key === 'format:defaultTypeMap') { + this.parseDefaultTypeMap(newValue); + } + }); + } + + /** + * Get the id of the default type for this field type + * using the format:defaultTypeMap config map + * + * @param {KBN_FIELD_TYPES} fieldType - the field type + * @param {ES_FIELD_TYPES[]} esTypes - Array of ES data types + * @return {FieldType} + */ + getDefaultConfig = (fieldType: KBN_FIELD_TYPES, esTypes?: ES_FIELD_TYPES[]): FieldType => { + const type = this.getDefaultTypeName(fieldType, esTypes); + + return ( + (this.defaultMap && this.defaultMap[type]) || { id: FIELD_FORMAT_IDS.STRING, params: {} } + ); + }; + + /** + * Get a derived FieldFormat class by its id. + * + * @param {IFieldFormatId} formatId - the format id + * @return {FieldFormat} + */ + getType = (formatId: IFieldFormatId): IFieldFormatType | undefined => { + return this.fieldFormats.get(formatId); + }; + + /** + * Get the default FieldFormat type (class) for + * a field type, using the format:defaultTypeMap. + * used by the field editor + * + * @param {KBN_FIELD_TYPES} fieldType + * @param {ES_FIELD_TYPES[]} esTypes - Array of ES data types + * @return {FieldFormat} + */ + getDefaultType = ( + fieldType: KBN_FIELD_TYPES, + esTypes: ES_FIELD_TYPES[] + ): IFieldFormatType | undefined => { + const config = this.getDefaultConfig(fieldType, esTypes); + + return this.getType(config.id); + }; + + /** + * Get the name of the default type for ES types like date_nanos + * using the format:defaultTypeMap config map + * + * @param {ES_FIELD_TYPES[]} esTypes - Array of ES data types + * @return {ES_FIELD_TYPES} + */ + getTypeNameByEsTypes = (esTypes: ES_FIELD_TYPES[] | undefined): ES_FIELD_TYPES | undefined => { + if (!Array.isArray(esTypes)) { + return; + } + + return esTypes.find(type => this.defaultMap[type] && this.defaultMap[type].es); + }; + + /** + * Get the default FieldFormat type name for + * a field type, using the format:defaultTypeMap. + * + * @param {KBN_FIELD_TYPES} fieldType + * @param {ES_FIELD_TYPES[]} esTypes + * @return {ES_FIELD_TYPES | String} + */ + getDefaultTypeName = ( + fieldType: KBN_FIELD_TYPES, + esTypes?: ES_FIELD_TYPES[] + ): ES_FIELD_TYPES | KBN_FIELD_TYPES => { + const esType = this.getTypeNameByEsTypes(esTypes); + + return esType || fieldType; + }; + + /** + * Get the singleton instance of the FieldFormat type by its id. + * + * @param {IFieldFormatId} formatId + * @return {FIELD_FORMATS_INSTANCES[number]} + */ + getInstance = memoize( + (formatId: IFieldFormatId): FieldFormat => { + const DerivedFieldFormat = this.getType(formatId); + + if (!DerivedFieldFormat) { + throw new Error(`Field Format '${formatId}' not found!`); + } + + return new DerivedFieldFormat({}, this.getConfig); + } + ); + + /** + * Get the default fieldFormat instance for a field format. + * + * @param {KBN_FIELD_TYPES} fieldType + * @param {ES_FIELD_TYPES[]} esTypes + * @return {FieldFormat} + */ + getDefaultInstancePlain(fieldType: KBN_FIELD_TYPES, esTypes?: ES_FIELD_TYPES[]): FieldFormat { + const conf = this.getDefaultConfig(fieldType, esTypes); + + const DerivedFieldFormat = this.getType(conf.id); + + if (!DerivedFieldFormat) { + throw new Error(`Field Format '${conf.id}' not found!`); + } + + return new DerivedFieldFormat(conf.params, this.getConfig); + } + /** + * Returns a cache key built by the given variables for caching in memoized + * Where esType contains fieldType, fieldType is returned + * -> kibana types have a higher priority in that case + * -> would lead to failing tests that match e.g. date format with/without esTypes + * https://lodash.com/docs#memoize + * + * @param {KBN_FIELD_TYPES} fieldType + * @param {ES_FIELD_TYPES[]} esTypes + * @return {String} + */ + getDefaultInstanceCacheResolver(fieldType: KBN_FIELD_TYPES, esTypes: ES_FIELD_TYPES[]): string { + // @ts-ignore + return Array.isArray(esTypes) && esTypes.indexOf(fieldType) === -1 + ? [fieldType, ...esTypes].join('-') + : fieldType; + } + + /** + * Get filtered list of field formats by format type + * + * @param {KBN_FIELD_TYPES} fieldType + * @return {FieldFormat[]} + */ + getByFieldType(fieldType: KBN_FIELD_TYPES): IFieldFormatType[] { + return [...this.fieldFormats.values()].filter( + (format: IFieldFormatType) => format.fieldType.indexOf(fieldType) !== -1 + ); + } + + /** + * Get the default fieldFormat instance for a field format. + * It's a memoized function that builds and reads a cache + * + * @param {KBN_FIELD_TYPES} fieldType + * @param {ES_FIELD_TYPES[]} esTypes + * @return {FieldFormat} + */ + getDefaultInstance = memoize(this.getDefaultInstancePlain, this.getDefaultInstanceCacheResolver); + + parseDefaultTypeMap(value: any) { + this.defaultMap = value; + forOwn(this, fn => { + if (isFunction(fn) && fn.cache) { + // clear all memoize caches + // @ts-ignore + fn.cache = new memoize.Cache(); + } + }); + } + + register = (fieldFormats: IFieldFormatType[]) => { + fieldFormats.forEach(fieldFormat => { + this.fieldFormats.set(fieldFormat.id, fieldFormat); + }); + + return this; + }; +} diff --git a/src/plugins/data/public/field_formats_provider/field_formats_service.ts b/src/plugins/data/public/field_formats_provider/field_formats_service.ts new file mode 100644 index 00000000000000..b144ea7ec25304 --- /dev/null +++ b/src/plugins/data/public/field_formats_provider/field_formats_service.ts @@ -0,0 +1,87 @@ +/* + * 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 { UiSettingsClientContract } from 'src/core/public'; +import { FieldFormatRegisty } from './field_formats'; + +import { + BoolFormat, + BytesFormat, + ColorFormat, + DateFormat, + DateNanosFormat, + DurationFormat, + IpFormat, + NumberFormat, + PercentFormat, + RelativeDateFormat, + SourceFormat, + StaticLookupFormat, + StringFormat, + TruncateFormat, + UrlFormat, +} from '../../common/'; + +/** + * Field Format Service + * @internal + */ +interface FieldFormatsServiceDependencies { + uiSettings: UiSettingsClientContract; +} + +export class FieldFormatsService { + private readonly fieldFormats: FieldFormatRegisty = new FieldFormatRegisty(); + + public setup({ uiSettings }: FieldFormatsServiceDependencies) { + this.fieldFormats.init(uiSettings); + + this.fieldFormats.register([ + BoolFormat, + BytesFormat, + ColorFormat, + DateFormat, + DateNanosFormat, + DurationFormat, + IpFormat, + NumberFormat, + PercentFormat, + RelativeDateFormat, + SourceFormat, + StaticLookupFormat, + StringFormat, + TruncateFormat, + UrlFormat, + ]); + + return this.fieldFormats as FieldFormatsSetup; + } + + public start() { + return this.fieldFormats as FieldFormatsStart; + } + + public stop() { + // nothing to do here yet + } +} + +/** @public */ +export type FieldFormatsSetup = Omit; +export type FieldFormatsStart = Omit; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/help_menu/help_menu.js b/src/plugins/data/public/field_formats_provider/index.ts similarity index 51% rename from src/legacy/core_plugins/kibana/public/dashboard/help_menu/help_menu.js rename to src/plugins/data/public/field_formats_provider/index.ts index 56b2bd253381c7..442d877c5316a7 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/help_menu/help_menu.js +++ b/src/plugins/data/public/field_formats_provider/index.ts @@ -17,26 +17,5 @@ * under the License. */ -import React, { Fragment, PureComponent } from 'react'; -import { EuiButton, EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; - -export class HelpMenu extends PureComponent { - render() { - return ( - - - - - - - - ); - } -} +export { FieldFormatRegisty } from './field_formats'; // TODO: Try to remove +export { FieldFormatsService, FieldFormatsSetup, FieldFormatsStart } from './field_formats_service'; diff --git a/src/legacy/ui/public/capabilities/route_setup.ts b/src/plugins/data/public/field_formats_provider/types.ts similarity index 58% rename from src/legacy/ui/public/capabilities/route_setup.ts rename to src/plugins/data/public/field_formats_provider/types.ts index c7817b8cc5748c..fc33bf4d38f850 100644 --- a/src/legacy/ui/public/capabilities/route_setup.ts +++ b/src/plugins/data/public/field_formats_provider/types.ts @@ -17,22 +17,10 @@ * under the License. */ -import { get } from 'lodash'; -import chrome from 'ui/chrome'; -import uiRoutes from 'ui/routes'; -import { UICapabilities } from '.'; +import { IFieldFormatId } from '../../common'; -uiRoutes.addSetupWork( - (uiCapabilities: UICapabilities, kbnBaseUrl: string, $route: any, kbnUrl: any) => { - const route = get($route, 'current.$$route') as any; - if (!route.requireUICapability) { - return; - } - - if (!get(uiCapabilities, route.requireUICapability)) { - const url = chrome.addBasePath(`${kbnBaseUrl}#/home`); - kbnUrl.redirect(url); - throw uiRoutes.WAIT_FOR_URL_CHANGE_TOKEN; - } - } -); +export interface FieldType { + id: IFieldFormatId; + params: Record; + es?: boolean; +} diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 4477c6defbc81b..6a2df6a61d1366 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -29,6 +29,8 @@ export { DataPublicPlugin as Plugin }; export * from '../common'; export * from './autocomplete_provider'; +export * from './field_formats_provider'; + export * from './types'; export { IRequestTypesMap, IResponseTypesMap } from './search'; diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 4aae63c24d7fc7..ff5c96c2d89edb 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { Plugin } from '.'; +import { FieldFormatRegisty, Plugin, FieldFormatsStart, FieldFormatsSetup } from '.'; import { searchSetupMock } from './search/mocks'; import { queryServiceMock } from './query/mocks'; @@ -29,11 +29,29 @@ const autocompleteMock: any = { clearProviders: jest.fn(), }; +const fieldFormatsMock: PublicMethodsOf = { + getByFieldType: jest.fn(), + getConfig: jest.fn(), + getDefaultConfig: jest.fn(), + getDefaultInstance: jest.fn() as any, + getDefaultInstanceCacheResolver: jest.fn(), + getDefaultInstancePlain: jest.fn(), + getDefaultType: jest.fn(), + getDefaultTypeName: jest.fn(), + getInstance: jest.fn() as any, + getType: jest.fn(), + getTypeNameByEsTypes: jest.fn(), + init: jest.fn(), + register: jest.fn(), + parseDefaultTypeMap: jest.fn(), +}; + const createSetupContract = (): Setup => { const querySetupMock = queryServiceMock.createSetupContract(); const setupContract = { autocomplete: autocompleteMock, search: searchSetupMock, + fieldFormats: fieldFormatsMock as FieldFormatsSetup, query: querySetupMock, }; @@ -46,6 +64,7 @@ const createStartContract = (): Start => { autocomplete: autocompleteMock, getSuggestions: jest.fn(), search: { search: jest.fn() }, + fieldFormats: fieldFormatsMock as FieldFormatsStart, query: queryStartMock, }; return startContract; diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index 79db34c022b39f..3aa9cd9a0bcb44 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -17,29 +17,34 @@ * under the License. */ -import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { Storage } from '../../kibana_utils/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from './types'; import { AutocompleteProviderRegister } from './autocomplete_provider'; import { getSuggestionsProvider } from './suggestions_provider'; import { SearchService } from './search/search_service'; +import { FieldFormatsService } from './field_formats_provider'; import { QueryService } from './query'; export class DataPublicPlugin implements Plugin { private readonly autocomplete = new AutocompleteProviderRegister(); private readonly searchService: SearchService; + private readonly fieldFormatsService: FieldFormatsService; private readonly queryService: QueryService; constructor(initializerContext: PluginInitializerContext) { this.searchService = new SearchService(initializerContext); this.queryService = new QueryService(); + this.fieldFormatsService = new FieldFormatsService(); } public setup(core: CoreSetup): DataPublicPluginSetup { const storage = new Storage(window.localStorage); + return { autocomplete: this.autocomplete, search: this.searchService.setup(core), + fieldFormats: this.fieldFormatsService.setup(core), query: this.queryService.setup({ uiSettings: core.uiSettings, storage, @@ -52,6 +57,7 @@ export class DataPublicPlugin implements Plugin { + appName: string; + uiSettings: CoreStart['uiSettings']; + savedObjects: CoreStart['savedObjects']; + notifications: CoreStart['notifications']; + http: CoreStart['http']; + storage: IStorageWrapper; + data: DataPublicPluginStart; +} diff --git a/src/legacy/core_plugins/kibana/public/field_formats/register.js b/src/test_utils/public/stub_field_formats.ts similarity index 60% rename from src/legacy/core_plugins/kibana/public/field_formats/register.js rename to src/test_utils/public/stub_field_formats.ts index 9709b56fc8c3c7..39c6fb2f6d10ea 100644 --- a/src/legacy/core_plugins/kibana/public/field_formats/register.js +++ b/src/test_utils/public/stub_field_formats.ts @@ -16,38 +16,49 @@ * specific language governing permissions and limitations * under the License. */ +import { UiSettingsClientContract } from 'kibana/public'; -import { fieldFormats } from 'ui/registry/field_formats'; import { - UrlFormat, - StringFormat, - NumberFormat, + FieldFormatRegisty, + BoolFormat, BytesFormat, - TruncateFormat, - RelativeDateFormat, - PercentFormat, - IpFormat, - DurationFormat, - DateNanosFormat, - DateFormat, ColorFormat, - BoolFormat, + DateFormat, + DateNanosFormat, + DurationFormat, + IpFormat, + NumberFormat, + PercentFormat, + RelativeDateFormat, SourceFormat, - StaticLookupFormat -} from '../../../../../plugins/data/public'; + StaticLookupFormat, + StringFormat, + TruncateFormat, + UrlFormat, +} from '../../plugins/data/public/'; + +export const getFieldFormatsRegistry = (uiSettings: UiSettingsClientContract) => { + const fieldFormats = new FieldFormatRegisty(); + + fieldFormats.register([ + BoolFormat, + BytesFormat, + ColorFormat, + DateFormat, + DateNanosFormat, + DurationFormat, + IpFormat, + NumberFormat, + PercentFormat, + RelativeDateFormat, + SourceFormat, + StaticLookupFormat, + StringFormat, + TruncateFormat, + UrlFormat, + ]); + + fieldFormats.init(uiSettings); -fieldFormats.register(UrlFormat); -fieldFormats.register(BytesFormat); -fieldFormats.register(DateFormat); -fieldFormats.register(DateNanosFormat); -fieldFormats.register(RelativeDateFormat); -fieldFormats.register(DurationFormat); -fieldFormats.register(IpFormat); -fieldFormats.register(NumberFormat); -fieldFormats.register(PercentFormat); -fieldFormats.register(StringFormat); -fieldFormats.register(SourceFormat); -fieldFormats.register(ColorFormat); -fieldFormats.register(TruncateFormat); -fieldFormats.register(BoolFormat); -fieldFormats.register(StaticLookupFormat); + return fieldFormats; +}; diff --git a/src/test_utils/public/stub_index_pattern.js b/src/test_utils/public/stub_index_pattern.js index a6a0eb386d32ec..b41ebe3e618615 100644 --- a/src/test_utils/public/stub_index_pattern.js +++ b/src/test_utils/public/stub_index_pattern.js @@ -28,9 +28,15 @@ import { formatHitProvider, flattenHitWrapper, } from 'ui/index_patterns'; -import { fieldFormats } from 'ui/registry/field_formats'; +import { + FIELD_FORMAT_IDS, +} from '../../plugins/data/public'; + +import { getFieldFormatsRegistry } from './stub_field_formats'; + +export default function StubIndexPattern(pattern, getConfig, timeField, fields, uiSettings) { + const registeredFieldFormats = getFieldFormatsRegistry(uiSettings); -export default function StubIndexPattern(pattern, getConfig, timeField, fields) { this.id = pattern; this.title = pattern; this.popularizeField = sinon.stub(); @@ -47,7 +53,7 @@ export default function StubIndexPattern(pattern, getConfig, timeField, fields) this.getComputedFields = IndexPattern.prototype.getComputedFields.bind(this); this.flattenHit = flattenHitWrapper(this, this.metaFields); - this.formatHit = formatHitProvider(this, fieldFormats.getDefaultInstance('string')); + this.formatHit = formatHitProvider(this, registeredFieldFormats.getDefaultInstance(FIELD_FORMAT_IDS.STRING)); this.fieldsFetcher = { apiClient: { baseUrl: '' } }; this.formatField = this.formatHit.formatField; @@ -56,7 +62,7 @@ export default function StubIndexPattern(pattern, getConfig, timeField, fields) }; this.stubSetFieldFormat = function (fieldName, id, params) { - const FieldFormat = fieldFormats.getType(id); + const FieldFormat = registeredFieldFormats.getType(id); this.fieldFormatMap[fieldName] = new FieldFormat(params); this._reindexFields(); }; diff --git a/test/accessibility/apps/discover.ts b/test/accessibility/apps/discover.ts index e3f73ad4bcaf86..38ee5b7db39c47 100644 --- a/test/accessibility/apps/discover.ts +++ b/test/accessibility/apps/discover.ts @@ -19,9 +19,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; -const FROM_TIME = '2015-09-19 06:31:44.000'; -const TO_TIME = '2015-09-23 18:31:44.000'; - export default function({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'timePicker']); const a11y = getService('a11y'); @@ -36,7 +33,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { defaultIndex: 'logstash-*', }); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(FROM_TIME, TO_TIME); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); it('main view', async () => { diff --git a/test/api_integration/apis/home/sample_data.js b/test/api_integration/apis/home/sample_data.js index 7ca66e87a93254..042f490768af09 100644 --- a/test/api_integration/apis/home/sample_data.js +++ b/test/api_integration/apis/home/sample_data.js @@ -22,7 +22,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const MILLISECOND_IN_WEEK = 1000 * 60 * 60 * 24 * 7; diff --git a/test/api_integration/apis/index_patterns/es_errors/errors.js b/test/api_integration/apis/index_patterns/es_errors/errors.js index 4e50b965211c29..77e024c9d20ccb 100644 --- a/test/api_integration/apis/index_patterns/es_errors/errors.js +++ b/test/api_integration/apis/index_patterns/es_errors/errors.js @@ -34,7 +34,7 @@ import { } from './lib'; export default function ({ getService }) { - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); describe('index_patterns/* error handler', () => { diff --git a/test/api_integration/apis/kql_telemetry/kql_telemetry.js b/test/api_integration/apis/kql_telemetry/kql_telemetry.js index 87d06ee9458d63..25a68bb4bb2b64 100644 --- a/test/api_integration/apis/kql_telemetry/kql_telemetry.js +++ b/test/api_integration/apis/kql_telemetry/kql_telemetry.js @@ -24,7 +24,7 @@ import { get } from 'lodash'; export default function ({ getService }) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); describe('telemetry API', () => { before(() => esArchiver.load('saved_objects/basic')); diff --git a/test/api_integration/apis/management/saved_objects/find.js b/test/api_integration/apis/management/saved_objects/find.js index 0e7cf08fa5c266..6bb3c0cebddbfc 100644 --- a/test/api_integration/apis/management/saved_objects/find.js +++ b/test/api_integration/apis/management/saved_objects/find.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { - const es = getService('es'); + const es = getService('legacyEs'); const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); diff --git a/test/api_integration/apis/saved_objects/bulk_create.js b/test/api_integration/apis/saved_objects/bulk_create.js index e77e08d949f2bf..3a520d369120d6 100644 --- a/test/api_integration/apis/saved_objects/bulk_create.js +++ b/test/api_integration/apis/saved_objects/bulk_create.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); const BULK_REQUESTS = [ diff --git a/test/api_integration/apis/saved_objects/bulk_get.js b/test/api_integration/apis/saved_objects/bulk_get.js index 8c5b589a12a2e4..52733aa70200b2 100644 --- a/test/api_integration/apis/saved_objects/bulk_get.js +++ b/test/api_integration/apis/saved_objects/bulk_get.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); const BULK_REQUESTS = [ diff --git a/test/api_integration/apis/saved_objects/bulk_update.js b/test/api_integration/apis/saved_objects/bulk_update.js index 4bdf257ceef024..b38934aecd0fbf 100644 --- a/test/api_integration/apis/saved_objects/bulk_update.js +++ b/test/api_integration/apis/saved_objects/bulk_update.js @@ -22,7 +22,7 @@ import _ from 'lodash'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); diff --git a/test/api_integration/apis/saved_objects/create.js b/test/api_integration/apis/saved_objects/create.js index eefd9e8ab1a95c..363aa9d30c08d1 100644 --- a/test/api_integration/apis/saved_objects/create.js +++ b/test/api_integration/apis/saved_objects/create.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); describe('create', () => { diff --git a/test/api_integration/apis/saved_objects/delete.js b/test/api_integration/apis/saved_objects/delete.js index a9037bf697406b..8aa76e99312b05 100644 --- a/test/api_integration/apis/saved_objects/delete.js +++ b/test/api_integration/apis/saved_objects/delete.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); describe('delete', () => { diff --git a/test/api_integration/apis/saved_objects/export.js b/test/api_integration/apis/saved_objects/export.js index 9ab7a09309952a..59c9afe24b8025 100644 --- a/test/api_integration/apis/saved_objects/export.js +++ b/test/api_integration/apis/saved_objects/export.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); describe('export', () => { diff --git a/test/api_integration/apis/saved_objects/find.js b/test/api_integration/apis/saved_objects/find.js index 7b2b15d298ce02..e1bec19cf8e6ae 100644 --- a/test/api_integration/apis/saved_objects/find.js +++ b/test/api_integration/apis/saved_objects/find.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); describe('find', () => { diff --git a/test/api_integration/apis/saved_objects/get.js b/test/api_integration/apis/saved_objects/get.js index fb4b6f91b8bb81..9034bc5d84a721 100644 --- a/test/api_integration/apis/saved_objects/get.js +++ b/test/api_integration/apis/saved_objects/get.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); describe('get', () => { diff --git a/test/api_integration/apis/saved_objects/migrations.js b/test/api_integration/apis/saved_objects/migrations.js index 5c492951ec9384..c4b258d47e24b4 100644 --- a/test/api_integration/apis/saved_objects/migrations.js +++ b/test/api_integration/apis/saved_objects/migrations.js @@ -31,7 +31,7 @@ import { SavedObjectsSerializer } from '../../../../src/core/server/saved_object import { SavedObjectsSchema } from '../../../../src/core/server/saved_objects/schema'; export default ({ getService }) => { - const es = getService('es'); + const es = getService('legacyEs'); const callCluster = (path, ...args) => _.get(es, path).call(es, ...args); describe('Kibana index migration', () => { diff --git a/test/api_integration/apis/saved_objects/update.js b/test/api_integration/apis/saved_objects/update.js index e6ad6b2b781dab..c73ed0ecc6424f 100644 --- a/test/api_integration/apis/saved_objects/update.js +++ b/test/api_integration/apis/saved_objects/update.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); describe('update', () => { diff --git a/test/api_integration/apis/ui_metric/ui_metric.js b/test/api_integration/apis/ui_metric/ui_metric.js index f0c86f2904638c..51959bf5f7fda8 100644 --- a/test/api_integration/apis/ui_metric/ui_metric.js +++ b/test/api_integration/apis/ui_metric/ui_metric.js @@ -22,7 +22,7 @@ import { ReportManager, METRIC_TYPE } from '@kbn/analytics'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const createStatsMetric = (eventName) => ({ key: ReportManager.createMetricKey({ appName: 'myApp', type: METRIC_TYPE.CLICK, eventName }), diff --git a/test/api_integration/services/index.ts b/test/api_integration/services/index.ts index 573450cae534a4..782ea271869ba4 100644 --- a/test/api_integration/services/index.ts +++ b/test/api_integration/services/index.ts @@ -23,10 +23,7 @@ import { services as commonServices } from '../../common/services'; import { KibanaSupertestProvider, ElasticsearchSupertestProvider } from './supertest'; export const services = { - es: commonServices.es, - esArchiver: commonServices.esArchiver, - retry: commonServices.retry, + ...commonServices, supertest: KibanaSupertestProvider, esSupertest: ElasticsearchSupertestProvider, - randomness: commonServices.randomness, }; diff --git a/src/legacy/core_plugins/data/public/types.ts b/test/common/services/elasticsearch.ts similarity index 61% rename from src/legacy/core_plugins/data/public/types.ts rename to test/common/services/elasticsearch.ts index b6c9c47cc0ae69..63c4bfeeb4ce77 100644 --- a/src/legacy/core_plugins/data/public/types.ts +++ b/test/common/services/elasticsearch.ts @@ -17,16 +17,17 @@ * under the License. */ -import { UiSettingsClientContract, CoreStart } from 'src/core/public'; -import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { format as formatUrl } from 'url'; -export interface IDataPluginServices extends Partial { - appName: string; - uiSettings: UiSettingsClientContract; - savedObjects: CoreStart['savedObjects']; - notifications: CoreStart['notifications']; - http: CoreStart['http']; - storage: IStorageWrapper; - data: DataPublicPluginStart; +import { Client } from '@elastic/elasticsearch'; + +import { FtrProviderContext } from '../ftr_provider_context'; + +export function ElasticsearchProvider({ getService }: FtrProviderContext) { + const config = getService('config'); + + return new Client({ + nodes: [formatUrl(config.get('servers.elasticsearch'))], + requestTimeout: config.get('timeouts.esRequestTimeout'), + }); } diff --git a/test/common/services/es_archiver.ts b/test/common/services/es_archiver.ts index e72bb49a76c0dc..cfe0610414b4fc 100644 --- a/test/common/services/es_archiver.ts +++ b/test/common/services/es_archiver.ts @@ -26,7 +26,7 @@ import * as KibanaServer from './kibana_server'; export function EsArchiverProvider({ getService, hasService }: FtrProviderContext): EsArchiver { const config = getService('config'); - const client = getService('es'); + const client = getService('legacyEs'); const log = getService('log'); if (!config.get('esArchiver')) { diff --git a/test/common/services/index.ts b/test/common/services/index.ts index 225aacc1c98958..3454964f35e070 100644 --- a/test/common/services/index.ts +++ b/test/common/services/index.ts @@ -18,13 +18,15 @@ */ import { LegacyEsProvider } from './legacy_es'; +import { ElasticsearchProvider } from './elasticsearch'; import { EsArchiverProvider } from './es_archiver'; import { KibanaServerProvider } from './kibana_server'; import { RetryProvider } from './retry'; import { RandomnessProvider } from './randomness'; export const services = { - es: LegacyEsProvider, + legacyEs: LegacyEsProvider, + es: ElasticsearchProvider, esArchiver: EsArchiverProvider, kibanaServer: KibanaServerProvider, retry: RetryProvider, diff --git a/test/functional/apps/context/_discover_navigation.js b/test/functional/apps/context/_discover_navigation.js index 147d0e74d1c98b..cb328dfa98e3ec 100644 --- a/test/functional/apps/context/_discover_navigation.js +++ b/test/functional/apps/context/_discover_navigation.js @@ -19,8 +19,6 @@ import expect from '@kbn/expect'; -const TEST_DISCOVER_START_TIME = '2015-09-19 06:31:44.000'; -const TEST_DISCOVER_END_TIME = '2015-09-23 18:31:44.000'; const TEST_COLUMN_NAMES = ['@message']; const TEST_FILTER_COLUMN_NAMES = [['extension', 'jpg'], ['geo.src', 'IN']]; @@ -34,7 +32,7 @@ export default function ({ getService, getPageObjects }) { this.tags('smoke'); before(async function () { await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(TEST_DISCOVER_START_TIME, TEST_DISCOVER_END_TIME); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await Promise.all(TEST_COLUMN_NAMES.map((columnName) => ( PageObjects.discover.clickFieldListItemAdd(columnName) ))); diff --git a/test/functional/apps/dashboard/dashboard_time.js b/test/functional/apps/dashboard/dashboard_time.js index 917157e54eee05..39ce43e19d7517 100644 --- a/test/functional/apps/dashboard/dashboard_time.js +++ b/test/functional/apps/dashboard/dashboard_time.js @@ -21,9 +21,6 @@ import expect from '@kbn/expect'; const dashboardName = 'Dashboard Test Time'; -const fromTime = '2015-09-19 06:31:44.000'; -const toTime = '2015-09-23 18:31:44.000'; - export default function ({ getPageObjects, getService }) { const PageObjects = getPageObjects(['dashboard', 'header', 'timePicker']); const browser = getService('browser'); @@ -46,31 +43,31 @@ export default function ({ getPageObjects, getService }) { }); it('Does not set the time picker on open', async () => { - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.dashboard.loadSavedDashboard(dashboardName); const time = await PageObjects.timePicker.getTimeConfig(); - expect(time.start).to.equal('Sep 19, 2015 @ 06:31:44.000'); - expect(time.end).to.equal('Sep 23, 2015 @ 18:31:44.000'); + expect(time.start).to.equal(PageObjects.timePicker.defaultStartTime); + expect(time.end).to.equal(PageObjects.timePicker.defaultEndTime); }); }); describe('dashboard with stored timed', function () { it('is saved with time', async function () { await PageObjects.dashboard.switchToEditMode(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.dashboard.saveDashboard(dashboardName, { storeTimeWithDashboard: true }); }); it('sets time on open', async function () { - await PageObjects.timePicker.setAbsoluteRange('2019-01-01 00:00:00.000', '2019-01-02 00:00:00.000'); + await PageObjects.timePicker.setAbsoluteRange('Jan 1, 2019 @ 00:00:00.000', 'Jan 2, 2019 @ 00:00:00.000'); await PageObjects.dashboard.loadSavedDashboard(dashboardName); const time = await PageObjects.timePicker.getTimeConfig(); - expect(time.start).to.equal('Sep 19, 2015 @ 06:31:44.000'); - expect(time.end).to.equal('Sep 23, 2015 @ 18:31:44.000'); + expect(time.start).to.equal(PageObjects.timePicker.defaultStartTime); + expect(time.end).to.equal(PageObjects.timePicker.defaultEndTime); }); // If time is stored with a dashboard, it's supposed to override the current time settings when opened. @@ -99,7 +96,7 @@ export default function ({ getPageObjects, getService }) { it('preserved during navigation', async function () { await PageObjects.dashboard.loadSavedDashboard(dashboardName); - await PageObjects.timePicker.setAbsoluteRange('2019-01-01 00:00:00.000', '2019-01-02 00:00:00.000'); + await PageObjects.timePicker.setAbsoluteRange('Jan 1, 2019 @ 00:00:00.000', 'Jan 2, 2019 @ 00:00:00.000'); await PageObjects.header.clickVisualize(); await PageObjects.header.clickDashboard(); diff --git a/test/functional/apps/dashboard/dashboard_time_picker.js b/test/functional/apps/dashboard/dashboard_time_picker.js index f09f3fc1de2ad6..ab1d1ff74168a9 100644 --- a/test/functional/apps/dashboard/dashboard_time_picker.js +++ b/test/functional/apps/dashboard/dashboard_time_picker.js @@ -26,6 +26,7 @@ export default function ({ getService, getPageObjects }) { const dashboardVisualizations = getService('dashboardVisualizations'); const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'timePicker']); const browser = getService('browser'); + const kibanaServer = getService('kibanaServer'); describe('dashboard time picker', function describeIndexTests() { before(async function () { @@ -33,6 +34,11 @@ export default function ({ getService, getPageObjects }) { await PageObjects.dashboard.preserveCrossAppState(); }); + after(async () => { + await kibanaServer.uiSettings.replace({}); + await browser.refresh(); + }); + it('Visualization updated when time picker changes', async () => { await PageObjects.dashboard.clickNewDashboard(); await PageObjects.dashboard.addVisualizations([PIE_CHART_VIS_NAME]); @@ -49,7 +55,7 @@ export default function ({ getService, getPageObjects }) { await dashboardExpect.docTableFieldCount(150); // Set to time range with no data - await PageObjects.timePicker.setAbsoluteRange('2000-01-01 00:00:00.000', '2000-01-01 01:00:00.000'); + await PageObjects.timePicker.setAbsoluteRange('Jan 1, 2000 @ 00:00:00.000', 'Jan 1, 2000 @ 01:00:00.000'); await dashboardExpect.docTableFieldCount(0); }); @@ -72,5 +78,16 @@ export default function ({ getService, getPageObjects }) { expect(time.end).to.be('Nov 17, 2015 @ 18:01:36.621'); expect(refresh.interval).to.be('2'); }); + + it('Timepicker respects dateFormat from UI settings', async () => { + await kibanaServer.uiSettings.replace({ 'dateFormat': 'YYYY-MM-DD HH:mm:ss.SSS', }); + await browser.refresh(); + await PageObjects.dashboard.gotoDashboardLandingPage(); + await PageObjects.dashboard.clickNewDashboard(); + await PageObjects.dashboard.addVisualizations([PIE_CHART_VIS_NAME]); + // Same date range as `setTimepickerInHistoricalDataRange` + await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000'); + await pieChart.expectPieSliceCount(10); + }); }); } diff --git a/test/functional/apps/dashboard/embed_mode.js b/test/functional/apps/dashboard/embed_mode.js index 7122d9ff8ca250..9eb5b2c9352d86 100644 --- a/test/functional/apps/dashboard/embed_mode.js +++ b/test/functional/apps/dashboard/embed_mode.js @@ -25,6 +25,7 @@ export default function ({ getService, getPageObjects }) { const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['dashboard', 'common']); const browser = getService('browser'); + const globalNav = getService('globalNav'); describe('embed mode', () => { before(async () => { @@ -38,8 +39,8 @@ export default function ({ getService, getPageObjects }) { }); it('hides the chrome', async () => { - const isChromeVisible = await PageObjects.common.isChromeVisible(); - expect(isChromeVisible).to.be(true); + const globalNavShown = await globalNav.exists(); + expect(globalNavShown).to.be(true); const currentUrl = await browser.getCurrentUrl(); const newUrl = currentUrl + '&embed=true'; @@ -48,8 +49,8 @@ export default function ({ getService, getPageObjects }) { await browser.get(newUrl.toString(), useTimeStamp); await retry.try(async () => { - const isChromeHidden = await PageObjects.common.isChromeHidden(); - expect(isChromeHidden).to.be(true); + const globalNavHidden = !(await globalNav.exists()); + expect(globalNavHidden).to.be(true); }); }); diff --git a/test/functional/apps/dashboard/embeddable_rendering.js b/test/functional/apps/dashboard/embeddable_rendering.js index d90de4204bc76d..bdbfd41b437eb7 100644 --- a/test/functional/apps/dashboard/embeddable_rendering.js +++ b/test/functional/apps/dashboard/embeddable_rendering.js @@ -102,8 +102,8 @@ export default function ({ getService, getPageObjects }) { await PageObjects.dashboard.preserveCrossAppState(); await PageObjects.dashboard.clickNewDashboard(); - const fromTime = '2018-01-01 00:00:00.000'; - const toTime = '2018-04-13 00:00:00.000'; + const fromTime = 'Jan 1, 2018 @ 00:00:00.000'; + const toTime = 'Apr 13, 2018 @ 00:00:00.000'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); }); @@ -145,8 +145,8 @@ export default function ({ getService, getPageObjects }) { it('data rendered correctly when dashboard is opened from listing page', async () => { // Change the time to make sure that it's updated when re-opened from the listing page. - const fromTime = '2018-05-10 00:00:00.000'; - const toTime = '2018-05-11 00:00:00.000'; + const fromTime = 'May 10, 2018 @ 00:00:00.000'; + const toTime = 'May 11, 2018 @ 00:00:00.000'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); await PageObjects.dashboard.loadSavedDashboard('embeddable rendering test'); await PageObjects.dashboard.waitForRenderComplete(); @@ -162,16 +162,16 @@ export default function ({ getService, getPageObjects }) { }); it('panels are updated when time changes outside of data', async () => { - const fromTime = '2018-05-11 00:00:00.000'; - const toTime = '2018-05-12 00:00:00.000'; + const fromTime = 'May 11, 2018 @ 00:00:00.000'; + const toTime = 'May 12, 2018 @ 00:00:00.000'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); await PageObjects.dashboard.waitForRenderComplete(); await expectNoDataRenders(); }); it('panels are updated when time changes inside of data', async () => { - const fromTime = '2018-01-01 00:00:00.000'; - const toTime = '2018-04-13 00:00:00.000'; + const fromTime = 'Jan 1, 2018 @ 00:00:00.000'; + const toTime = 'Apr 13, 2018 @ 00:00:00.000'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); await PageObjects.dashboard.waitForRenderComplete(); await expectAllDataRenders(); diff --git a/test/functional/apps/dashboard/time_zones.js b/test/functional/apps/dashboard/time_zones.js index 19516e5092c1cd..30031882ae09e2 100644 --- a/test/functional/apps/dashboard/time_zones.js +++ b/test/functional/apps/dashboard/time_zones.js @@ -51,8 +51,8 @@ export default function ({ getService, getPageObjects }) { it('Exported dashboard adjusts EST time to UTC', async () => { const time = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); - expect(time.start).to.be('2018-04-10 03:00:00.000'); - expect(time.end).to.be('2018-04-10 04:00:00.000'); + expect(time.start).to.be('Apr 10, 2018 @ 03:00:00.000'); + expect(time.end).to.be('Apr 10, 2018 @ 04:00:00.000'); await pieChart.expectPieSliceCount(4); }); @@ -63,8 +63,8 @@ export default function ({ getService, getPageObjects }) { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.loadSavedDashboard('time zone test'); const time = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); - expect(time.start).to.be('2018-04-09 22:00:00.000'); - expect(time.end).to.be('2018-04-09 23:00:00.000'); + expect(time.start).to.be('Apr 9, 2018 @ 22:00:00.000'); + expect(time.end).to.be('Apr 9, 2018 @ 23:00:00.000'); await pieChart.expectPieSliceCount(4); }); }); diff --git a/test/functional/apps/dashboard/view_edit.js b/test/functional/apps/dashboard/view_edit.js index 958a889271b617..74ddb476064c58 100644 --- a/test/functional/apps/dashboard/view_edit.js +++ b/test/functional/apps/dashboard/view_edit.js @@ -75,7 +75,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.dashboard.saveDashboard(dashboardName, { storeTimeWithDashboard: true }); await PageObjects.dashboard.switchToEditMode(); - await PageObjects.timePicker.setAbsoluteRange('2013-09-19 06:31:44.000', '2013-09-19 06:31:44.000'); + await PageObjects.timePicker.setAbsoluteRange('Sep 19, 2013 @ 06:31:44.000', 'Sep 19, 2013 @ 06:31:44.000'); await PageObjects.dashboard.clickCancelOutOfEditMode(); // confirm lose changes @@ -161,10 +161,10 @@ export default function ({ getService, getPageObjects }) { describe('and preserves edits on cancel', function () { it('when time changed is stored with dashboard', async function () { await PageObjects.dashboard.gotoDashboardEditMode(dashboardName); - await PageObjects.timePicker.setAbsoluteRange('2013-09-19 06:31:44.000', '2013-09-19 06:31:44.000'); + await PageObjects.timePicker.setAbsoluteRange('Sep 19, 2013 @ 06:31:44.000', 'Sep 19, 2013 @ 06:31:44.000'); await PageObjects.dashboard.saveDashboard(dashboardName, true); await PageObjects.dashboard.switchToEditMode(); - await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-19 06:31:44.000'); + await PageObjects.timePicker.setAbsoluteRange('Sep 19, 2015 @ 06:31:44.000', 'Sep 19, 2015 @ 06:31:44.000'); await PageObjects.dashboard.clickCancelOutOfEditMode(); await PageObjects.common.clickCancelOnModal(); @@ -186,7 +186,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.dashboard.setTimepickerInDataRange(); await PageObjects.dashboard.saveDashboard(dashboardName, true); await PageObjects.dashboard.switchToEditMode(); - await PageObjects.timePicker.setAbsoluteRange('2013-09-19 06:31:44.000', '2013-09-19 06:31:44.000'); + await PageObjects.timePicker.setAbsoluteRange('Sep 19, 2013 @ 06:31:44.000', 'Sep 19, 2013 @ 06:31:44.000'); const newTime = await PageObjects.timePicker.getTimeConfig(); await PageObjects.dashboard.clickCancelOutOfEditMode(); @@ -208,7 +208,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.dashboard.gotoDashboardEditMode(dashboardName); await PageObjects.dashboard.saveDashboard(dashboardName, { storeTimeWithDashboard: false }); await PageObjects.dashboard.switchToEditMode(); - await PageObjects.timePicker.setAbsoluteRange('2014-10-19 06:31:44.000', '2014-12-19 06:31:44.000'); + await PageObjects.timePicker.setAbsoluteRange('Oct 19, 2014 @ 06:31:44.000', 'Dec 19, 2014 @ 06:31:44.000'); await PageObjects.dashboard.clickCancelOutOfEditMode(); await PageObjects.common.expectConfirmModalOpenState(false); diff --git a/test/functional/apps/discover/_date_nanos.js b/test/functional/apps/discover/_date_nanos.js index 6876c8eb6daa41..d9eb40c16c2d48 100644 --- a/test/functional/apps/discover/_date_nanos.js +++ b/test/functional/apps/discover/_date_nanos.js @@ -23,8 +23,8 @@ export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); const kibanaServer = getService('kibanaServer'); - const fromTime = '2019-09-22 20:31:44.000'; - const toTime = '2019-09-23 03:31:44.000'; + const fromTime = 'Sep 22, 2019 @ 20:31:44.000'; + const toTime = 'Sep 23, 2019 @ 03:31:44.000'; describe('date_nanos', function () { @@ -41,8 +41,8 @@ export default function ({ getService, getPageObjects }) { it('should show a timestamp with nanoseconds in the first result row', async function () { const time = await PageObjects.timePicker.getTimeConfig(); - expect(time.start).to.be('Sep 22, 2019 @ 20:31:44.000'); - expect(time.end).to.be('Sep 23, 2019 @ 03:31:44.000'); + expect(time.start).to.be(fromTime); + expect(time.end).to.be(toTime); const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData.startsWith('Sep 22, 2019 @ 23:50:13.253123345')).to.be.ok(); }); diff --git a/test/functional/apps/discover/_date_nanos_mixed.js b/test/functional/apps/discover/_date_nanos_mixed.js index 8c9a7eb4d5f138..c77ea3b2915e1e 100644 --- a/test/functional/apps/discover/_date_nanos_mixed.js +++ b/test/functional/apps/discover/_date_nanos_mixed.js @@ -23,8 +23,8 @@ export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); const kibanaServer = getService('kibanaServer'); - const fromTime = '2019-01-01 00:00:00.000'; - const toTime = '2019-01-01 23:59:59.999'; + const fromTime = 'Jan 1, 2019 @ 00:00:00.000'; + const toTime = 'Jan 1, 2019 @ 23:59:59.999'; describe('date_nanos_mixed', function () { diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js index 9d3f95e28942a8..94b2941ecd3d18 100644 --- a/test/functional/apps/discover/_discover.js +++ b/test/functional/apps/discover/_discover.js @@ -33,8 +33,6 @@ export default function ({ getService, getPageObjects }) { }; describe('discover test', function describeIndexTests() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; before(async function () { log.debug('load kibana index with default index pattern'); @@ -45,7 +43,7 @@ export default function ({ getService, getPageObjects }) { await kibanaServer.uiSettings.replace(defaultSettings); log.debug('discover'); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); describe('query', function () { @@ -54,8 +52,8 @@ export default function ({ getService, getPageObjects }) { it('should show correct time range string by timepicker', async function () { const time = await PageObjects.timePicker.getTimeConfig(); - expect(time.start).to.be('Sep 19, 2015 @ 06:31:44.000'); - expect(time.end).to.be('Sep 23, 2015 @ 18:31:44.000'); + expect(time.start).to.be(PageObjects.timePicker.defaultStartTime); + expect(time.end).to.be(PageObjects.timePicker.defaultEndTime); const rowData = await PageObjects.discover.getDocTableIndex(1); log.debug('check the newest doc timestamp in UTC (check diff timezone in last test)'); expect(rowData.startsWith('Sep 22, 2015 @ 23:50:13.253')).to.be.ok(); @@ -88,12 +86,12 @@ export default function ({ getService, getPageObjects }) { it('should show correct time range string in chart', async function () { const actualTimeString = await PageObjects.discover.getChartTimespan(); - const expectedTimeString = `Sep 19, 2015 @ 06:31:44.000 - Sep 23, 2015 @ 18:31:44.000`; + const expectedTimeString = `${PageObjects.timePicker.defaultStartTime} - ${PageObjects.timePicker.defaultEndTime}`; expect(actualTimeString).to.be(expectedTimeString); }); it('should modify the time range when a bar is clicked', async function () { - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.discover.clickHistogramBar(); const time = await PageObjects.timePicker.getTimeConfig(); expect(time.start).to.be('Sep 21, 2015 @ 09:00:00.000'); @@ -103,7 +101,7 @@ export default function ({ getService, getPageObjects }) { }); it('should modify the time range when the histogram is brushed', async function () { - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.discover.brushHistogram(); const newDurationHours = await PageObjects.timePicker.getTimeDurationInHours(); @@ -113,7 +111,7 @@ export default function ({ getService, getPageObjects }) { }); it('should show correct initial chart interval of Auto', async function () { - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.discover.waitUntilSearchingHasFinished(); const actualInterval = await PageObjects.discover.getChartInterval(); @@ -135,8 +133,8 @@ export default function ({ getService, getPageObjects }) { }); describe('query #2, which has an empty time range', () => { - const fromTime = '1999-06-11 09:22:11.000'; - const toTime = '1999-06-12 11:21:04.000'; + const fromTime = 'Jun 11, 1999 @ 09:22:11.000'; + const toTime = 'Jun 12, 1999 @ 11:21:04.000'; before(async () => { log.debug('setAbsoluteRangeForAnotherQuery'); @@ -159,7 +157,7 @@ export default function ({ getService, getPageObjects }) { before(async () => { log.debug('setAbsoluteRangeForAnotherQuery'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.discover.waitUntilSearchingHasFinished(); }); @@ -213,7 +211,7 @@ export default function ({ getService, getPageObjects }) { await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'America/Phoenix' }); await browser.refresh(); await PageObjects.header.awaitKibanaChrome(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('check that the newest doc timestamp is now -7 hours from the UTC time in the first test'); const rowData = await PageObjects.discover.getDocTableIndex(1); diff --git a/test/functional/apps/discover/_doc_navigation.js b/test/functional/apps/discover/_doc_navigation.js index e9afa7c9b4c195..dc9f6c35174642 100644 --- a/test/functional/apps/discover/_doc_navigation.js +++ b/test/functional/apps/discover/_doc_navigation.js @@ -19,8 +19,6 @@ import expect from '@kbn/expect'; -const TEST_DOC_START_TIME = '2015-09-19 06:31:44.000'; -const TEST_DOC_END_TIME = '2015-09-23 18:31:44.000'; const TEST_COLUMN_NAMES = ['@message']; const TEST_FILTER_COLUMN_NAMES = [['extension', 'jpg'], ['geo.src', 'IN']]; @@ -35,7 +33,7 @@ export default function ({ getService, getPageObjects }) { before(async function () { await esArchiver.loadIfNeeded('logstash_functional'); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(TEST_DOC_START_TIME, TEST_DOC_END_TIME); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await Promise.all(TEST_COLUMN_NAMES.map((columnName) => ( PageObjects.discover.clickFieldListItemAdd(columnName) ))); diff --git a/test/functional/apps/discover/_field_data.js b/test/functional/apps/discover/_field_data.js index 828975445e1ef6..38d9a89eecbf3e 100644 --- a/test/functional/apps/discover/_field_data.js +++ b/test/functional/apps/discover/_field_data.js @@ -30,8 +30,6 @@ export default function ({ getService, getPageObjects }) { describe('discover tab', function describeIndexTests() { this.tags('smoke'); before(async function () { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; await esArchiver.loadIfNeeded('logstash_functional'); await esArchiver.load('discover'); @@ -41,7 +39,7 @@ export default function ({ getService, getPageObjects }) { }); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); describe('field data', function () { diff --git a/test/functional/apps/discover/_inspector.js b/test/functional/apps/discover/_inspector.js index 6a2144e829920c..bd04cd6d1bd643 100644 --- a/test/functional/apps/discover/_inspector.js +++ b/test/functional/apps/discover/_inspector.js @@ -58,7 +58,7 @@ export default function ({ getService, getPageObjects }) { }); it('should display request stats with results', async () => { - await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await inspector.open(); const requestStats = await inspector.getTableData(); diff --git a/test/functional/apps/discover/_saved_queries.js b/test/functional/apps/discover/_saved_queries.js index 54b026135025c6..8fbc40f86e8dca 100644 --- a/test/functional/apps/discover/_saved_queries.js +++ b/test/functional/apps/discover/_saved_queries.js @@ -34,8 +34,6 @@ export default function ({ getService, getPageObjects }) { const testSubjects = getService('testSubjects'); describe('saved queries saved objects', function describeIndexTests() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; before(async function () { log.debug('load kibana index with default index pattern'); @@ -46,7 +44,7 @@ export default function ({ getService, getPageObjects }) { await kibanaServer.uiSettings.replace(defaultSettings); log.debug('discover'); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); describe('saved query management component functionality', function () { @@ -55,8 +53,8 @@ export default function ({ getService, getPageObjects }) { log.debug('set up a query with filters to save'); await queryBar.setQuery('response:200'); await filterBar.addFilter('extension.raw', 'is one of', 'jpg'); - const fromTime = '2015-09-20 08:00:00.000'; - const toTime = '2015-09-21 08:00:00.000'; + const fromTime = 'Sep 20, 2015 @ 08:00:00.000'; + const toTime = 'Sep 21, 2015 @ 08:00:00.000'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); }); @@ -79,15 +77,13 @@ export default function ({ getService, getPageObjects }) { }); it('reinstates filters and the time filter when a saved query has filters and a time filter included', async () => { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); await savedQueryManagementComponent.loadSavedQuery('OkResponse'); const timePickerValues = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); - expect(timePickerValues.start).to.not.eql(fromTime); - expect(timePickerValues.end).to.not.eql(toTime); + expect(timePickerValues.start).to.not.eql(PageObjects.timePicker.defaultStartTime); + expect(timePickerValues.end).to.not.eql(PageObjects.timePicker.defaultEndTime); }); it('allows saving changes to a currently loaded query via the saved query management component', async () => { diff --git a/test/functional/apps/discover/_shared_links.js b/test/functional/apps/discover/_shared_links.js index 0b2b4f14f126dd..8381d9bc7caeae 100644 --- a/test/functional/apps/discover/_shared_links.js +++ b/test/functional/apps/discover/_shared_links.js @@ -38,9 +38,6 @@ export default function ({ getService, getPageObjects }) { baseUrl = baseUrl.replace(':80', '').replace(':443', ''); log.debug('New baseUrl = ' + baseUrl); - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - // delete .kibana index and update configDoc await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*', @@ -57,7 +54,7 @@ export default function ({ getService, getPageObjects }) { log.debug('discover'); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); //After hiding the time picker, we need to wait for //the refresh button to hide before clicking the share button diff --git a/test/functional/apps/discover/_sidebar.js b/test/functional/apps/discover/_sidebar.js index 26a47b0a124222..4cf8fd44da2395 100644 --- a/test/functional/apps/discover/_sidebar.js +++ b/test/functional/apps/discover/_sidebar.js @@ -27,8 +27,6 @@ export default function ({ getService, getPageObjects }) { describe('discover sidebar', function describeIndexTests() { before(async function () { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; // delete .kibana index and update configDoc await kibanaServer.uiSettings.replace({ @@ -44,7 +42,7 @@ export default function ({ getService, getPageObjects }) { log.debug('discover'); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); describe('field filtering', function () { diff --git a/test/functional/apps/discover/_source_filters.js b/test/functional/apps/discover/_source_filters.js index 14ecde383fd442..06c81acbd5d436 100644 --- a/test/functional/apps/discover/_source_filters.js +++ b/test/functional/apps/discover/_source_filters.js @@ -27,8 +27,6 @@ export default function ({ getService, getPageObjects }) { describe('source filters', function describeIndexTests() { before(async function () { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; // delete .kibana index and update configDoc await kibanaServer.uiSettings.replace({ @@ -44,7 +42,7 @@ export default function ({ getService, getPageObjects }) { log.debug('discover'); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); //After hiding the time picker, we need to wait for //the refresh button to hide before clicking the share button diff --git a/test/functional/apps/home/_navigation.ts b/test/functional/apps/home/_navigation.ts index c60788b64b2a70..58e0793b2d547f 100644 --- a/test/functional/apps/home/_navigation.ts +++ b/test/functional/apps/home/_navigation.ts @@ -28,8 +28,6 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const retry = getService('retry'); const kibanaServer = getService('kibanaServer'); - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; const getHost = () => { if (process.env.TEST_KIBANA_HOSTNAME) { @@ -77,7 +75,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { // Navigate to discover app await appsMenu.clickLink('Discover'); const discoverUrl = await browser.getCurrentUrl(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); const modifiedTimeDiscoverUrl = await browser.getCurrentUrl(); // Navigate to dashboard app diff --git a/test/functional/apps/home/_sample_data.js b/test/functional/apps/home/_sample_data.js index 954c2dd110585c..44cb7c36a7f282 100644 --- a/test/functional/apps/home/_sample_data.js +++ b/test/functional/apps/home/_sample_data.js @@ -18,6 +18,7 @@ */ import expect from '@kbn/expect'; +import moment from 'moment'; export default function ({ getService, getPageObjects }) { const retry = getService('retry'); @@ -85,10 +86,9 @@ export default function ({ getService, getPageObjects }) { await PageObjects.home.launchSampleDataSet('flights'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); - const today = new Date(); - const todayYearMonthDay = today.toISOString().substring(0, 10); - const fromTime = `${todayYearMonthDay} 00:00:00.000`; - const toTime = `${todayYearMonthDay} 23:59:59.999`; + const todayYearMonthDay = moment().format('MMM D, YYYY'); + const fromTime = `${todayYearMonthDay} @ 00:00:00.000`; + const toTime = `${todayYearMonthDay} @ 23:59:59.999`; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); const panelCount = await PageObjects.dashboard.getPanelCount(); expect(panelCount).to.be(18); @@ -119,10 +119,9 @@ export default function ({ getService, getPageObjects }) { await PageObjects.home.launchSampleDataSet('logs'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); - const today = new Date(); - const todayYearMonthDay = today.toISOString().substring(0, 10); - const fromTime = `${todayYearMonthDay} 00:00:00.000`; - const toTime = `${todayYearMonthDay} 23:59:59.999`; + const todayYearMonthDay = moment().format('MMM D, YYYY'); + const fromTime = `${todayYearMonthDay} @ 00:00:00.000`; + const toTime = `${todayYearMonthDay} @ 23:59:59.999`; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); const panelCount = await PageObjects.dashboard.getPanelCount(); expect(panelCount).to.be(11); @@ -132,10 +131,9 @@ export default function ({ getService, getPageObjects }) { await PageObjects.home.launchSampleDataSet('ecommerce'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); - const today = new Date(); - const todayYearMonthDay = today.toISOString().substring(0, 10); - const fromTime = `${todayYearMonthDay} 00:00:00.000`; - const toTime = `${todayYearMonthDay} 23:59:59.999`; + const todayYearMonthDay = moment().format('MMM D, YYYY'); + const fromTime = `${todayYearMonthDay} @ 00:00:00.000`; + const toTime = `${todayYearMonthDay} @ 23:59:59.999`; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); const panelCount = await PageObjects.dashboard.getPanelCount(); expect(panelCount).to.be(12); diff --git a/test/functional/apps/management/_handle_alias.js b/test/functional/apps/management/_handle_alias.js index 1556897d69387c..99941d37df9e77 100644 --- a/test/functional/apps/management/_handle_alias.js +++ b/test/functional/apps/management/_handle_alias.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const retry = getService('retry'); const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover', 'timePicker']); @@ -72,8 +72,8 @@ export default function ({ getService, getPageObjects }) { it('should be able to discover and verify no of hits for alias2', async function () { const expectedHitCount = '5'; - const fromTime = '2016-11-12 05:00:00.000'; - const toTime = '2016-11-19 05:00:00.000'; + const fromTime = 'Nov 12, 2016 @ 05:00:00.000'; + const toTime = 'Nov 19, 2016 @ 05:00:00.000'; await PageObjects.common.navigateToApp('discover'); await PageObjects.discover.selectIndexPattern('alias2'); diff --git a/test/functional/apps/management/_handle_version_conflict.js b/test/functional/apps/management/_handle_version_conflict.js index 217e6d4c1a8d38..e6c0825136ba10 100644 --- a/test/functional/apps/management/_handle_version_conflict.js +++ b/test/functional/apps/management/_handle_version_conflict.js @@ -32,7 +32,7 @@ import expect from '@kbn/expect'; export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const browser = getService('browser'); - const es = getService('es'); + const es = getService('legacyEs'); const retry = getService('retry'); const scriptedFiledName = 'versionConflictScript'; const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover', 'header']); diff --git a/test/functional/apps/management/_kibana_settings.js b/test/functional/apps/management/_kibana_settings.js index 9fb302cdba00a6..e5ae709b34d6d0 100644 --- a/test/functional/apps/management/_kibana_settings.js +++ b/test/functional/apps/management/_kibana_settings.js @@ -55,7 +55,7 @@ export default function ({ getService, getPageObjects }) { it('when false, dashboard state is unhashed', async function () { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); const currentUrl = await browser.getCurrentUrl(); const urlPieces = currentUrl.match(/(.*)?_g=(.*)&_a=(.*)/); const globalState = urlPieces[2]; @@ -78,7 +78,7 @@ export default function ({ getService, getPageObjects }) { it('when true, dashboard state is hashed', async function () { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); const currentUrl = await browser.getCurrentUrl(); const urlPieces = currentUrl.match(/(.*)?_g=(.*)&_a=(.*)/); const globalState = urlPieces[2]; diff --git a/test/functional/apps/management/_scripted_fields.js b/test/functional/apps/management/_scripted_fields.js index 94e38402deebef..7a21e5171595fe 100644 --- a/test/functional/apps/management/_scripted_fields.js +++ b/test/functional/apps/management/_scripted_fields.js @@ -124,8 +124,8 @@ export default function ({ getService, getPageObjects }) { }); it('should see scripted field value in Discover', async function () { - const fromTime = '2015-09-17 06:31:44.000'; - const toTime = '2015-09-18 18:31:44.000'; + const fromTime = 'Sep 17, 2015 @ 06:31:44.000'; + const toTime = 'Sep 18, 2015 @ 18:31:44.000'; await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); @@ -186,8 +186,8 @@ export default function ({ getService, getPageObjects }) { }); it('should see scripted field value in Discover', async function () { - const fromTime = '2015-09-17 06:31:44.000'; - const toTime = '2015-09-18 18:31:44.000'; + const fromTime = 'Sep 17, 2015 @ 06:31:44.000'; + const toTime = 'Sep 18, 2015 @ 18:31:44.000'; await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); @@ -246,8 +246,8 @@ export default function ({ getService, getPageObjects }) { }); it('should see scripted field value in Discover', async function () { - const fromTime = '2015-09-17 06:31:44.000'; - const toTime = '2015-09-18 18:31:44.000'; + const fromTime = 'Sep 17, 2015 @ 06:31:44.000'; + const toTime = 'Sep 18, 2015 @ 18:31:44.000'; await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); @@ -307,8 +307,8 @@ export default function ({ getService, getPageObjects }) { }); it('should see scripted field value in Discover', async function () { - const fromTime = '2015-09-17 19:22:00.000'; - const toTime = '2015-09-18 07:00:00.000'; + const fromTime = 'Sep 17, 2015 @ 19:22:00.000'; + const toTime = 'Sep 18, 2015 @ 07:00:00.000'; await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); @@ -326,7 +326,7 @@ export default function ({ getService, getPageObjects }) { it('should filter by scripted field value in Discover', async function () { await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2); - await log.debug('filter by "2015-09-17 23:00" in the expanded scripted field list'); + await log.debug('filter by "Sep 17, 2015 @ 23:00" in the expanded scripted field list'); await PageObjects.discover.clickFieldListPlusFilter(scriptedPainlessFieldName2, '2015-09-17 23:00'); await PageObjects.header.waitUntilLoadingHasFinished(); diff --git a/test/functional/apps/timelion/_expression_typeahead.js b/test/functional/apps/timelion/_expression_typeahead.js index 7cc0740823f3c5..55297c68104972 100644 --- a/test/functional/apps/timelion/_expression_typeahead.js +++ b/test/functional/apps/timelion/_expression_typeahead.js @@ -24,11 +24,9 @@ export default function ({ getPageObjects }) { describe('expression typeahead', () => { before(async () => { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; await PageObjects.timelion.initTests(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); it('should display function suggestions filtered by function name', async () => { diff --git a/test/functional/apps/visualize/_area_chart.js b/test/functional/apps/visualize/_area_chart.js index 963b7f25bc1c91..c6b33886a2456a 100644 --- a/test/functional/apps/visualize/_area_chart.js +++ b/test/functional/apps/visualize/_area_chart.js @@ -30,8 +30,6 @@ export default function ({ getService, getPageObjects }) { const vizName1 = 'Visualization AreaChart Name Test'; const initAreaChart = async () => { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; log.debug('navigateToApp visualize'); await PageObjects.visualize.navigateToNewVisualization(); @@ -39,7 +37,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.clickAreaChart(); log.debug('clickNewSearch'); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Click X-axis'); await PageObjects.visualize.clickBucket('X-axis'); log.debug('Click Date Histogram'); @@ -327,8 +325,8 @@ export default function ({ getService, getPageObjects }) { }); describe('date histogram with long time range', () => { // that dataset spans from Oct 26, 2013 @ 06:10:17.855 to Apr 18, 2019 @ 11:38:12.790 - const fromTime = '2013-01-01 00:00:00.000'; - const toTime = '2020-01-01 00:00:00.000'; + const fromTime = 'Jan 1, 2013 @ 00:00:00.000'; + const toTime = 'Jan 1, 2020 @ 00:00:00.000'; it('should render a yearly area with 12 svg paths', async () => { log.debug('navigateToApp visualize'); await PageObjects.visualize.navigateToNewVisualization(); diff --git a/test/functional/apps/visualize/_data_table.js b/test/functional/apps/visualize/_data_table.js index b540c1e949fbc1..51c0984c89fed5 100644 --- a/test/functional/apps/visualize/_data_table.js +++ b/test/functional/apps/visualize/_data_table.js @@ -26,9 +26,6 @@ export default function ({ getService, getPageObjects }) { const filterBar = getService('filterBar'); const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']); - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - describe('data table', function indexPatternCreation() { const vizName1 = 'Visualization DataTable'; @@ -39,7 +36,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); log.debug('clickNewSearch'); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = Split rows'); await PageObjects.visualize.clickBucket('Split rows'); log.debug('Aggregation = Histogram'); @@ -112,7 +109,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.clickBucket('Split rows'); await PageObjects.visualize.selectAggregation('Range'); await PageObjects.visualize.selectField('bytes'); @@ -157,7 +154,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.clickBucket('Metric', 'metrics'); await PageObjects.visualize.selectAggregation('Average Bucket', 'metrics'); await PageObjects.visualize.selectAggregation('Terms', 'metrics', 'buckets'); @@ -172,7 +169,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.clickBucket('Split rows'); await PageObjects.visualize.selectAggregation('Date Histogram'); await PageObjects.visualize.selectField('@timestamp'); @@ -191,7 +188,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.clickBucket('Split rows'); await PageObjects.visualize.selectAggregation('Date Histogram'); await PageObjects.visualize.selectField('@timestamp'); @@ -227,7 +224,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.clickMetricEditor(); await PageObjects.visualize.selectAggregation('Top Hit', 'metrics'); await PageObjects.visualize.selectField('agent.raw', 'metrics'); @@ -241,7 +238,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.clickBucket('Split rows'); await PageObjects.visualize.selectAggregation('Range'); await PageObjects.visualize.selectField('bytes'); @@ -259,7 +256,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.clickBucket('Split rows'); await PageObjects.visualize.selectAggregation('Terms'); await PageObjects.visualize.selectField('extension.raw'); @@ -297,7 +294,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.clickBucket('Split rows'); await PageObjects.visualize.selectAggregation('Terms'); await PageObjects.visualize.selectField('extension.raw'); @@ -392,7 +389,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.clickBucket('Split table'); await PageObjects.visualize.selectAggregation('Terms'); await PageObjects.visualize.selectField('extension.raw'); diff --git a/test/functional/apps/visualize/_embedding_chart.js b/test/functional/apps/visualize/_embedding_chart.js index 12bc53ec265073..fcf5e549543477 100644 --- a/test/functional/apps/visualize/_embedding_chart.js +++ b/test/functional/apps/visualize/_embedding_chart.js @@ -26,9 +26,6 @@ export default function ({ getService, getPageObjects }) { const embedding = getService('embedding'); const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']); - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - describe('embedding', () => { describe('a data table', () => { @@ -36,7 +33,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.clickBucket('Split rows'); await PageObjects.visualize.selectAggregation('Date Histogram'); await PageObjects.visualize.selectField('@timestamp'); diff --git a/test/functional/apps/visualize/_gauge_chart.js b/test/functional/apps/visualize/_gauge_chart.js index 0d0383ba91aa74..2125717247e9e8 100644 --- a/test/functional/apps/visualize/_gauge_chart.js +++ b/test/functional/apps/visualize/_gauge_chart.js @@ -29,8 +29,6 @@ export default function ({ getService, getPageObjects }) { // FLAKY: https://github.com/elastic/kibana/issues/45089 describe('gauge chart', function indexPatternCreation() { this.tags('smoke'); - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; async function initGaugeVis() { log.debug('navigateToApp visualize'); @@ -38,7 +36,7 @@ export default function ({ getService, getPageObjects }) { log.debug('clickGauge'); await PageObjects.visualize.clickGauge(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); } before(initGaugeVis); diff --git a/test/functional/apps/visualize/_heatmap_chart.js b/test/functional/apps/visualize/_heatmap_chart.js index ac48f78b5dde76..547ecdd5988118 100644 --- a/test/functional/apps/visualize/_heatmap_chart.js +++ b/test/functional/apps/visualize/_heatmap_chart.js @@ -27,8 +27,6 @@ export default function ({ getService, getPageObjects }) { describe('heatmap chart', function indexPatternCreation() { this.tags('smoke'); const vizName1 = 'Visualization HeatmapChart'; - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; before(async function () { log.debug('navigateToApp visualize'); @@ -36,7 +34,7 @@ export default function ({ getService, getPageObjects }) { log.debug('clickHeatmapChart'); await PageObjects.visualize.clickHeatmapChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = X-Axis'); await PageObjects.visualize.clickBucket('X-axis'); log.debug('Aggregation = Date Histogram'); diff --git a/test/functional/apps/visualize/_histogram_request_start.js b/test/functional/apps/visualize/_histogram_request_start.js index 76709e3ed968f2..10b87d204d862c 100644 --- a/test/functional/apps/visualize/_histogram_request_start.js +++ b/test/functional/apps/visualize/_histogram_request_start.js @@ -26,15 +26,13 @@ export default function ({ getService, getPageObjects }) { describe('histogram agg onSearchRequestStart', function () { before(async function () { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; log.debug('navigateToApp visualize'); await PageObjects.visualize.navigateToNewVisualization(); log.debug('clickDataTable'); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = Split Rows'); await PageObjects.visualize.clickBucket('Split rows'); log.debug('Aggregation = Histogram'); diff --git a/test/functional/apps/visualize/_inspector.js b/test/functional/apps/visualize/_inspector.js index 3c45063a1a16d9..5917216cad40fe 100644 --- a/test/functional/apps/visualize/_inspector.js +++ b/test/functional/apps/visualize/_inspector.js @@ -27,14 +27,12 @@ export default function ({ getService, getPageObjects }) { describe('inspector', function describeIndexTests() { this.tags('smoke'); before(async function () { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVerticalBarChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); describe('inspector table', function indexPatternCreation() { diff --git a/test/functional/apps/visualize/_line_chart.js b/test/functional/apps/visualize/_line_chart.js index 29116b0ca56c4b..cbadb7408f9855 100644 --- a/test/functional/apps/visualize/_line_chart.js +++ b/test/functional/apps/visualize/_line_chart.js @@ -30,15 +30,13 @@ export default function ({ getService, getPageObjects }) { const vizName1 = 'Visualization LineChart'; const initLineChart = async function () { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; log.debug('navigateToApp visualize'); await PageObjects.visualize.navigateToNewVisualization(); log.debug('clickLineChart'); await PageObjects.visualize.clickLineChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = Split chart'); await PageObjects.visualize.clickBucket('Split chart'); log.debug('Aggregation = Terms'); diff --git a/test/functional/apps/visualize/_linked_saved_searches.js b/test/functional/apps/visualize/_linked_saved_searches.js index 5343873e9e0a5d..4d9553b4d9262c 100644 --- a/test/functional/apps/visualize/_linked_saved_searches.js +++ b/test/functional/apps/visualize/_linked_saved_searches.js @@ -25,8 +25,6 @@ export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['common', 'discover', 'visualize', 'header', 'timePicker']); describe('visualize app', function describeIndexTests() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; describe('linked saved searched', () => { @@ -45,7 +43,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickSavedSearch(savedSearchName); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await retry.waitFor('wait for count to equal 9,109', async () => { const data = await PageObjects.visualize.getTableVisData(); return data.trim() === '9,109'; @@ -53,7 +51,7 @@ export default function ({ getService, getPageObjects }) { }); it('should respect the time filter when linked to a saved search', async () => { - await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-21 10:00:00.000'); + await PageObjects.timePicker.setAbsoluteRange('Sep 19, 2015 @ 06:31:44.000', 'Sep 21, 2015 @ 10:00:00.000'); await retry.waitFor('wait for count to equal 3,950', async () => { const data = await PageObjects.visualize.getTableVisData(); return data.trim() === '3,950'; diff --git a/test/functional/apps/visualize/_metric_chart.js b/test/functional/apps/visualize/_metric_chart.js index 237ee1ef50b1e5..1a4f5683f65c66 100644 --- a/test/functional/apps/visualize/_metric_chart.js +++ b/test/functional/apps/visualize/_metric_chart.js @@ -27,8 +27,6 @@ export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']); describe('metric chart', function () { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; before(async function () { log.debug('navigateToApp visualize'); @@ -36,7 +34,7 @@ export default function ({ getService, getPageObjects }) { log.debug('clickMetric'); await PageObjects.visualize.clickMetric(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); it('should have inspector enabled', async function () { diff --git a/test/functional/apps/visualize/_pie_chart.js b/test/functional/apps/visualize/_pie_chart.js index b9c10aaf578bfa..fc040c038afd0d 100644 --- a/test/functional/apps/visualize/_pie_chart.js +++ b/test/functional/apps/visualize/_pie_chart.js @@ -25,8 +25,6 @@ export default function ({ getService, getPageObjects }) { const pieChart = getService('pieChart'); const inspector = getService('inspector'); const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings', 'timePicker']); - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; describe('pie chart', function () { const vizName1 = 'Visualization PieChart'; @@ -36,7 +34,7 @@ export default function ({ getService, getPageObjects }) { log.debug('clickPieChart'); await PageObjects.visualize.clickPieChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split slices'); await PageObjects.visualize.clickBucket('Split slices'); log.debug('Click aggregation Histogram'); @@ -85,7 +83,7 @@ export default function ({ getService, getPageObjects }) { log.debug('clickPieChart'); await PageObjects.visualize.clickPieChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split slices'); await PageObjects.visualize.clickBucket('Split slices'); log.debug('Click aggregation Terms'); @@ -187,7 +185,7 @@ export default function ({ getService, getPageObjects }) { log.debug('clickPieChart'); await PageObjects.visualize.clickPieChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split slices'); await PageObjects.visualize.clickBucket('Split slices'); log.debug('Click aggregation Filters'); @@ -199,8 +197,8 @@ export default function ({ getService, getPageObjects }) { log.debug('Set the 2nd filter value'); await PageObjects.visualize.setFilterAggregationValue('geo.dest:"CN"', 1); await PageObjects.visualize.clickGo(); - const emptyFromTime = '2016-09-19 06:31:44.000'; - const emptyToTime = '2016-09-23 18:31:44.000'; + const emptyFromTime = 'Sep 19, 2016 @ 06:31:44.000'; + const emptyToTime = 'Sep 23, 2016 @ 18:31:44.000'; log.debug('Switch to a different time range from \"' + emptyFromTime + '\" to \"' + emptyToTime + '\"'); await PageObjects.timePicker.setAbsoluteRange(emptyFromTime, emptyToTime); await PageObjects.visualize.waitForVisualization(); @@ -214,8 +212,10 @@ export default function ({ getService, getPageObjects }) { log.debug('clickPieChart'); await PageObjects.visualize.clickPieChart(); await PageObjects.visualize.clickNewSearch(); - log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + log.debug('Set absolute time range from \"' + + PageObjects.timePicker.defaultStartTime + '\" to \"' + + PageObjects.timePicker.defaultEndTime + '\"'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split slices'); await PageObjects.visualize.clickBucket('Split slices'); log.debug('Click aggregation Histogram'); @@ -275,7 +275,7 @@ export default function ({ getService, getPageObjects }) { log.debug('clickPieChart'); await PageObjects.visualize.clickPieChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split slices'); await PageObjects.visualize.clickBucket('Split slices'); log.debug('Click aggregation Filters'); @@ -302,7 +302,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickPieChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split chart'); await PageObjects.visualize.clickBucket('Split chart'); await PageObjects.visualize.selectAggregation('Terms'); diff --git a/test/functional/apps/visualize/_point_series_options.js b/test/functional/apps/visualize/_point_series_options.js index d175acbd6c02e4..9bbe140d22f9be 100644 --- a/test/functional/apps/visualize/_point_series_options.js +++ b/test/functional/apps/visualize/_point_series_options.js @@ -29,15 +29,13 @@ export default function ({ getService, getPageObjects }) { const inspector = getService('inspector'); async function initChart() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; log.debug('navigateToApp visualize'); await PageObjects.visualize.navigateToNewVisualization(); log.debug('clickLineChart'); await PageObjects.visualize.clickLineChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = X-axis'); await PageObjects.visualize.clickBucket('X-axis'); log.debug('Aggregation = Date Histogram'); @@ -208,8 +206,8 @@ export default function ({ getService, getPageObjects }) { }); it('should show different labels in different timezone', async function () { - const fromTime = '2015-09-22 09:05:47.415'; - const toTime = '2015-09-22 16:08:34.554'; + const fromTime = 'Sep 22, 2015 @ 09:05:47.415'; + const toTime = 'Sep 22, 2015 @ 16:08:34.554'; // note that we're setting the absolute time range while we're in 'America/Phoenix' tz await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); await PageObjects.visualize.waitForRenderingCount(); diff --git a/test/functional/apps/visualize/_region_map.js b/test/functional/apps/visualize/_region_map.js index 11eb2e3f0ca836..e94867f903f077 100644 --- a/test/functional/apps/visualize/_region_map.js +++ b/test/functional/apps/visualize/_region_map.js @@ -23,8 +23,6 @@ import expect from '@kbn/expect'; export default function ({ getService, getPageObjects }) { describe('vector map', function () { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; const inspector = getService('inspector'); const log = getService('log'); @@ -38,7 +36,7 @@ export default function ({ getService, getPageObjects }) { log.debug('clickRegionMap'); await PageObjects.visualize.clickRegionMap(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = Shape field'); await PageObjects.visualize.clickBucket('Shape field'); log.debug('Aggregation = Terms'); diff --git a/test/functional/apps/visualize/_tag_cloud.js b/test/functional/apps/visualize/_tag_cloud.js index 4ed95214550c15..5b435ef29a2688 100644 --- a/test/functional/apps/visualize/_tag_cloud.js +++ b/test/functional/apps/visualize/_tag_cloud.js @@ -30,8 +30,6 @@ export default function ({ getService, getPageObjects }) { describe('tag cloud chart', function () { const vizName1 = 'Visualization tagCloud'; - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; const termsField = 'machine.ram'; before(async function () { @@ -40,7 +38,7 @@ export default function ({ getService, getPageObjects }) { log.debug('clickTagCloud'); await PageObjects.visualize.clickTagCloud(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select Tags'); await PageObjects.visualize.clickBucket('Tags'); log.debug('Click aggregation Terms'); @@ -140,7 +138,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.common.navigateToApp('visualize'); await PageObjects.visualize.loadSavedVisualization(vizName1, { navigateToVisualize: false }); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.waitForVisualization(); }); diff --git a/test/functional/apps/visualize/_tile_map.js b/test/functional/apps/visualize/_tile_map.js index 0e580f6a7ab3fd..f25e7e649a4270 100644 --- a/test/functional/apps/visualize/_tile_map.js +++ b/test/functional/apps/visualize/_tile_map.js @@ -37,15 +37,12 @@ export default function ({ getService, getPageObjects }) { before(async function () { await browser.setWindowSize(1280, 1000); - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - log.debug('navigateToApp visualize'); await PageObjects.visualize.navigateToNewVisualization(); log.debug('clickTileMap'); await PageObjects.visualize.clickTileMap(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); //do not configure aggs }); @@ -62,15 +59,12 @@ export default function ({ getService, getPageObjects }) { before(async function () { await browser.setWindowSize(1280, 1000); - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - log.debug('navigateToApp visualize'); await PageObjects.visualize.navigateToNewVisualization(); log.debug('clickTileMap'); await PageObjects.visualize.clickTileMap(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Geo Coordinates'); await PageObjects.visualize.clickBucket('Geo coordinates'); log.debug('Click aggregation Geohash'); diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index d4896d0a0fd71d..dc4b9a786eaa46 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -121,8 +121,8 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const value = await PageObjects.visualBuilder.getMetricValue(); expect(value).to.eql('156'); await PageObjects.visualBuilder.clickPanelOptions('metric'); - const fromTime = '2018-10-22 00:00:00.000'; - const toTime = '2018-10-28 23:59:59.999'; + const fromTime = 'Oct 22, 2018 @ 00:00:00.000'; + const toTime = 'Oct 28, 2018 @ 23:59:59.999'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); await PageObjects.visualBuilder.setIndexPatternValue('kibana_sample_data_flights'); await PageObjects.visualBuilder.selectIndexPatternTimeField('timestamp'); diff --git a/test/functional/apps/visualize/_tsvb_markdown.ts b/test/functional/apps/visualize/_tsvb_markdown.ts index f7b8415583a396..b7307ac9c6cabc 100644 --- a/test/functional/apps/visualize/_tsvb_markdown.ts +++ b/test/functional/apps/visualize/_tsvb_markdown.ts @@ -43,7 +43,10 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { before(async () => { await visualBuilder.resetPage(); await visualBuilder.clickMarkdown(); - await timePicker.setAbsoluteRange('2015-09-22 06:00:00.000', '2015-09-22 11:00:00.000'); + await timePicker.setAbsoluteRange( + 'Sep 22, 2015 @ 06:00:00.000', + 'Sep 22, 2015 @ 11:00:00.000' + ); }); it('should render subtabs and table variables markdown components', async () => { diff --git a/test/functional/apps/visualize/_tsvb_table.ts b/test/functional/apps/visualize/_tsvb_table.ts index 0b3541d7989509..c096c28f7b8043 100644 --- a/test/functional/apps/visualize/_tsvb_table.ts +++ b/test/functional/apps/visualize/_tsvb_table.ts @@ -27,7 +27,7 @@ export default function({ getPageObjects }: FtrProviderContext) { describe('visual builder', function describeIndexTests() { describe('table', () => { beforeEach(async () => { - await visualBuilder.resetPage('2015-09-22 06:00:00.000', '2015-09-22 11:00:00.000'); + await visualBuilder.resetPage('Sep 22, 2015 @ 06:00:00.000', 'Sep 22, 2015 @ 11:00:00.000'); await visualBuilder.clickTable(); await visualBuilder.checkTableTabIsPresent(); diff --git a/test/functional/apps/visualize/_vega_chart.js b/test/functional/apps/visualize/_vega_chart.js index 186318ebe6325a..f4fb9f13d83bd2 100644 --- a/test/functional/apps/visualize/_vega_chart.js +++ b/test/functional/apps/visualize/_vega_chart.js @@ -25,9 +25,6 @@ export default function ({ getService, getPageObjects }) { const inspector = getService('inspector'); const log = getService('log'); - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - describe('visualize app', () => { before(async () => { log.debug('navigateToApp visualize'); @@ -64,7 +61,7 @@ export default function ({ getService, getPageObjects }) { describe('with filters', () => { before(async () => { log.debug('setAbsoluteRange'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); afterEach(async () => { diff --git a/test/functional/apps/visualize/_vertical_bar_chart.js b/test/functional/apps/visualize/_vertical_bar_chart.js index 3a704ab0a20261..fb08cfa8982376 100644 --- a/test/functional/apps/visualize/_vertical_bar_chart.js +++ b/test/functional/apps/visualize/_vertical_bar_chart.js @@ -28,8 +28,6 @@ export default function ({ getService, getPageObjects }) { // FLAKY: https://github.com/elastic/kibana/issues/22322 describe('vertical bar chart', function () { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; const vizName1 = 'Visualization VerticalBarChart'; const initBarChart = async () => { @@ -38,7 +36,7 @@ export default function ({ getService, getPageObjects }) { log.debug('clickVerticalBarChart'); await PageObjects.visualize.clickVerticalBarChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = X-Axis'); await PageObjects.visualize.clickBucket('X-axis'); log.debug('Aggregation = Date Histogram'); @@ -110,8 +108,8 @@ export default function ({ getService, getPageObjects }) { }); it('should have `drop partial buckets` option', async () => { - const fromTime = '2015-09-20 06:31:44.000'; - const toTime = '2015-09-22 18:31:44.000'; + const fromTime = 'Sep 20, 2015 @ 06:31:44.000'; + const toTime = 'Sep 22, 2015 @ 18:31:44.000'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); diff --git a/test/functional/apps/visualize/input_control_vis/input_control_options.js b/test/functional/apps/visualize/input_control_vis/input_control_options.js index 4088ab6193a59f..ef58f0da4c8f43 100644 --- a/test/functional/apps/visualize/input_control_vis/input_control_options.js +++ b/test/functional/apps/visualize/input_control_vis/input_control_options.js @@ -35,7 +35,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickInputControlVis(); // set time range to time with no documents - input controls do not use time filter be default - await PageObjects.timePicker.setAbsoluteRange('2017-01-01 00:00:00.000', '2017-01-02 00:00:00.000'); + await PageObjects.timePicker.setAbsoluteRange('Jan 1, 2017 @ 00:00:00.000', 'Jan 1, 2017 @ 00:00:00.000'); await PageObjects.visualize.clickVisEditorTab('controls'); await PageObjects.visualize.addInputControl(); await comboBox.set('indexPatternSelect-0', 'logstash- '); @@ -176,7 +176,7 @@ export default function ({ getService, getPageObjects }) { }); it('should re-create control when global time filter is updated', async () => { - await PageObjects.timePicker.setAbsoluteRange('2015-01-01 00:00:00.000', '2016-01-01 00:00:00.000'); + await PageObjects.timePicker.setAbsoluteRange('Jan 1, 2015 @ 00:00:00.000', 'Jan 1, 2016 @ 00:00:00.000'); // Expect control to have values for selected time filter const menu = await comboBox.getOptionsList('listControlSelect0'); diff --git a/test/functional/page_objects/dashboard_page.js b/test/functional/page_objects/dashboard_page.js index 24604227228354..49e6bd02d681fc 100644 --- a/test/functional/page_objects/dashboard_page.js +++ b/test/functional/page_objects/dashboard_page.js @@ -527,20 +527,18 @@ export function DashboardPageProvider({ getService, getPageObjects }) { } async setTimepickerInHistoricalDataRange() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); } async setTimepickerInDataRange() { - const fromTime = '2018-01-01 00:00:00.000'; - const toTime = '2018-04-13 00:00:00.000'; + const fromTime = 'Jan 1, 2018 @ 00:00:00.000'; + const toTime = 'Apr 13, 2018 @ 00:00:00.000'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); } async setTimepickerInLogstashDataRange() { - const fromTime = '2018-04-09 00:00:00.000'; - const toTime = '2018-04-13 00:00:00.000'; + const fromTime = 'Apr 9, 2018 @ 00:00:00.000'; + const toTime = 'Apr 13, 2018 @ 00:00:00.000'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); } diff --git a/test/functional/page_objects/time_picker.js b/test/functional/page_objects/time_picker.js index 2b4147908559ad..75628c9c452c73 100644 --- a/test/functional/page_objects/time_picker.js +++ b/test/functional/page_objects/time_picker.js @@ -36,8 +36,8 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { formatDateToAbsoluteTimeString(date) { // toISOString returns dates in format 'YYYY-MM-DDTHH:mm:ss.sssZ' // Need to replace T with space and remove timezone - const dateString = date.toISOString().replace('T', ' '); - return dateString.substring(0, 23); + const DEFAULT_DATE_FORMAT = 'MMM D, YYYY @ HH:mm:ss.SSS'; + return moment(date).format(DEFAULT_DATE_FORMAT); } async getTimePickerPanel() { @@ -74,8 +74,8 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { } /** - * @param {String} fromTime YYYY-MM-DD HH:mm:ss.SSS - * @param {String} fromTime YYYY-MM-DD HH:mm:ss.SSS + * @param {String} fromTime MMM D, YYYY @ HH:mm:ss.SSS + * @param {String} toTime MMM D, YYYY @ HH:mm:ss.SSS */ async setAbsoluteRange(fromTime, toTime) { log.debug(`Setting absolute range to ${fromTime} to ${toTime}`); @@ -112,6 +112,13 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); } + get defaultStartTime() { return 'Sep 19, 2015 @ 06:31:44.000'; } + get defaultEndTime() { return 'Sep 23, 2015 @ 18:31:44.000'; } + + async setDefaultAbsoluteRange() { + await this.setAbsoluteRange(this.defaultStartTime, this.defaultEndTime); + } + async isQuickSelectMenuOpen() { return await testSubjects.exists('superDatePickerQuickMenu'); } @@ -214,7 +221,7 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { } async getTimeDurationInHours() { - const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss.SSS'; + const DEFAULT_DATE_FORMAT = 'MMM D, YYYY @ HH:mm:ss.SSS'; const { start, end } = await this.getTimeConfigAsAbsoluteTimes(); const startMoment = moment(start, DEFAULT_DATE_FORMAT); diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 4b65de57f12d81..97d57873503762 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -43,8 +43,8 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro class VisualBuilderPage { public async resetPage( - fromTime = '2015-09-19 06:31:44.000', - toTime = '2015-09-22 18:31:44.000' + fromTime = 'Sep 19, 2015 @ 06:31:44.000', + toTime = 'Sep 22, 2015 @ 18:31:44.000' ) { await PageObjects.common.navigateToUrl('visualize', 'create?type=metrics'); log.debug('Set absolute time range from "' + fromTime + '" to "' + toTime + '"'); diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json index da1bb597f57308..769acc52e207b6 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.9.0", + "@elastic/eui": "16.0.0", "react": "^16.8.0", "react-dom": "^16.8.0" } diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app.js b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app.js index b0db26c0c67434..fe004b79ba45c7 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app.js +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app.js @@ -37,7 +37,6 @@ import 'uiExports/visRequestHandlers'; import 'uiExports/visEditorTypes'; import 'uiExports/visualize'; import 'uiExports/savedObjectTypes'; -import 'uiExports/fieldFormats'; import 'uiExports/search'; import { Main } from './components/main'; diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json index 4d0444265825a2..41e1e6baca0ecc 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.9.0", + "@elastic/eui": "16.0.0", "react": "^16.8.0" } } diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json index 9df9352f76fc27..a0b03e52640fce 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.9.0", + "@elastic/eui": "16.0.0", "react": "^16.8.0" }, "scripts": { diff --git a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json index 054276b6209078..952d06c4873d4d 100644 --- a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json +++ b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "14.9.0", + "@elastic/eui": "16.0.0", "react": "^16.8.0" }, "scripts": { diff --git a/test/server_integration/config.js b/test/server_integration/config.js index 18575fcb5fcff4..77aeaa8af3b9f9 100644 --- a/test/server_integration/config.js +++ b/test/server_integration/config.js @@ -29,9 +29,7 @@ export default async function ({ readConfigFile }) { return { services: { - es: commonConfig.get('services.es'), - esArchiver: commonConfig.get('services.esArchiver'), - retry: commonConfig.get('services.retry'), + ...commonConfig.get('services'), supertest: KibanaSupertestProvider, supertestWithoutAuth: KibanaSupertestWithoutAuthProvider, esSupertest: ElasticsearchSupertestProvider, diff --git a/test/visual_regression/tests/discover/chart_visualization.js b/test/visual_regression/tests/discover/chart_visualization.js index 4e1d9bf643cf6d..540d95973b547f 100644 --- a/test/visual_regression/tests/discover/chart_visualization.js +++ b/test/visual_regression/tests/discover/chart_visualization.js @@ -33,8 +33,6 @@ export default function ({ getService, getPageObjects }) { }; describe('discover', function describeIndexTests() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; before(async function () { log.debug('load kibana index with default index pattern'); @@ -45,7 +43,7 @@ export default function ({ getService, getPageObjects }) { await kibanaServer.uiSettings.replace(defaultSettings); log.debug('discover'); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); }); describe('query', function () { @@ -132,7 +130,7 @@ export default function ({ getService, getPageObjects }) { await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'America/Phoenix' }); await browser.refresh(); await PageObjects.header.awaitKibanaChrome(); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); await PageObjects.discover.waitUntilSearchingHasFinished(); await visualTesting.snapshot({ diff --git a/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap b/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap index 0521270a7ba743..9d82cd6b5455c4 100644 --- a/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap +++ b/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap @@ -102,6 +102,8 @@ exports[`Error TRANSACTION_TYPE 1`] = `"request"`; exports[`Error URL_FULL 1`] = `undefined`; +exports[`Error USER_AGENT_NAME 1`] = `undefined`; + exports[`Error USER_ID 1`] = `undefined`; exports[`Span CLIENT_GEO_COUNTRY_ISO_CODE 1`] = `undefined`; @@ -206,6 +208,8 @@ exports[`Span TRANSACTION_TYPE 1`] = `undefined`; exports[`Span URL_FULL 1`] = `undefined`; +exports[`Span USER_AGENT_NAME 1`] = `undefined`; + exports[`Span USER_ID 1`] = `undefined`; exports[`Transaction CLIENT_GEO_COUNTRY_ISO_CODE 1`] = `undefined`; @@ -310,4 +314,6 @@ exports[`Transaction TRANSACTION_TYPE 1`] = `"transaction type"`; exports[`Transaction URL_FULL 1`] = `"http://www.elastic.co"`; +exports[`Transaction USER_AGENT_NAME 1`] = `"Other"`; + exports[`Transaction USER_ID 1`] = `"1337"`; diff --git a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.test.ts b/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.test.ts index 52471e08b1b4d4..82a679ccdd32e3 100644 --- a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.test.ts +++ b/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.test.ts @@ -34,6 +34,7 @@ describe('Transaction', () => { timestamp: { us: 1337 }, trace: { id: 'trace id' }, user: { id: '1337' }, + user_agent: { name: 'Other', original: 'test original' }, parent: { id: 'parentId' }, diff --git a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts b/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts index 552c149ce62146..d0830337e0d351 100644 --- a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts +++ b/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts @@ -10,6 +10,7 @@ export const SERVICE_NODE_NAME = 'service.node.name'; export const URL_FULL = 'url.full'; export const HTTP_REQUEST_METHOD = 'http.request.method'; export const USER_ID = 'user.id'; +export const USER_AGENT_NAME = 'user_agent.name'; export const OBSERVER_VERSION_MAJOR = 'observer.version_major'; export const OBSERVER_LISTENING = 'observer.listening'; diff --git a/x-pack/legacy/plugins/apm/common/processor_event.ts b/x-pack/legacy/plugins/apm/common/processor_event.ts index a513f620927675..83dadfc21da902 100644 --- a/x-pack/legacy/plugins/apm/common/processor_event.ts +++ b/x-pack/legacy/plugins/apm/common/processor_event.ts @@ -4,4 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -export type ProcessorEvent = 'transaction' | 'error' | 'metric'; +export enum ProcessorEvent { + transaction = 'transaction', + error = 'error', + metric = 'metric' +} diff --git a/x-pack/legacy/plugins/apm/common/transaction_types.ts b/x-pack/legacy/plugins/apm/common/transaction_types.ts index 4dd59af63047d5..1226e926b1ee32 100644 --- a/x-pack/legacy/plugins/apm/common/transaction_types.ts +++ b/x-pack/legacy/plugins/apm/common/transaction_types.ts @@ -5,5 +5,5 @@ */ export const TRANSACTION_PAGE_LOAD = 'page-load'; -export const TRANSACTION_ROUTE_CHANGE = 'route-change'; export const TRANSACTION_REQUEST = 'request'; +export const TRANSACTION_ROUTE_CHANGE = 'route-change'; diff --git a/x-pack/legacy/plugins/apm/common/viz_colors.ts b/x-pack/legacy/plugins/apm/common/viz_colors.ts new file mode 100644 index 00000000000000..cc070005409b66 --- /dev/null +++ b/x-pack/legacy/plugins/apm/common/viz_colors.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; + +function getVizColorsForTheme(theme = lightTheme) { + return [ + theme.euiColorVis0, + theme.euiColorVis1, + theme.euiColorVis2, + theme.euiColorVis3, + theme.euiColorVis4, + theme.euiColorVis5, + theme.euiColorVis6, + theme.euiColorVis7, + theme.euiColorVis8, + theme.euiColorVis9 + ]; +} + +export function getVizColorForIndex(index = 0, theme = lightTheme) { + const colors = getVizColorsForTheme(theme); + return colors[index % colors.length]; +} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap index c663c52d7d639e..cc16eef397d20c 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap +++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap @@ -122,7 +122,7 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` @@ -501,7 +501,7 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` diff --git a/x-pack/legacy/plugins/apm/public/components/app/GlobalHelpExtension/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/GlobalHelpExtension/index.tsx deleted file mode 100644 index 57a0e5ad9ddc4d..00000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/GlobalHelpExtension/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiLink } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React, { Fragment } from 'react'; -import styled from 'styled-components'; -import url from 'url'; -import { px, units } from '../../../style/variables'; -import { useKibanaCore } from '../../../../../observability/public'; - -const Container = styled.div` - margin: ${px(units.minus)} 0; -`; - -export const GlobalHelpExtension: React.SFC = () => { - const core = useKibanaCore(); - - return ( - - - - {i18n.translate('xpack.apm.feedbackMenu.provideFeedbackTitle', { - defaultMessage: 'Provide feedback for APM' - })} - - - - - {i18n.translate('xpack.apm.helpMenu.upgradeAssistantLink', { - defaultMessage: 'Upgrade assistant' - })} - - - - ); -}; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx new file mode 100644 index 00000000000000..e95f733fb4bc85 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { BrowserLineChart } from './BrowserLineChart'; + +describe('BrowserLineChart', () => { + describe('render', () => { + it('renders', () => { + expect(() => shallow()).not.toThrowError(); + }); + }); +}); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx new file mode 100644 index 00000000000000..58bc4655f730cb --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiTitle } from '@elastic/eui'; +import { TransactionLineChart } from './TransactionLineChart'; +import { + getMaxY, + getResponseTimeTickFormatter, + getResponseTimeTooltipFormatter +} from '.'; +import { getDurationFormatter } from '../../../../utils/formatters'; +import { useAvgDurationByBrowser } from '../../../../hooks/useAvgDurationByBrowser'; + +export function BrowserLineChart() { + const { data } = useAvgDurationByBrowser(); + const maxY = getMaxY(data); + const formatter = getDurationFormatter(maxY); + const formatTooltipValue = getResponseTimeTooltipFormatter(formatter); + const tickFormatY = getResponseTimeTickFormatter(formatter); + + return ( + <> + + + {i18n.translate( + 'xpack.apm.metrics.pageLoadCharts.avgPageLoadByBrowser', + { + defaultMessage: 'Avg. page load duration distribution by browser' + } + )} + + + + + ); +} diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx index 6176397170797c..bea858d1358c58 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx @@ -4,33 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGrid, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; +import { EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { useAvgDurationByCountry } from '../../../../../hooks/useAvgDurationByCountry'; + import { ChoroplethMap } from '../ChoroplethMap'; export const DurationByCountryMap: React.SFC = () => { const { data } = useAvgDurationByCountry(); return ( - - - - - - {i18n.translate( - 'xpack.apm.metrics.durationByCountryMap.avgPageLoadByCountryLabel', - { - defaultMessage: - 'Avg. page load duration distribution by country' - } - )} - - - - - - + <> + {' '} + + + {i18n.translate( + 'xpack.apm.metrics.durationByCountryMap.avgPageLoadByCountryLabel', + { + defaultMessage: 'Avg. page load duration distribution by country' + } + )} + + + + ); }; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx index c032d603599031..97794bf66687b5 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx @@ -33,6 +33,7 @@ import { MLJobLink } from '../../Links/MachineLearningLinks/MLJobLink'; import { LicenseContext } from '../../../../context/LicenseContext'; import { TransactionLineChart } from './TransactionLineChart'; import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue'; +import { BrowserLineChart } from './BrowserLineChart'; import { DurationByCountryMap } from './DurationByCountryMap'; import { TRANSACTION_PAGE_LOAD, @@ -59,31 +60,29 @@ const ShiftedEuiText = styled(EuiText)` top: 5px; `; -export class TransactionCharts extends Component { - public getMaxY = (responseTimeSeries: TimeSeries[]) => { - const coordinates = flatten( - responseTimeSeries.map((serie: TimeSeries) => serie.data as Coordinate[]) - ); - - const numbers: number[] = coordinates.map((c: Coordinate) => - c.y ? c.y : 0 - ); +export function getResponseTimeTickFormatter(formatter: TimeFormatter) { + return (t: number) => formatter(t).formatted; +} - return Math.max(...numbers, 0); +export function getResponseTimeTooltipFormatter(formatter: TimeFormatter) { + return (p: Coordinate) => { + return isValidCoordinateValue(p.y) + ? formatter(p.y).formatted + : NOT_AVAILABLE_LABEL; }; +} - public getResponseTimeTickFormatter = (formatter: TimeFormatter) => { - return (t: number) => formatter(t).formatted; - }; +export function getMaxY(responseTimeSeries: TimeSeries[]) { + const coordinates = flatten( + responseTimeSeries.map((serie: TimeSeries) => serie.data as Coordinate[]) + ); - public getResponseTimeTooltipFormatter = (formatter: TimeFormatter) => { - return (p: Coordinate) => { - return isValidCoordinateValue(p.y) - ? formatter(p.y).formatted - : NOT_AVAILABLE_LABEL; - }; - }; + const numbers: number[] = coordinates.map((c: Coordinate) => (c.y ? c.y : 0)); + return Math.max(...numbers, 0); +} + +export class TransactionCharts extends Component { public getTPMFormatter = (t: number) => { const { urlParams } = this.props; const unit = tpmUnit(urlParams.transactionType); @@ -154,7 +153,7 @@ export class TransactionCharts extends Component { const { charts, urlParams } = this.props; const { responseTimeSeries, tpmSeries } = charts; const { transactionType } = urlParams; - const maxY = this.getMaxY(responseTimeSeries); + const maxY = getMaxY(responseTimeSeries); const formatter = getDurationFormatter(maxY); return ( @@ -177,8 +176,8 @@ export class TransactionCharts extends Component { @@ -205,7 +204,18 @@ export class TransactionCharts extends Component { {transactionType === TRANSACTION_PAGE_LOAD && ( <> - + + + + + + + + + + + + )} diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts b/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts index 5fa9294a95dfd3..1806e7395a8cce 100644 --- a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts +++ b/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts @@ -83,24 +83,24 @@ export function getPathParams(pathname: string = ''): PathParams { switch (servicePageName) { case 'transactions': return { - processorEvent: 'transaction', + processorEvent: ProcessorEvent.transaction, serviceName }; case 'errors': return { - processorEvent: 'error', + processorEvent: ProcessorEvent.error, serviceName, errorGroupId: paths[3] }; case 'metrics': return { - processorEvent: 'metric', + processorEvent: ProcessorEvent.metric, serviceName, serviceNodeName }; case 'nodes': return { - processorEvent: 'metric', + processorEvent: ProcessorEvent.metric, serviceName }; case 'service-map': @@ -113,7 +113,7 @@ export function getPathParams(pathname: string = ''): PathParams { case 'traces': return { - processorEvent: 'transaction' + processorEvent: ProcessorEvent.transaction }; default: return {}; diff --git a/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.test.ts b/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.test.ts new file mode 100644 index 00000000000000..38f26c2ba9fbd7 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.test.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { renderHook } from 'react-hooks-testing-library'; +import theme from '@elastic/eui/dist/eui_theme_light.json'; +import * as useFetcherModule from './useFetcher'; +import { useAvgDurationByBrowser } from './useAvgDurationByBrowser'; + +describe('useAvgDurationByBrowser', () => { + it('returns data', () => { + const data = [ + { title: 'Other', data: [{ x: 1572530100000, y: 130010.8947368421 }] } + ]; + jest.spyOn(useFetcherModule, 'useFetcher').mockReturnValueOnce({ + data, + refetch: () => {}, + status: 'success' as useFetcherModule.FETCH_STATUS + }); + const { result } = renderHook(() => useAvgDurationByBrowser()); + + expect(result.current.data).toEqual([ + { + color: theme.euiColorVis0, + data: [{ x: 1572530100000, y: 130010.8947368421 }], + title: 'Other', + type: 'linemark' + } + ]); + }); +}); diff --git a/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.ts b/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.ts new file mode 100644 index 00000000000000..a1e9294455d548 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import theme from '@elastic/eui/dist/eui_theme_light.json'; +import { useFetcher } from './useFetcher'; +import { useUrlParams } from './useUrlParams'; +import { AvgDurationByBrowserAPIResponse } from '../../server/lib/transactions/avg_duration_by_browser'; +import { TimeSeries } from '../../typings/timeseries'; +import { getVizColorForIndex } from '../../common/viz_colors'; + +function toTimeSeries(data?: AvgDurationByBrowserAPIResponse): TimeSeries[] { + if (!data) { + return []; + } + + return data.map((item, index) => { + return { + ...item, + color: getVizColorForIndex(index, theme), + type: 'linemark' + }; + }); +} + +export function useAvgDurationByBrowser() { + const { + urlParams: { serviceName, start, end, transactionName }, + uiFilters + } = useUrlParams(); + + const { data, error, status } = useFetcher( + callApmApi => { + if (serviceName && start && end) { + return callApmApi({ + pathname: + '/api/apm/services/{serviceName}/transaction_groups/avg_duration_by_browser', + params: { + path: { serviceName }, + query: { + start, + end, + transactionName, + uiFilters: JSON.stringify(uiFilters) + } + } + }); + } + }, + [serviceName, start, end, transactionName, uiFilters] + ); + + return { + data: toTimeSeries(data), + status, + error + }; +} diff --git a/x-pack/legacy/plugins/apm/public/index.tsx b/x-pack/legacy/plugins/apm/public/index.tsx index 7d6fa70f025aae..8fd3cb0893dea2 100644 --- a/x-pack/legacy/plugins/apm/public/index.tsx +++ b/x-pack/legacy/plugins/apm/public/index.tsx @@ -4,33 +4,43 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import ReactDOM from 'react-dom'; import { npStart } from 'ui/new_platform'; import 'react-vis/dist/style.css'; import 'ui/autoload/all'; import chrome from 'ui/chrome'; +import { i18n } from '@kbn/i18n'; +import url from 'url'; + // @ts-ignore import { uiModules } from 'ui/modules'; -import { GlobalHelpExtension } from './components/app/GlobalHelpExtension'; import { plugin } from './new-platform'; import { REACT_APP_ROOT_ID } from './new-platform/plugin'; import './style/global_overrides.css'; import template from './templates/index.html'; -import { KibanaCoreContextProvider } from '../../observability/public'; + const { core } = npStart; // render APM feedback link in global help menu -core.chrome.setHelpExtension(domElement => { - ReactDOM.render( - - - , - domElement - ); - return () => { - ReactDOM.unmountComponentAtNode(domElement); - }; +core.chrome.setHelpExtension({ + appName: i18n.translate('xpack.apm.feedbackMenu.appName', { + defaultMessage: 'APM' + }), + links: [ + { + linkType: 'discuss', + href: 'https://discuss.elastic.co/c/apm' + }, + { + linkType: 'custom', + href: url.format({ + pathname: core.http.basePath.prepend('/app/kibana'), + hash: '/management/elasticsearch/upgrade_assistant' + }), + content: i18n.translate('xpack.apm.helpMenu.upgradeAssistantLink', { + defaultMessage: 'Upgrade assistant' + }) + } + ] }); // @ts-ignore diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/fetchAndTransformGcMetrics.ts b/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/fetchAndTransformGcMetrics.ts index 180537d68a2a2c..8cff6e5d3aa805 100644 --- a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/fetchAndTransformGcMetrics.ts +++ b/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/fetchAndTransformGcMetrics.ts @@ -23,16 +23,7 @@ import { METRIC_JAVA_GC_TIME } from '../../../../../../common/elasticsearch_fieldnames'; import { getBucketSize } from '../../../../helpers/get_bucket_size'; - -const colors = [ - theme.euiColorVis0, - theme.euiColorVis1, - theme.euiColorVis2, - theme.euiColorVis3, - theme.euiColorVis4, - theme.euiColorVis5, - theme.euiColorVis6 -]; +import { getVizColorForIndex } from '../../../../../../common/viz_colors'; export async function fetchAndTransformGcMetrics({ setup, @@ -148,7 +139,7 @@ export async function fetchAndTransformGcMetrics({ title: label, key: label, type: chartBase.type, - color: colors[i], + color: getVizColorForIndex(i, theme), overallValue, data }; diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts b/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts index 1e7f197435a677..03f21e4f26e7be 100644 --- a/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts +++ b/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts @@ -11,16 +11,7 @@ import { ESSearchRequest } from '../../../typings/elasticsearch'; import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations'; - -const colors = [ - theme.euiColorVis0, - theme.euiColorVis1, - theme.euiColorVis2, - theme.euiColorVis3, - theme.euiColorVis4, - theme.euiColorVis5, - theme.euiColorVis6 -]; +import { getVizColorForIndex } from '../../../common/viz_colors'; export type GenericMetricsChart = ReturnType< typeof transformDataToMetricsChart @@ -66,7 +57,8 @@ export function transformDataToMetricsChart( title: chartBase.series[seriesKey].title, key: seriesKey, type: chartBase.type, - color: chartBase.series[seriesKey].color || colors[i], + color: + chartBase.series[seriesKey].color || getVizColorForIndex(i, theme), overallValue, data: timeseriesData?.buckets.map(bucket => { diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts new file mode 100644 index 00000000000000..3f0f8a84dc62ff --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + ESSearchResponse, + ESSearchRequest +} from '../../../../../typings/elasticsearch'; + +export const response = ({ + hits: { + total: 599, + max_score: 0, + hits: [] + }, + took: 4, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0 + }, + aggregations: { + user_agent_keys: { + buckets: [{ key: 'Firefox' }, { key: 'Other' }] + }, + browsers: { + buckets: [ + { + key_as_string: '2019-10-21T04:38:20.000-05:00', + key: 1571650700000, + doc_count: 0, + user_agent: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [] + } + }, + { + key_as_string: '2019-10-21T04:40:00.000-05:00', + key: 1571650800000, + doc_count: 1, + user_agent: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'Other', + doc_count: 1, + avg_duration: { + value: 860425.0 + } + }, + { + key: 'Firefox', + doc_count: 10, + avg_duration: { + value: 86425.1 + } + } + ] + } + } + ] + } + } +} as unknown) as ESSearchResponse< + unknown, + ESSearchRequest, + { restTotalHitsAsInt: false } +>; diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts new file mode 100644 index 00000000000000..f2227524db0813 --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Setup } from '../../helpers/setup_request'; +import { fetcher } from './fetcher'; + +describe('fetcher', () => { + it('performs a search', async () => { + const search = jest.fn(); + const setup = ({ + client: { search }, + indices: {}, + uiFiltersES: [] + } as unknown) as Setup; + + await fetcher({ serviceName: 'testServiceName', setup }); + + expect(search).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts new file mode 100644 index 00000000000000..8a96a25aef50e3 --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ESFilter } from '../../../../typings/elasticsearch'; +import { PromiseReturnType } from '../../../../typings/common'; +import { + PROCESSOR_EVENT, + SERVICE_NAME, + TRANSACTION_TYPE, + USER_AGENT_NAME, + TRANSACTION_DURATION +} from '../../../../common/elasticsearch_fieldnames'; +import { rangeFilter } from '../../helpers/range_filter'; +import { getBucketSize } from '../../helpers/get_bucket_size'; +import { Options } from '.'; +import { TRANSACTION_PAGE_LOAD } from '../../../../common/transaction_types'; +import { ProcessorEvent } from '../../../../common/processor_event'; + +export type ESResponse = PromiseReturnType; + +export function fetcher(options: Options) { + const { end, client, indices, start, uiFiltersES } = options.setup; + const { serviceName } = options; + const { intervalString } = getBucketSize(start, end, 'auto'); + + const filter: ESFilter[] = [ + { term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } }, + { term: { [SERVICE_NAME]: serviceName } }, + { term: { [TRANSACTION_TYPE]: TRANSACTION_PAGE_LOAD } }, + { range: rangeFilter(start, end) }, + ...uiFiltersES + ]; + + const params = { + index: indices['apm_oss.transactionIndices'], + body: { + size: 0, + query: { bool: { filter } }, + aggs: { + user_agent_keys: { + terms: { + field: USER_AGENT_NAME + } + }, + browsers: { + date_histogram: { + extended_bounds: { + max: end, + min: start + }, + field: '@timestamp', + fixed_interval: intervalString, + min_doc_count: 0 + }, + aggs: { + user_agent: { + terms: { + field: USER_AGENT_NAME + }, + aggs: { + avg_duration: { + avg: { + field: TRANSACTION_DURATION + } + } + } + } + } + } + } + } + }; + + return client.search(params); +} diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts new file mode 100644 index 00000000000000..fe103ade24161d --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + getTransactionAvgDurationByBrowser, + Options, + AvgDurationByBrowserAPIResponse +} from '.'; +import * as transformerModule from './transformer'; +import * as fetcherModule from './fetcher'; +import { response } from './__fixtures__/responses'; + +describe('getAvgDurationByBrowser', () => { + it('returns a transformed response', async () => { + const transformer = jest + .spyOn(transformerModule, 'transformer') + .mockReturnValueOnce(({} as unknown) as AvgDurationByBrowserAPIResponse); + const search = () => {}; + const options = ({ + setup: { client: { search }, indices: {}, uiFiltersES: [] } + } as unknown) as Options; + jest + .spyOn<{ fetcher: any }, 'fetcher'>(fetcherModule, 'fetcher') + .mockResolvedValueOnce(response); + + await getTransactionAvgDurationByBrowser(options); + + expect(transformer).toHaveBeenCalledWith({ response }); + }); +}); diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts new file mode 100644 index 00000000000000..57b3c8cbe9f937 --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Coordinate } from '../../../../typings/timeseries'; +import { Setup } from '../../helpers/setup_request'; +import { fetcher } from './fetcher'; +import { transformer } from './transformer'; + +export interface Options { + serviceName: string; + setup: Setup; +} + +export type AvgDurationByBrowserAPIResponse = Array<{ + data: Coordinate[]; + title: string; +}>; + +export async function getTransactionAvgDurationByBrowser(options: Options) { + return transformer({ response: await fetcher(options) }); +} diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts new file mode 100644 index 00000000000000..5caec12c81d5d3 --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { transformer } from './transformer'; +import { response } from './__fixtures__/responses'; + +describe('transformer', () => { + it('transforms', () => { + expect(transformer({ response })).toEqual([ + { + data: [ + { x: 1571650700000, y: undefined }, + { x: 1571650800000, y: 86425.1 } + ], + title: 'Firefox' + }, + { + data: [ + { x: 1571650700000, y: undefined }, + { x: 1571650800000, y: 860425.0 } + ], + title: 'Other' + } + ]); + }); +}); diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts new file mode 100644 index 00000000000000..805f8f192bdb19 --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ESResponse } from './fetcher'; +import { AvgDurationByBrowserAPIResponse } from '.'; +import { Coordinate } from '../../../../typings/timeseries'; + +export function transformer({ + response +}: { + response: ESResponse; +}): AvgDurationByBrowserAPIResponse { + const allUserAgentKeys = new Set( + // TODO(TS-3.7-ESLINT) + // eslint-disable-next-line @typescript-eslint/camelcase + (response.aggregations?.user_agent_keys?.buckets ?? []).map(({ key }) => + key.toString() + ) + ); + const buckets = response.aggregations?.browsers?.buckets ?? []; + + const series = buckets.reduce<{ [key: string]: Coordinate[] }>( + (acc, next) => { + const userAgentBuckets = next.user_agent?.buckets ?? []; + const x = next.key; + const seenUserAgentKeys = new Set(); + + userAgentBuckets.map(userAgentBucket => { + const key = userAgentBucket.key; + const y = userAgentBucket.avg_duration?.value; + + seenUserAgentKeys.add(key.toString()); + acc[key] = (acc[key] || []).concat({ x, y }); + }); + + const emptyUserAgents = new Set( + [...allUserAgentKeys].filter(key => !seenUserAgentKeys.has(key)) + ); + + // If no user agent requests exist for this bucked, fill in the data with + // undefined + [...emptyUserAgents].map(key => { + acc[key] = (acc[key] || []).concat({ x, y: undefined }); + }); + + return acc; + }, + {} + ); + + return Object.entries(series).map(([title, data]) => ({ title, data })); +} diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/constants.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/constants.ts index 0e288de1e4600d..dcf6e8e07c45bd 100644 --- a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/constants.ts +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/constants.ts @@ -3,19 +3,5 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import theme from '@elastic/eui/dist/eui_theme_light.json'; export const MAX_KPIS = 20; - -export const COLORS = [ - theme.euiColorVis0, - theme.euiColorVis1, - theme.euiColorVis2, - theme.euiColorVis3, - theme.euiColorVis4, - theme.euiColorVis5, - theme.euiColorVis6, - theme.euiColorVis7, - theme.euiColorVis8, - theme.euiColorVis9 -]; diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.ts index 3166938090d8f7..12f66941169502 100644 --- a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.ts +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.ts @@ -18,7 +18,8 @@ import { import { Setup } from '../../helpers/setup_request'; import { rangeFilter } from '../../helpers/range_filter'; import { getMetricsDateHistogramParams } from '../../helpers/metrics'; -import { MAX_KPIS, COLORS } from './constants'; +import { MAX_KPIS } from './constants'; +import { getVizColorForIndex } from '../../../../common/viz_colors'; export async function getTransactionBreakdown({ setup, @@ -142,7 +143,7 @@ export async function getTransactionBreakdown({ const kpis = sortByOrder(visibleKpis, 'name').map((kpi, index) => { return { ...kpi, - color: COLORS[index % COLORS.length] + color: getVizColorForIndex(index) }; }); diff --git a/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts b/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts index c35b66b4536342..1735aa9da7dca3 100644 --- a/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts @@ -42,7 +42,8 @@ import { transactionGroupsChartsRoute, transactionGroupsDistributionRoute, transactionGroupsRoute, - transactionGroupsAvgDurationByCountry + transactionGroupsAvgDurationByCountry, + transactionGroupsAvgDurationByBrowser } from './transaction_groups'; import { errorGroupsLocalFiltersRoute, @@ -102,6 +103,7 @@ const createApmApi = () => { .add(transactionGroupsChartsRoute) .add(transactionGroupsDistributionRoute) .add(transactionGroupsRoute) + .add(transactionGroupsAvgDurationByBrowser) .add(transactionGroupsAvgDurationByCountry) // UI filters diff --git a/x-pack/legacy/plugins/apm/server/routes/transaction_groups.ts b/x-pack/legacy/plugins/apm/server/routes/transaction_groups.ts index 0b5c29fc298578..269f5fee9738c9 100644 --- a/x-pack/legacy/plugins/apm/server/routes/transaction_groups.ts +++ b/x-pack/legacy/plugins/apm/server/routes/transaction_groups.ts @@ -12,6 +12,7 @@ import { getTransactionBreakdown } from '../lib/transactions/breakdown'; import { getTransactionGroupList } from '../lib/transaction_groups'; import { createRoute } from './create_route'; import { uiFiltersRt, rangeRt } from './default_api_types'; +import { getTransactionAvgDurationByBrowser } from '../lib/transactions/avg_duration_by_browser'; import { getTransactionAvgDurationByCountry } from '../lib/transactions/avg_duration_by_country'; export const transactionGroupsRoute = createRoute(() => ({ @@ -144,6 +145,32 @@ export const transactionGroupsBreakdownRoute = createRoute(() => ({ } })); +export const transactionGroupsAvgDurationByBrowser = createRoute(() => ({ + path: `/api/apm/services/{serviceName}/transaction_groups/avg_duration_by_browser`, + params: { + path: t.type({ + serviceName: t.string + }), + query: t.intersection([ + t.partial({ + transactionType: t.string, + transactionName: t.string + }), + uiFiltersRt, + rangeRt + ]) + }, + handler: async (req, { path }) => { + const setup = await setupRequest(req); + const { serviceName } = path; + + return getTransactionAvgDurationByBrowser({ + serviceName, + setup + }); + } +})); + export const transactionGroupsAvgDurationByCountry = createRoute(() => ({ path: `/api/apm/services/{serviceName}/transaction_groups/avg_duration_by_country`, params: { diff --git a/x-pack/legacy/plugins/canvas/i18n/components.ts b/x-pack/legacy/plugins/canvas/i18n/components.ts index 5b9f6f00940f4b..c898db7467b445 100644 --- a/x-pack/legacy/plugins/canvas/i18n/components.ts +++ b/x-pack/legacy/plugins/canvas/i18n/components.ts @@ -531,7 +531,7 @@ export const ComponentStrings = { }), getKeyboardShortcutsLinkLabel: () => i18n.translate('xpack.canvas.helpMenu.keyboardShortcutsLinkLabel', { - defaultMessage: 'Keyboard Shortcuts', + defaultMessage: 'Keyboard shortcuts', }), }, KeyboardShortcutsDoc: { @@ -547,7 +547,7 @@ export const ComponentStrings = { }), getTitle: () => i18n.translate('xpack.canvas.keyboardShortcutsDoc.flyoutHeaderTitle', { - defaultMessage: 'Keyboard Shortcuts', + defaultMessage: 'Keyboard shortcuts', }), }, Link: { diff --git a/x-pack/legacy/plugins/canvas/public/app.js b/x-pack/legacy/plugins/canvas/public/app.js index 0ba7385cf7a9ea..760bb7a46f9552 100644 --- a/x-pack/legacy/plugins/canvas/public/app.js +++ b/x-pack/legacy/plugins/canvas/public/app.js @@ -9,7 +9,9 @@ import './angular/config'; import './angular/services'; import React from 'react'; import ReactDOM from 'react-dom'; +import { i18n } from '@kbn/i18n'; import chrome from 'ui/chrome'; +import { documentationLinks } from './lib/documentation_links'; import { CanvasRootController } from './angular/controllers'; // Import the uiExports that the application uses @@ -19,7 +21,6 @@ import 'uiExports/visRequestHandlers'; import 'uiExports/visEditorTypes'; import 'uiExports/savedObjectTypes'; import 'uiExports/spyModes'; -import 'uiExports/fieldFormats'; import 'uiExports/embeddableFactories'; import 'uiExports/interpreter'; @@ -34,6 +35,17 @@ import { HelpMenu } from './components/help_menu/help_menu'; chrome.setRootController('canvas', CanvasRootController); // add Canvas docs to help menu in global nav -chrome.helpExtension.set(domNode => { - ReactDOM.render(, domNode); +chrome.helpExtension.set({ + appName: i18n.translate('xpack.canvas.helpMenu.appName', { + defaultMessage: 'Canvas', + }), + links: [ + { + linkType: 'documentation', + href: documentationLinks.canvas, + }, + ], + content: domNode => { + ReactDOM.render(, domNode); + }, }); diff --git a/x-pack/legacy/plugins/canvas/public/components/custom_element_modal/__examples__/__snapshots__/custom_element_modal.examples.storyshot b/x-pack/legacy/plugins/canvas/public/components/custom_element_modal/__examples__/__snapshots__/custom_element_modal.examples.storyshot index 134a3da5ec6579..a42360d815176b 100644 --- a/x-pack/legacy/plugins/canvas/public/components/custom_element_modal/__examples__/__snapshots__/custom_element_modal.examples.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/custom_element_modal/__examples__/__snapshots__/custom_element_modal.examples.storyshot @@ -246,8 +246,9 @@ Array [ />
- - - +
@@ -277,8 +278,8 @@ Array [ best element ever

-
- +
@@ -599,8 +600,9 @@ Array [ />
- - - +
@@ -626,8 +628,8 @@ Array [

-
- +
@@ -949,8 +951,9 @@ Array [ />
- - - +
My Chart @@ -980,8 +983,8 @@ Array [

-
- +
@@ -1301,8 +1304,9 @@ Array [ />
- - - +
@@ -1332,8 +1336,8 @@ Array [

-
- +
diff --git a/x-pack/legacy/plugins/canvas/public/components/element_card/__examples__/__snapshots__/element_card.examples.storyshot b/x-pack/legacy/plugins/canvas/public/components/element_card/__examples__/__snapshots__/element_card.examples.storyshot index 35135b5662b24a..f09921607ef467 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_card/__examples__/__snapshots__/element_card.examples.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/element_card/__examples__/__snapshots__/element_card.examples.storyshot @@ -8,11 +8,11 @@ exports[`Storyshots components/Elements/ElementCard with click handler 1`] = ` } } > -
- - +
- +
`; @@ -60,8 +66,9 @@ exports[`Storyshots components/Elements/ElementCard with image 1`] = ` >
- - - +
Element 1 @@ -87,8 +94,8 @@ exports[`Storyshots components/Elements/ElementCard with image 1`] = ` Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce lobortis aliquet arcu ut turpis duis.

-
- +
@@ -103,11 +110,11 @@ exports[`Storyshots components/Elements/ElementCard with tags 1`] = ` } } > -
- - +
- - +
+
`; @@ -270,8 +283,9 @@ exports[`Storyshots components/Elements/ElementCard with title and description 1 >
- - - +
Element 1 @@ -301,8 +315,8 @@ exports[`Storyshots components/Elements/ElementCard with title and description 1 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce lobortis aliquet arcu ut turpis duis.

-
- +
diff --git a/x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/__snapshots__/element_grid.examples.storyshot b/x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/__snapshots__/element_grid.examples.storyshot index c192e082523cb8..01774f849dfe79 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/__snapshots__/element_grid.examples.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/element_types/__examples__/__snapshots__/element_grid.examples.storyshot @@ -14,11 +14,11 @@ exports[`Storyshots components/Elements/ElementGrid with controls 1`] = `
-
- - +
- +
@@ -115,11 +121,11 @@ exports[`Storyshots components/Elements/ElementGrid with controls 1`] = `
-
- - +
- +
@@ -216,11 +228,11 @@ exports[`Storyshots components/Elements/ElementGrid with controls 1`] = `
-
- - +
- +
@@ -332,11 +350,11 @@ exports[`Storyshots components/Elements/ElementGrid with controls and filter 1`]
-
- - +
- +
@@ -448,11 +472,11 @@ exports[`Storyshots components/Elements/ElementGrid with tags filter 1`] = `
-
- - +
- - +
+
@@ -522,11 +552,11 @@ exports[`Storyshots components/Elements/ElementGrid with text filter 1`] = `
-
- - +
- - +
+
@@ -596,11 +632,11 @@ exports[`Storyshots components/Elements/ElementGrid without controls 1`] = `
-
- - +
- - +
+
-
- - +
- - +
+
-
- - +
- - +
+
diff --git a/x-pack/legacy/plugins/canvas/public/components/help_menu/help_menu.js b/x-pack/legacy/plugins/canvas/public/components/help_menu/help_menu.js index 6e1de129e84c6b..4512ce2b4992e7 100644 --- a/x-pack/legacy/plugins/canvas/public/components/help_menu/help_menu.js +++ b/x-pack/legacy/plugins/canvas/public/components/help_menu/help_menu.js @@ -5,8 +5,7 @@ */ import React, { Fragment, PureComponent } from 'react'; -import { EuiButton, EuiHorizontalRule, EuiText, EuiSpacer, EuiPortal } from '@elastic/eui'; -import { documentationLinks } from '../../lib/documentation_links'; +import { EuiButtonEmpty, EuiPortal } from '@elastic/eui'; import { KeyboardShortcutsDoc } from '../keyboard_shortcuts_doc'; import { ComponentStrings } from '../../../i18n'; @@ -26,19 +25,14 @@ export class HelpMenu extends PureComponent { render() { return ( - - - -

{strings.getHelpMenuDescription()}

-
- - - {strings.getDocumentationLinkLabel()} - - - + {strings.getKeyboardShortcutsLinkLabel()} - + {this.state.isFlyoutVisible && ( diff --git a/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.examples.storyshot b/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.examples.storyshot index cf37481d8ad7cb..e02d64e3e0647f 100644 --- a/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.examples.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.examples.storyshot @@ -1,1601 +1,1599 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Storyshots components/KeyboardShortcutsDoc default 1`] = ` - +
-
-
+
+ } + tabIndex={1} + /> +
+
- -
-

- Keyboard Shortcuts -

-
+ Keyboard shortcuts + +
+
-
-

- Element controls -

-
-
-
- Cut -
-
- - - - CTRL - - - - - - X - - - -
-
- Copy -
-
- - - - CTRL - - - - - - C - - - -
-
- Paste -
-
- - - - CTRL - - - - - - V - - - -
-
- Clone -
-
- - - - CTRL - - - - - - D - - - -
-
- Delete -
-
- - - - DEL - - - - - - or - - - - - - BACKSPACE - - - -
-
- Bring forward -
-
- - - - CTRL - - - - - - ↑ - - - -
-
- Bring to front -
-
- - - - CTRL - - - - - - SHIFT - - - - - - ↑ - - - -
-
- Send backward -
-
- - - - CTRL - - - - - - ↓ - - - -
-
- Send to back -
-
- - - - CTRL - - - - - - SHIFT - - - - - - ↓ - - - -
-
- Group -
-
- - - - G - - - -
-
- Ungroup -
-
- - - - U - - - -
-
- Shift up by 10px -
-
- - - - ↑ - - - -
-
- Shift down by 10px -
-
- - - - ↓ - - - -
-
- Shift left by 10px -
-
- - - - ← - - - -
-
- Shift right by 10px -
-
- - - - → - - - -
-
- Shift up by 1px -
-
- - - - SHIFT - - - - - - ↑ - - - -
-
- Shift down by 1px -
-
- - - - SHIFT - - - - - - ↓ - - - -
-
- Shift left by 1px -
-
- - - - SHIFT - - - - - - ← - - - -
-
- Shift right by 1px -
-
- - - - SHIFT - - - - - - → - - - -
-
-
-
+ Element controls + +
+
+
+ Cut +
+
+ + + + CTRL + + + + + + X + + + +
+
+ Copy +
+
+ + + + CTRL + + + + + + C + + + +
+
+ Paste +
+
+ + + + CTRL + + + + + + V + + + +
+
+ Clone +
+
+ + + + CTRL + + + + + + D + + + +
+
+ Delete +
+
+ + + + DEL + + + + + + or + + + + + + BACKSPACE + + + +
+
+ Bring forward +
+
+ + + + CTRL + + + + + + ↑ + + + +
+
+ Bring to front +
+
+ + + + CTRL + + + + + + SHIFT + + + + + + ↑ + + + +
+
+ Send backward +
+
+ + + + CTRL + + + + + + ↓ + + + +
+
+ Send to back +
+
+ + + + CTRL + + + + + + SHIFT + + + + + + ↓ + + + +
+
+ Group +
+
+ + + + G + + + +
+
+ Ungroup +
+
+ + + + U + + + +
+
+ Shift up by 10px +
+
+ + + + ↑ + + + +
+
+ Shift down by 10px +
+
+ + + + ↓ + + + +
+
+ Shift left by 10px +
+
+ + + + ← + + + +
+
+ Shift right by 10px +
+
+ + + + → + + + +
+
+ Shift up by 1px +
+
+ + + + SHIFT + + + + + + ↑ + + + +
+
+ Shift down by 1px +
+
+ + + + SHIFT + + + + + + ↓ + + + +
+
+ Shift left by 1px +
+
+ + + + SHIFT + + + + + + ← + + + +
+
+ Shift right by 1px +
+
+ + + + SHIFT + + + + + + → + + + +
+
+
+
+

+ Expression controls +

+
+
-

- Expression controls -

-
-
-
- Run whole expression -
-
- - - - CTRL - - - - - - ENTER - - - -
-
-
-
+
+ Run whole expression +
+
+ + + + CTRL + + + + + + ENTER + + + +
+
+
+
+

+ Editor controls +

+
+
-

- Editor controls -

-
-
-
- Select multiple elements -
-
- - - - SHIFT - - - - - - CLICK - - - -
-
- Resize from center -
-
- - - - ALT - - - - - - DRAG - - - -
-
- Move, resize, and rotate without snapping -
-
- - - - CTRL - - - - - - DRAG - - - -
-
- Select element below -
-
- - - - CTRL - - - - - - CLICK - - - -
-
- Undo last action -
-
- - - - CTRL - - - - - - Z - - - -
-
- Redo last action -
-
- - - - CTRL - - - - - - SHIFT - - - - - - Z - - - -
-
- Go to previous page -
-
- - - - ALT - - - - - - [ - - - -
-
- Go to next page -
-
- - - - ALT - - - - - - ] - - - -
-
- Toggle edit mode -
-
- - - - ALT - - - - - - E - - - -
-
- Show grid -
-
- - - - ALT - - - - - - G - - - -
-
- Refresh workpad -
-
- - - - ALT - - - - - - R - - - -
-
- Zoom in -
-
- - - - CTRL - - - - - - ALT - - - - - - + - - - -
-
- Zoom out -
-
- - - - CTRL - - - - - - ALT - - - - - - - - - - -
-
- Reset zoom to 100% -
-
- - - - CTRL - - - - - - ALT - - - - - - [ - - - -
-
- Enter presentation mode -
-
- - - - ALT - - - - - - F - - - - - - or - - - - - - ALT - - - - - - P - - - -
-
-
-
+
+ Select multiple elements +
+
+ + + + SHIFT + + + + + + CLICK + + + +
+
+ Resize from center +
+
+ + + + ALT + + + + + + DRAG + + + +
+
+ Move, resize, and rotate without snapping +
+
+ + + + CTRL + + + + + + DRAG + + + +
+
+ Select element below +
+
+ + + + CTRL + + + + + + CLICK + + + +
+
+ Undo last action +
+
+ + + + CTRL + + + + + + Z + + + +
+
+ Redo last action +
+
+ + + + CTRL + + + + + + SHIFT + + + + + + Z + + + +
+
+ Go to previous page +
+
+ + + + ALT + + + + + + [ + + + +
+
+ Go to next page +
+
+ + + + ALT + + + + + + ] + + + +
+
+ Toggle edit mode +
+
+ + + + ALT + + + + + + E + + + +
+
+ Show grid +
+
+ + + + ALT + + + + + + G + + + +
+
+ Refresh workpad +
+
+ + + + ALT + + + + + + R + + + +
+
+ Zoom in +
+
+ + + + CTRL + + + + + + ALT + + + + + + + + + + +
+
+ Zoom out +
+
+ + + + CTRL + + + + + + ALT + + + + + + - + + + +
+
+ Reset zoom to 100% +
+
+ + + + CTRL + + + + + + ALT + + + + + + [ + + + +
+
+ Enter presentation mode +
+
+ + + + ALT + + + + + + F + + + + + + or + + + + + + ALT + + + + + + P + + + +
+
+
+
+

+ Presentation controls +

+
+
-

- Presentation controls -

-
-
-
- Enter presentation mode -
-
- - - - ALT - - - - - - F - - - - - - or - - - - - - ALT - - - - - - P - - - -
-
- Exit presentation mode -
-
- - - - ESC - - - -
-
- Go to previous page -
-
- - - - ALT - - - - - - [ - - - - - - or - - - - - - BACKSPACE - - - - - - or - - - - - - ← - - - -
-
- Go to next page -
-
- - - - ALT - - - - - - ] - - - - - - or - - - - - - SPACE - - - - - - or - - - - - - → - - - -
-
- Refresh workpad -
-
- - - - ALT - - - - - - R - - - -
-
- Toggle page cycling -
-
- - - - P - - - -
-
-
-
+
+ Enter presentation mode +
+
+ + + + ALT + + + + + + F + + + + + + or + + + + + + ALT + + + + + + P + + + +
+
+ Exit presentation mode +
+
+ + + + ESC + + + +
+
+ Go to previous page +
+
+ + + + ALT + + + + + + [ + + + + + + or + + + + + + BACKSPACE + + + + + + or + + + + + + ← + + + +
+
+ Go to next page +
+
+ + + + ALT + + + + + + ] + + + + + + or + + + + + + SPACE + + + + + + or + + + + + + → + + + +
+
+ Refresh workpad +
+
+ + + + ALT + + + + + + R + + + +
+
+ Toggle page cycling +
+
+ + + + P + + + +
+
+
-
- +
+
`; diff --git a/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__examples__/__snapshots__/autoplay_settings.examples.storyshot b/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__examples__/__snapshots__/autoplay_settings.examples.storyshot index 1e66e19b3c0e18..e9f496bfe6358f 100644 --- a/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__examples__/__snapshots__/autoplay_settings.examples.storyshot +++ b/x-pack/legacy/plugins/canvas/shareable_runtime/components/footer/settings/__examples__/__snapshots__/autoplay_settings.examples.storyshot @@ -27,7 +27,7 @@ exports[`Storyshots shareables/Footer/Settings/AutoplaySettings component: off, >
`; -exports[` can navigate Toolbar Settings, closes when activated 3`] = `"
Settings

Hide Toolbar

Hide the toolbar when the mouse is not within the Canvas?
"`; +exports[` can navigate Toolbar Settings, closes when activated 3`] = `"
Settings

Hide Toolbar

Hide the toolbar when the mouse is not within the Canvas?
"`; diff --git a/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js b/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js index 5f7ac218e1b982..8093c57d2631aa 100644 --- a/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js +++ b/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js @@ -26,7 +26,6 @@ import 'uiExports/embeddableActions'; import 'uiExports/embeddableFactories'; import 'uiExports/navbarExtensions'; import 'uiExports/docViews'; -import 'uiExports/fieldFormats'; import 'uiExports/search'; import 'uiExports/shareContextMenuExtensions'; import _ from 'lodash'; @@ -38,6 +37,8 @@ import 'ui/agg_response'; import 'ui/agg_types'; import 'leaflet'; import { npStart } from 'ui/new_platform'; +import { localApplicationService } from 'plugins/kibana/local_application_service'; + import { showAppRedirectNotification } from 'ui/notify'; import { DashboardConstants, createDashboardEditUrl } from 'plugins/kibana/dashboard/dashboard_constants'; @@ -45,6 +46,8 @@ import { DashboardConstants, createDashboardEditUrl } from 'plugins/kibana/dashb uiModules.get('kibana') .config(dashboardConfigProvider => dashboardConfigProvider.turnHideWriteControlsOn()); +localApplicationService.attachToAngular(routes); + routes.enable(); routes.otherwise({ redirectTo: defaultUrl() }); diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx b/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx index 8dede207b803c7..5fae9720db39a5 100644 --- a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx +++ b/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx @@ -18,7 +18,7 @@ import { i18n } from '@kbn/i18n'; import classNames from 'classnames'; import { FormattedMessage } from '@kbn/i18n/react'; import { connect } from 'react-redux'; -import { IDataPluginServices } from 'src/legacy/core_plugins/data/public/types'; +import { IDataPluginServices } from 'src/plugins/data/public'; import { GraphState, hasDatasourceSelector, diff --git a/x-pack/legacy/plugins/graph/public/components/search_bar.tsx b/x-pack/legacy/plugins/graph/public/components/search_bar.tsx index b6200d831b248c..82e50c702997f5 100644 --- a/x-pack/legacy/plugins/graph/public/components/search_bar.tsx +++ b/x-pack/legacy/plugins/graph/public/components/search_bar.tsx @@ -10,8 +10,7 @@ import React, { useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { connect } from 'react-redux'; import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; -import { IDataPluginServices } from 'src/legacy/core_plugins/data/public/types'; -import { Query } from 'src/plugins/data/public'; +import { IDataPluginServices, Query } from 'src/plugins/data/public'; import { IndexPatternSavedObject, IndexPatternProvider } from '../types'; import { QueryBarInput, IndexPattern } from '../../../../../../src/legacy/core_plugins/data/public'; import { openSourceModal } from '../services/source_modal'; diff --git a/x-pack/legacy/plugins/graph/public/index.ts b/x-pack/legacy/plugins/graph/public/index.ts index 48420d403653f1..988aa786950950 100644 --- a/x-pack/legacy/plugins/graph/public/index.ts +++ b/x-pack/legacy/plugins/graph/public/index.ts @@ -6,7 +6,6 @@ // legacy imports currently necessary to power Graph // for a cutover all of these have to be resolved -import 'uiExports/fieldFormats'; import 'uiExports/savedObjectTypes'; import 'uiExports/autocompleteProviders'; import 'ui/autoload/all'; @@ -20,6 +19,7 @@ import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_regis import { npSetup, npStart } from 'ui/new_platform'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { start as data } from '../../../../../src/legacy/core_plugins/data/public/legacy'; +import { start as navigation } from '../../../../../src/legacy/core_plugins/navigation/public/legacy'; import { GraphPlugin } from './plugin'; // @ts-ignore @@ -53,6 +53,7 @@ async function getAngularInjectedDependencies(): Promise; + navigation: NavigationStart; } export interface GraphPluginSetupDependencies { @@ -30,6 +32,7 @@ export interface GraphPluginStartDependencies { export class GraphPlugin implements Plugin { private dataStart: DataStart | null = null; + private navigationStart: NavigationStart | null = null; private npDataStart: ReturnType | null = null; private savedObjectsClient: SavedObjectsClientContract | null = null; private angularDependencies: LegacyAngularInjectedDependencies | null = null; @@ -42,6 +45,7 @@ export class GraphPlugin implements Plugin { const { renderApp } = await import('./render_app'); return renderApp({ ...params, + navigation: this.navigationStart!, npData: this.npDataStart!, savedObjectsClient: this.savedObjectsClient!, xpackInfo, @@ -66,9 +70,9 @@ export class GraphPlugin implements Plugin { start( core: CoreStart, - { data, npData, __LEGACY: { angularDependencies } }: GraphPluginStartDependencies + { data, npData, navigation, __LEGACY: { angularDependencies } }: GraphPluginStartDependencies ) { - // TODO is this really the right way? I though the app context would give us those + this.navigationStart = navigation; this.dataStart = data; this.npDataStart = npData; this.angularDependencies = angularDependencies; diff --git a/x-pack/legacy/plugins/graph/public/render_app.ts b/x-pack/legacy/plugins/graph/public/render_app.ts index a8a86f4d1f850e..18cdf0ddd81b24 100644 --- a/x-pack/legacy/plugins/graph/public/render_app.ts +++ b/x-pack/legacy/plugins/graph/public/render_app.ts @@ -25,6 +25,7 @@ import { DataStart } from 'src/legacy/core_plugins/data/public'; import { AppMountContext, ChromeStart, + LegacyCoreStart, SavedObjectsClientContract, ToastsStart, UiSettingsClientContract, @@ -32,6 +33,7 @@ import { // @ts-ignore import { initGraphApp } from './app'; import { Plugin as DataPlugin } from '../../../../../src/plugins/data/public'; +import { NavigationStart } from '../../../../../src/legacy/core_plugins/navigation/public'; /** * These are dependencies of the Graph app besides the base dependencies @@ -44,6 +46,7 @@ export interface GraphDependencies extends LegacyAngularInjectedDependencies { appBasePath: string; capabilities: Record>; coreStart: AppMountContext['core']; + navigation: NavigationStart; chrome: ChromeStart; config: UiSettingsClientContract; toastNotifications: ToastsStart; @@ -75,8 +78,8 @@ export interface LegacyAngularInjectedDependencies { } export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies) => { - const graphAngularModule = createLocalAngularModule(deps.coreStart); - configureAppAngularModule(graphAngularModule); + const graphAngularModule = createLocalAngularModule(deps.navigation); + configureAppAngularModule(graphAngularModule, deps.coreStart as LegacyCoreStart, true); initGraphApp(graphAngularModule, deps); const $injector = mountGraphApp(appBasePath, element); return () => $injector.get('$rootScope').$destroy(); @@ -104,9 +107,9 @@ function mountGraphApp(appBasePath: string, element: HTMLElement) { return $injector; } -function createLocalAngularModule(core: AppMountContext['core']) { +function createLocalAngularModule(navigation: NavigationStart) { createLocalI18nModule(); - createLocalTopNavModule(); + createLocalTopNavModule(navigation); createLocalConfirmModalModule(); const graphAngularModule = angular.module(moduleName, [ @@ -125,11 +128,11 @@ function createLocalConfirmModalModule() { .directive('confirmModal', reactDirective => reactDirective(EuiConfirmModal)); } -function createLocalTopNavModule() { +function createLocalTopNavModule(navigation: NavigationStart) { angular .module('graphTopNav', ['react']) .directive('kbnTopNav', createTopNavDirective) - .directive('kbnTopNavHelper', createTopNavHelper); + .directive('kbnTopNavHelper', createTopNavHelper(navigation.ui)); } function createLocalI18nModule() { diff --git a/x-pack/legacy/plugins/infra/public/components/help_center_content.tsx b/x-pack/legacy/plugins/infra/public/components/help_center_content.tsx index 0560c42d7498b0..3095230ab8311c 100644 --- a/x-pack/legacy/plugins/infra/public/components/help_center_content.tsx +++ b/x-pack/legacy/plugins/infra/public/components/help_center_content.tsx @@ -4,37 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiLink } from '@elastic/eui'; import React, { useEffect } from 'react'; -import ReactDOM from 'react-dom'; import chrome from 'ui/chrome'; interface HelpCenterContentProps { feedbackLink: string; - feedbackLinkText: string; + appName: string; } -const Content: React.FC = ({ feedbackLink, feedbackLinkText }) => ( - - {feedbackLinkText} - -); - -export const HelpCenterContent: React.FC = ({ - feedbackLink, - feedbackLinkText, -}) => { +export const HelpCenterContent: React.FC = ({ feedbackLink, appName }) => { useEffect(() => { - chrome.helpExtension.set(domElement => { - ReactDOM.render( - , - domElement - ); - return () => { - ReactDOM.unmountComponentAtNode(domElement); - }; + chrome.helpExtension.set({ + appName, + links: [ + { + linkType: 'discuss', + href: feedbackLink, + }, + ], }); - }, [feedbackLink, feedbackLinkText]); + }, [feedbackLink, appName]); return null; }; diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/index.tsx b/x-pack/legacy/plugins/infra/public/pages/infrastructure/index.tsx index 6affcae1805b3c..fe48fcc62f77d3 100644 --- a/x-pack/legacy/plugins/infra/public/pages/infrastructure/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/infrastructure/index.tsx @@ -41,10 +41,9 @@ export const InfrastructurePage = injectUICapabilities(
- +
; - - export const EuiShowFor: React.SFC; - type EuiInMemoryTableProps = CommonProps & { items?: any; columns?: any; diff --git a/x-pack/legacy/plugins/lens/common/constants.ts b/x-pack/legacy/plugins/lens/common/constants.ts index 787a348a788b8b..c2eed1940fa1ab 100644 --- a/x-pack/legacy/plugins/lens/common/constants.ts +++ b/x-pack/legacy/plugins/lens/common/constants.ts @@ -6,9 +6,13 @@ export const PLUGIN_ID = 'lens'; -export const BASE_APP_URL = '/app/lens'; +export const BASE_APP_URL = '/app/kibana'; export const BASE_API_URL = '/api/lens'; +export function getBasePath() { + return `${BASE_APP_URL}#/lens`; +} + export function getEditPath(id: string) { - return `${BASE_APP_URL}#/edit/${encodeURIComponent(id)}`; + return `${BASE_APP_URL}#/lens/edit/${encodeURIComponent(id)}`; } diff --git a/x-pack/legacy/plugins/lens/index.ts b/x-pack/legacy/plugins/lens/index.ts index 20f92ebbe0654a..d4cea28d140859 100644 --- a/x-pack/legacy/plugins/lens/index.ts +++ b/x-pack/legacy/plugins/lens/index.ts @@ -12,7 +12,7 @@ import mappings from './mappings.json'; import { PLUGIN_ID, getEditPath } from './common'; import { lensServerPlugin } from './server'; -const NOT_INTERNATIONALIZED_PRODUCT_NAME = 'Lens Visualizations'; +export const NOT_INTERNATIONALIZED_PRODUCT_NAME = 'Lens Visualizations'; export const lens: LegacyPluginInitializer = kibana => { return new kibana.Plugin({ @@ -26,10 +26,11 @@ export const lens: LegacyPluginInitializer = kibana => { app: { title: NOT_INTERNATIONALIZED_PRODUCT_NAME, description: 'Explore and visualize data.', - main: `plugins/${PLUGIN_ID}/index`, + main: `plugins/${PLUGIN_ID}/redirect`, listed: false, }, - embeddableFactories: ['plugins/lens/register_embeddable'], + visualize: [`plugins/${PLUGIN_ID}/legacy`], + embeddableFactories: [`plugins/${PLUGIN_ID}/legacy`], styleSheetPaths: resolve(__dirname, 'public/index.scss'), mappings, visTypes: ['plugins/lens/register_vis_type_alias'], diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx index 3fcb6609f28f18..ce05af46ade668 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx @@ -99,6 +99,12 @@ describe('Lens App', () => { data: { query: { filterManager: createMockFilterManager(), + timefilter: { + timefilter: { + getTime: jest.fn(() => ({ from: 'now-7d', to: 'now' })), + setTime: jest.fn(), + }, + }, }, }, dataShim: { @@ -109,7 +115,6 @@ describe('Lens App', () => { }), }, }, - timefilter: { history: {} }, }, storage: { get: jest.fn(), diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx index fc5088c1271ad4..553b98643f8a58 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx @@ -62,33 +62,35 @@ export function App({ docStorage: SavedObjectStore; redirectTo: (id?: string) => void; }) { - const timeDefaults = core.uiSettings.get('timepicker:timeDefaults'); const language = storage.get('kibana.userQueryLanguage') || core.uiSettings.get('search:queryLanguage'); - const [state, setState] = useState({ - isLoading: !!docId, - isSaveModalVisible: false, - indexPatternsForTopNav: [], - query: { query: '', language }, - dateRange: { - fromDate: timeDefaults.from, - toDate: timeDefaults.to, - }, - filters: [], + const [state, setState] = useState(() => { + const currentRange = data.query.timefilter.timefilter.getTime(); + return { + isLoading: !!docId, + isSaveModalVisible: false, + indexPatternsForTopNav: [], + query: { query: '', language }, + dateRange: { + fromDate: currentRange.from, + toDate: currentRange.to, + }, + filters: [], + }; }); const { lastKnownDoc } = state; useEffect(() => { - const subscription = data.query.filterManager.getUpdates$().subscribe({ + const filterSubscription = data.query.filterManager.getUpdates$().subscribe({ next: () => { setState(s => ({ ...s, filters: data.query.filterManager.getFilters() })); trackUiEvent('app_filters_updated'); }, }); return () => { - subscription.unsubscribe(); + filterSubscription.unsubscribe(); }; }, []); @@ -199,6 +201,7 @@ export function App({ dateRange.from !== state.dateRange.fromDate || dateRange.to !== state.dateRange.toDate ) { + data.query.timefilter.timefilter.setTime(dateRange); trackUiEvent('app_date_change'); } else { trackUiEvent('app_query_change'); diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/plugin.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/plugin.tsx index 56c19ea2bb9f26..60a375f696f7b8 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/plugin.tsx @@ -4,18 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ +import 'ui/autoload/all'; +// Used to run esaggs queries +import 'uiExports/fieldFormats'; +import 'uiExports/search'; +import 'uiExports/visRequestHandlers'; +import 'uiExports/visResponseHandlers'; +// Used for kibana_context function +import 'uiExports/savedObjectTypes'; + import React from 'react'; import { I18nProvider, FormattedMessage } from '@kbn/i18n/react'; import { HashRouter, Switch, Route, RouteComponentProps } from 'react-router-dom'; +import { render, unmountComponentAtNode } from 'react-dom'; import chrome from 'ui/chrome'; import { CoreSetup, CoreStart } from 'src/core/public'; -import { npSetup, npStart } from 'ui/new_platform'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { DataStart } from '../../../../../../src/legacy/core_plugins/data/public'; -import { start as dataShimStart } from '../../../../../../src/legacy/core_plugins/data/public/legacy'; import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; import { editorFrameSetup, editorFrameStart, editorFrameStop } from '../editor_frame_plugin'; import { indexPatternDatasourceSetup, indexPatternDatasourceStop } from '../indexpattern_plugin'; +import { addHelpMenuToAppChrome } from '../help_menu_util'; import { SavedObjectIndexStore } from '../persistence'; import { xyVisualizationSetup, xyVisualizationStop } from '../xy_visualization_plugin'; import { metricVisualizationSetup, metricVisualizationStop } from '../metric_visualization_plugin'; @@ -31,10 +40,15 @@ import { stopReportManager, trackUiEvent, } from '../lens_ui_telemetry'; +import { LocalApplicationService } from '../../../../../../src/legacy/core_plugins/kibana/public/local_application_service'; +import { NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../../index'; export interface LensPluginStartDependencies { data: DataPublicPluginStart; dataShim: DataStart; + __LEGACY: { + localApplicationService: LocalApplicationService; + }; } export class AppPlugin { private instance: EditorFrameInstance | null = null; @@ -58,11 +72,16 @@ export class AppPlugin { editorFrameSetupInterface.registerDatasource('indexpattern', indexPattern); } - start(core: CoreStart, { data, dataShim }: LensPluginStartDependencies) { + start( + core: CoreStart, + { data, dataShim, __LEGACY: { localApplicationService } }: LensPluginStartDependencies + ) { if (this.store === null) { throw new Error('Start lifecycle called before setup lifecycle'); } + addHelpMenuToAppChrome(core.chrome); + const store = this.store; const editorFrameStartInterface = editorFrameStart(); @@ -89,9 +108,9 @@ export class AppPlugin { docStorage={store} redirectTo={id => { if (!id) { - routeProps.history.push('/'); + routeProps.history.push('/lens'); } else { - routeProps.history.push(`/edit/${id}`); + routeProps.history.push(`/lens/edit/${id}`); } }} /> @@ -103,17 +122,27 @@ export class AppPlugin { return ; } - return ( - - - - - - - - - - ); + localApplicationService.register({ + id: 'lens', + title: NOT_INTERNATIONALIZED_PRODUCT_NAME, + mount: async (context, params) => { + render( + + + + + + + + + , + params.element + ); + return () => { + unmountComponentAtNode(params.element); + }; + }, + }); } stop() { @@ -131,10 +160,3 @@ export class AppPlugin { editorFrameStop(); } } - -const app = new AppPlugin(); - -export const appSetup = () => app.setup(npSetup.core, {}); -export const appStart = () => - app.start(npStart.core, { dataShim: dataShimStart, data: npStart.plugins.data }); -export const appStop = () => app.stop(); diff --git a/x-pack/legacy/plugins/lens/public/help_menu_util.tsx b/x-pack/legacy/plugins/lens/public/help_menu_util.tsx index 30a05dbc38537c..9ead31690e854c 100644 --- a/x-pack/legacy/plugins/lens/public/help_menu_util.tsx +++ b/x-pack/legacy/plugins/lens/public/help_menu_util.tsx @@ -4,55 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { EuiHorizontalRule, EuiSpacer, EuiLink, EuiText, EuiIcon, EuiButton } from '@elastic/eui'; import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { Chrome } from 'ui/chrome'; +import { ChromeStart } from 'kibana/public'; -const docsPage = 'lens'; - -export function addHelpMenuToAppChrome(chrome: Chrome) { - chrome.helpExtension.set(domElement => { - render(, domElement); - return () => { - unmountComponentAtNode(domElement); - }; +export function addHelpMenuToAppChrome(chrome: ChromeStart) { + chrome.setHelpExtension({ + appName: 'Lens', + links: [ + { + linkType: 'documentation', + href: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/lens.html`, + }, + { + linkType: 'github', + title: '[Lens]', + labels: ['Feature:Lens'], + }, + ], }); } - -function HelpMenu() { - return ( - <> - - {docsPage && ( - <> - - - - - - )} - - - -   - - {i18n.translate('xpack.lens.helpMenu.feedbackLinkText', { - defaultMessage: 'Provide feedback for the Lens application', - })} - - - - ); -} diff --git a/x-pack/legacy/plugins/lens/public/index.ts b/x-pack/legacy/plugins/lens/public/index.ts index 2a5422d4bb8a16..9f4141dbcae7df 100644 --- a/x-pack/legacy/plugins/lens/public/index.ts +++ b/x-pack/legacy/plugins/lens/public/index.ts @@ -5,36 +5,3 @@ */ export * from './types'; - -import 'ui/autoload/all'; -// Used to run esaggs queries -import 'uiExports/fieldFormats'; -import 'uiExports/search'; -import 'uiExports/visRequestHandlers'; -import 'uiExports/visResponseHandlers'; -import 'uiExports/interpreter'; -// Used for kibana_context function -import 'uiExports/savedObjectTypes'; - -import { render, unmountComponentAtNode } from 'react-dom'; -import { IScope } from 'angular'; -import chrome from 'ui/chrome'; -import { appStart, appSetup, appStop } from './app_plugin'; -import { PLUGIN_ID } from '../common'; -import { addHelpMenuToAppChrome } from './help_menu_util'; - -// TODO: Convert this to the "new platform" way of doing UI -function Root($scope: IScope, $element: JQLite) { - const el = $element[0]; - $scope.$on('$destroy', () => { - unmountComponentAtNode(el); - appStop(); - }); - - appSetup(); - addHelpMenuToAppChrome(chrome); - - return render(appStart(), el); -} - -chrome.setRootController(PLUGIN_ID, Root); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.test.tsx index affb1accbbef46..dc23df250ebd47 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.test.tsx @@ -17,7 +17,6 @@ import { EuiProgress } from '@elastic/eui'; import { documentField } from './document_field'; jest.mock('ui/new_platform'); -jest.mock('../../../../../../src/legacy/ui/public/registry/field_formats'); const initialState: IndexPatternPrivateState = { indexPatternRefs: [], diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.test.tsx index 9956c0ec330616..1b49eb6bca7faa 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.test.tsx @@ -10,63 +10,58 @@ import { EuiLoadingSpinner, EuiPopover } from '@elastic/eui'; import { FieldItem, FieldItemProps } from './field_item'; import { coreMock } from 'src/core/public/mocks'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { npStart } from 'ui/new_platform'; +import { FieldFormatsStart } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from './types'; jest.mock('ui/new_platform'); -// Formatter must be mocked to return a string, or the rendering will fail -jest.mock('../../../../../../src/legacy/ui/public/registry/field_formats', () => ({ - fieldFormats: { - getDefaultInstance: jest.fn().mockReturnValue({ - convert: jest.fn().mockReturnValue((s: unknown) => JSON.stringify(s)), - }), - }, -})); - const waitForPromises = () => new Promise(resolve => setTimeout(resolve)); -const indexPattern = { - id: '1', - title: 'my-fake-index-pattern', - timeFieldName: 'timestamp', - fields: [ - { - name: 'timestamp', - type: 'date', - aggregatable: true, - searchable: true, - }, - { - name: 'bytes', - type: 'number', - aggregatable: true, - searchable: true, - }, - { - name: 'memory', - type: 'number', - aggregatable: true, - searchable: true, - }, - { - name: 'unsupported', - type: 'geo', - aggregatable: true, - searchable: true, - }, - { - name: 'source', - type: 'string', - aggregatable: true, - searchable: true, - }, - ], -}; - describe('IndexPattern Field Item', () => { let defaultProps: FieldItemProps; + let indexPattern: IndexPattern; let core: ReturnType; beforeEach(() => { + indexPattern = { + id: '1', + title: 'my-fake-index-pattern', + timeFieldName: 'timestamp', + fields: [ + { + name: 'timestamp', + type: 'date', + aggregatable: true, + searchable: true, + }, + { + name: 'bytes', + type: 'number', + aggregatable: true, + searchable: true, + }, + { + name: 'memory', + type: 'number', + aggregatable: true, + searchable: true, + }, + { + name: 'unsupported', + type: 'geo', + aggregatable: true, + searchable: true, + }, + { + name: 'source', + type: 'string', + aggregatable: true, + searchable: true, + }, + ], + } as IndexPattern; + core = coreMock.createSetup(); core.http.post.mockClear(); defaultProps = { @@ -87,6 +82,12 @@ describe('IndexPattern Field Item', () => { }, exists: true, }; + + npStart.plugins.data.fieldFormats = ({ + getDefaultInstance: jest.fn(() => ({ + convert: jest.fn((s: unknown) => JSON.stringify(s)), + })), + } as unknown) as FieldFormatsStart; }); it('should request field stats every time the button is clicked', async () => { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx index 3536ad80538916..20505107be1220 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx @@ -7,48 +7,49 @@ import React, { useState } from 'react'; import DateMath from '@elastic/datemath'; import { + EuiButtonGroup, EuiFlexGroup, EuiFlexItem, - EuiProgress, - EuiPopover, - EuiLoadingSpinner, + EuiIconTip, EuiKeyboardAccessible, - EuiText, - EuiToolTip, - EuiButtonGroup, + EuiLoadingSpinner, + EuiPopover, EuiPopoverFooter, EuiPopoverTitle, - EuiIconTip, + EuiProgress, + EuiText, + EuiToolTip, } from '@elastic/eui'; +import { npStart } from 'ui/new_platform'; import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; import { - Chart, Axis, + BarSeries, + Chart, + DataSeriesColorsValues, getAxisId, getSpecId, - BarSeries, + niceTimeFormatter, Position, ScaleType, Settings, - DataSeriesColorsValues, TooltipType, - niceTimeFormatter, } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { Query, + KBN_FIELD_TYPES, + ES_FIELD_TYPES, esFilters, esQuery, IIndexPattern, } from '../../../../../../src/plugins/data/public'; -// @ts-ignore -import { fieldFormats } from '../../../../../../src/legacy/ui/public/registry/field_formats'; import { DraggedField } from './indexpattern'; import { DragDrop } from '../drag_drop'; import { DatasourceDataPanelProps, DataType } from '../types'; import { BucketedAggregation, FieldStatsResponse } from '../../common'; import { IndexPattern, IndexPatternField } from './types'; -import { LensFieldIcon, getColorForDataType } from './lens_field_icon'; +import { getColorForDataType, LensFieldIcon } from './lens_field_icon'; import { trackUiEvent } from '../lens_ui_telemetry'; export interface FieldItemProps { @@ -238,6 +239,7 @@ export function FieldItem(props: FieldItemProps) { } function FieldItemPopoverContents(props: State & FieldItemProps) { + const fieldFormats = npStart.plugins.data.fieldFormats; const { histogram, topValues, indexPattern, field, dateRange, core, sampledValues } = props; const IS_DARK_THEME = core.uiSettings.get('theme:darkMode'); @@ -289,7 +291,10 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { formatter = { convert: (data: unknown) => JSON.stringify(data) }; } } else { - formatter = fieldFormats.getDefaultInstance(field.type, field.esTypes); + formatter = fieldFormats.getDefaultInstance( + field.type as KBN_FIELD_TYPES, + field.esTypes as ES_FIELD_TYPES[] + ); } const euiButtonColor = @@ -370,7 +375,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { )}{' '} {fieldFormats - .getDefaultInstance('number', ['integer']) + .getDefaultInstance(KBN_FIELD_TYPES.NUMBER, [ES_FIELD_TYPES.INTEGER]) .convert(props.totalDocuments)} {' '} {i18n.translate('xpack.lens.indexPattern.ofDocumentsLabel', { diff --git a/x-pack/legacy/plugins/lens/public/legacy.ts b/x-pack/legacy/plugins/lens/public/legacy.ts new file mode 100644 index 00000000000000..0285e041de78cf --- /dev/null +++ b/x-pack/legacy/plugins/lens/public/legacy.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { npSetup, npStart } from 'ui/new_platform'; +import { start as dataShimStart } from '../../../../../src/legacy/core_plugins/data/public/legacy'; + +export * from './types'; + +import { localApplicationService } from '../../../../../src/legacy/core_plugins/kibana/public/local_application_service'; +import { AppPlugin } from './app_plugin'; + +const app = new AppPlugin(); +app.setup(npSetup.core, {}); +app.start(npStart.core, { + dataShim: dataShimStart, + data: npStart.plugins.data, + __LEGACY: { + localApplicationService, + }, +}); diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx index ba1ac461161b17..9220c3ec75fad6 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx +++ b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx @@ -9,7 +9,7 @@ import { LensMultiTable } from '../types'; import React from 'react'; import { shallow } from 'enzyme'; import { MetricConfig } from './types'; -import { FieldFormat } from 'ui/registry/field_formats'; +import { FieldFormat } from '../../../../../../src/plugins/data/public'; function sampleArgs() { const data: LensMultiTable = { diff --git a/x-pack/legacy/plugins/lens/public/redirect.ts b/x-pack/legacy/plugins/lens/public/redirect.ts new file mode 100644 index 00000000000000..25b0188214c5e4 --- /dev/null +++ b/x-pack/legacy/plugins/lens/public/redirect.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// This file redirects lens urls starting with app/lens#... to their counterpart on app/kibana#lens/... to +// make sure it's compatible with the 7.5 release + +import { npSetup } from 'ui/new_platform'; +import chrome from 'ui/chrome'; + +chrome.setRootController('lens', () => { + // prefix the path in the hash with lens/ + const prefixedHashRoute = window.location.hash.replace(/^#\//, '#/lens/'); + + // redirect to the new lens url `app/kibana#/lens/...` + window.location.href = npSetup.core.http.basePath.prepend('/app/kibana' + prefixedHashRoute); +}); diff --git a/x-pack/legacy/plugins/lens/public/register_embeddable.ts b/x-pack/legacy/plugins/lens/public/register_embeddable.ts deleted file mode 100644 index f86cb91a0029e7..00000000000000 --- a/x-pack/legacy/plugins/lens/public/register_embeddable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { indexPatternDatasourceSetup } from './indexpattern_plugin'; -import { xyVisualizationSetup } from './xy_visualization_plugin'; -import { editorFrameSetup, editorFrameStart } from './editor_frame_plugin'; -import { datatableVisualizationSetup } from './datatable_visualization_plugin'; -import { metricVisualizationSetup } from './metric_visualization_plugin'; - -// bootstrap shimmed plugins to register everything necessary (expression functions and embeddables). -// the new platform will take care of this once in place. -indexPatternDatasourceSetup(); -datatableVisualizationSetup(); -xyVisualizationSetup(); -metricVisualizationSetup(); -editorFrameSetup(); -editorFrameStart(); diff --git a/x-pack/legacy/plugins/lens/public/register_vis_type_alias.ts b/x-pack/legacy/plugins/lens/public/register_vis_type_alias.ts index 562d0f0ef6135f..185df12054a3c6 100644 --- a/x-pack/legacy/plugins/lens/public/register_vis_type_alias.ts +++ b/x-pack/legacy/plugins/lens/public/register_vis_type_alias.ts @@ -6,10 +6,10 @@ import { i18n } from '@kbn/i18n'; import { visualizations } from '../../../../../src/legacy/core_plugins/visualizations/public'; -import { BASE_APP_URL, getEditPath } from '../common'; +import { getBasePath, getEditPath } from '../common'; visualizations.types.registerAlias({ - aliasUrl: BASE_APP_URL, + aliasUrl: getBasePath(), name: 'lens', promotion: { description: i18n.translate('xpack.lens.visTypeAlias.promotion.description', { diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap index d5be772af9441b..03421e66c77f59 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap +++ b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AddLicense component when license is active should display correct verbiage 1`] = `"
Update your license

If you already have a new license, upload it now.

Update license
"`; +exports[`AddLicense component when license is active should display correct verbiage 1`] = `"
Update your license

If you already have a new license, upload it now.

"`; -exports[`AddLicense component when license is expired should display with correct verbiage 1`] = `"
Update your license

If you already have a new license, upload it now.

Update license
"`; +exports[`AddLicense component when license is expired should display with correct verbiage 1`] = `"
Update your license

If you already have a new license, upload it now.

"`; diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap index 8d91ddcc563df0..8670b2c378dca1 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap +++ b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`RequestTrialExtension component should display when license is active and trial has been used 1`] = `"
Extend your trial

If you’d like to continue using machine learning, advanced security, and our other awesome Platinum features, request an extension now.

Extend trial
"`; +exports[`RequestTrialExtension component should display when license is active and trial has been used 1`] = `"
Extend your trial

If you’d like to continue using machine learning, advanced security, and our other awesome Platinum features, request an extension now.

"`; -exports[`RequestTrialExtension component should display when license is not active and trial has been used 1`] = `"
Extend your trial

If you’d like to continue using machine learning, advanced security, and our other awesome Platinum features, request an extension now.

Extend trial
"`; +exports[`RequestTrialExtension component should display when license is not active and trial has been used 1`] = `"
Extend your trial

If you’d like to continue using machine learning, advanced security, and our other awesome Platinum features, request an extension now.

"`; -exports[`RequestTrialExtension component should display when platinum license is not active and trial has been used 1`] = `"
Extend your trial

If you’d like to continue using machine learning, advanced security, and our other awesome Platinum features, request an extension now.

Extend trial
"`; +exports[`RequestTrialExtension component should display when platinum license is not active and trial has been used 1`] = `"
Extend your trial

If you’d like to continue using machine learning, advanced security, and our other awesome Platinum features, request an extension now.

"`; diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap index 3b3cf35dcfd913..cb2a41dadbe9e1 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap +++ b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`RevertToBasic component should display when license is about to expire 1`] = `"
Revert to Basic license

You’ll revert to our free features and lose access to machine learning, advanced security, and other Platinum features.

"`; +exports[`RevertToBasic component should display when license is about to expire 1`] = `"
Revert to Basic license

You’ll revert to our free features and lose access to machine learning, advanced security, and other Platinum features.

"`; -exports[`RevertToBasic component should display when license is expired 1`] = `"
Revert to Basic license

You’ll revert to our free features and lose access to machine learning, advanced security, and other Platinum features.

"`; +exports[`RevertToBasic component should display when license is expired 1`] = `"
Revert to Basic license

You’ll revert to our free features and lose access to machine learning, advanced security, and other Platinum features.

"`; -exports[`RevertToBasic component should display when trial is active 1`] = `"
Revert to Basic license

You’ll revert to our free features and lose access to machine learning, advanced security, and other Platinum features.

"`; +exports[`RevertToBasic component should display when trial is active 1`] = `"
Revert to Basic license

You’ll revert to our free features and lose access to machine learning, advanced security, and other Platinum features.

"`; diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap index 9812152dc6363d..df82a820e95136 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap +++ b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`StartTrial component when trial is allowed display for basic license 1`] = `"
Start a 30-day trial

Experience what machine learning, advanced security, and all our other Platinum features have to offer.

"`; +exports[`StartTrial component when trial is allowed display for basic license 1`] = `"
Start a 30-day trial

Experience what machine learning, advanced security, and all our other Platinum features have to offer.

"`; -exports[`StartTrial component when trial is allowed should display for expired platinum license 1`] = `"
Start a 30-day trial

Experience what machine learning, advanced security, and all our other Platinum features have to offer.

"`; +exports[`StartTrial component when trial is allowed should display for expired platinum license 1`] = `"
Start a 30-day trial

Experience what machine learning, advanced security, and all our other Platinum features have to offer.

"`; -exports[`StartTrial component when trial is allowed should display for gold license 1`] = `"
Start a 30-day trial

Experience what machine learning, advanced security, and all our other Platinum features have to offer.

"`; +exports[`StartTrial component when trial is allowed should display for gold license 1`] = `"
Start a 30-day trial

Experience what machine learning, advanced security, and all our other Platinum features have to offer.

"`; diff --git a/x-pack/legacy/plugins/maps/public/components/help_menu.js b/x-pack/legacy/plugins/maps/public/components/help_menu.js deleted file mode 100644 index 34cb66eb7bd835..00000000000000 --- a/x-pack/legacy/plugins/maps/public/components/help_menu.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Fragment, PureComponent } from 'react'; -import { EuiButton, EuiHorizontalRule, EuiSpacer, EuiLink, EuiText, EuiIcon } from '@elastic/eui'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; - -export class HelpMenu extends PureComponent { - render() { - return ( - - - - - - - - -   - - {i18n.translate('xpack.maps.helpMenu.feedbackLinkText', { - defaultMessage: 'Provide feedback for the Maps application', - })} - - - - ); - } -} diff --git a/x-pack/legacy/plugins/maps/public/help_menu_util.js b/x-pack/legacy/plugins/maps/public/help_menu_util.js index c81261554004a5..72d51cc180eb37 100644 --- a/x-pack/legacy/plugins/maps/public/help_menu_util.js +++ b/x-pack/legacy/plugins/maps/public/help_menu_util.js @@ -3,15 +3,21 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { HelpMenu } from './components/help_menu'; +import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; export function addHelpMenuToAppChrome(chrome) { - chrome.helpExtension.set(domElement => { - render(, domElement); - return () => { - unmountComponentAtNode(domElement); - }; + chrome.helpExtension.set({ + appName: 'Maps', + links: [ + { + linkType: 'documentation', + href: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/maps.html`, + }, + { + linkType: 'github', + title: '[Maps]', + labels: ['Team:Geo'], + }, + ], }); } diff --git a/x-pack/legacy/plugins/maps/public/index.js b/x-pack/legacy/plugins/maps/public/index.js index 49d8646c6a251d..964753f464d95e 100644 --- a/x-pack/legacy/plugins/maps/public/index.js +++ b/x-pack/legacy/plugins/maps/public/index.js @@ -10,7 +10,6 @@ import { wrapInI18nContext } from 'ui/i18n'; import { i18n } from '@kbn/i18n'; // import the uiExports that we want to "use" -import 'uiExports/fieldFormats'; import 'uiExports/inspectorViews'; import 'uiExports/search'; import 'uiExports/embeddableFactories'; diff --git a/x-pack/legacy/plugins/ml/public/app.js b/x-pack/legacy/plugins/ml/public/app.js index b88346035f3067..ead1af5f64e07f 100644 --- a/x-pack/legacy/plugins/ml/public/app.js +++ b/x-pack/legacy/plugins/ml/public/app.js @@ -5,8 +5,6 @@ */ -// import the uiExports that we want to "use" -import 'uiExports/fieldFormats'; import 'uiExports/savedObjectTypes'; import 'ui/autoload/all'; diff --git a/x-pack/legacy/plugins/ml/public/components/navigation_menu/top_nav/__snapshots__/top_nav.test.tsx.snap b/x-pack/legacy/plugins/ml/public/components/navigation_menu/top_nav/__snapshots__/top_nav.test.tsx.snap index b898558b20738e..f9df085d2cbe7a 100644 --- a/x-pack/legacy/plugins/ml/public/components/navigation_menu/top_nav/__snapshots__/top_nav.test.tsx.snap +++ b/x-pack/legacy/plugins/ml/public/components/navigation_menu/top_nav/__snapshots__/top_nav.test.tsx.snap @@ -69,6 +69,7 @@ exports[`Navigation Menu: Minimal initialization. 1`] = ` refreshInterval={0} showUpdateButton={true} start="Thu Aug 29 2019 02:04:19 GMT+0200" + timeFormat="HH:mm" />
diff --git a/x-pack/legacy/plugins/ml/public/formatters/__tests__/abbreviate_whole_number.js b/x-pack/legacy/plugins/ml/public/formatters/__tests__/abbreviate_whole_number.js deleted file mode 100644 index 4e85bae4b18629..00000000000000 --- a/x-pack/legacy/plugins/ml/public/formatters/__tests__/abbreviate_whole_number.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - -import expect from '@kbn/expect'; -import { abbreviateWholeNumber } from '../abbreviate_whole_number'; - -describe('ML - abbreviateWholeNumber formatter', () => { - - it('returns the correct format using default max digits', () => { - expect(abbreviateWholeNumber(1)).to.be(1); - expect(abbreviateWholeNumber(12)).to.be(12); - expect(abbreviateWholeNumber(123)).to.be(123); - expect(abbreviateWholeNumber(1234)).to.be('1k'); - expect(abbreviateWholeNumber(12345)).to.be('12k'); - expect(abbreviateWholeNumber(123456)).to.be('123k'); - expect(abbreviateWholeNumber(1234567)).to.be('1m'); - expect(abbreviateWholeNumber(12345678)).to.be('12m'); - expect(abbreviateWholeNumber(123456789)).to.be('123m'); - expect(abbreviateWholeNumber(1234567890)).to.be('1b'); - expect(abbreviateWholeNumber(5555555555555.55)).to.be('6t'); - }); - - it('returns the correct format using custom max digits', () => { - expect(abbreviateWholeNumber(1, 4)).to.be(1); - expect(abbreviateWholeNumber(12, 4)).to.be(12); - expect(abbreviateWholeNumber(123, 4)).to.be(123); - expect(abbreviateWholeNumber(1234, 4)).to.be(1234); - expect(abbreviateWholeNumber(12345, 4)).to.be('12k'); - expect(abbreviateWholeNumber(123456, 6)).to.be(123456); - expect(abbreviateWholeNumber(1234567, 4)).to.be('1m'); - expect(abbreviateWholeNumber(12345678, 3)).to.be('12m'); - expect(abbreviateWholeNumber(123456789, 9)).to.be(123456789); - expect(abbreviateWholeNumber(1234567890, 3)).to.be('1b'); - expect(abbreviateWholeNumber(5555555555555.55, 5)).to.be('6t'); - }); - -}); diff --git a/x-pack/legacy/plugins/ml/public/formatters/__tests__/format_value.js b/x-pack/legacy/plugins/ml/public/formatters/__tests__/format_value.js deleted file mode 100644 index ffdbdd915d3a88..00000000000000 --- a/x-pack/legacy/plugins/ml/public/formatters/__tests__/format_value.js +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - -import expect from '@kbn/expect'; -import moment from 'moment-timezone'; -import { formatValue } from '../format_value'; - -describe('ML - formatValue formatter', () => { - const timeOfWeekRecord = { - job_id: 'gallery_time_of_week', - result_type: 'record', - probability: 0.012818, - record_score: 53.55134, - bucket_span: 900, - detector_index: 0, - timestamp: 1530155700000, - by_field_name: 'clientip', - by_field_value: '65.55.215.39', - function: 'time_of_week', - function_description: 'time' - }; - - const timeOfDayRecord = { - job_id: 'gallery_time_of_day', - result_type: 'record', - probability: 0.012818, - record_score: 97.94245, - bucket_span: 900, - detector_index: 0, - timestamp: 1517472900000, - by_field_name: 'clientip', - by_field_value: '157.56.93.83', - function: 'time_of_day', - function_description: 'time' - }; - - // Set timezone to US/Eastern for time_of_day and time_of_week tests. - beforeEach(() => { - moment.tz.setDefault('US/Eastern'); - }); - - afterEach(() => { - moment.tz.setDefault('Browser'); - }); - - // For time_of_day and time_of_week test values which are offsets in seconds - // from UTC start of week / day are formatted correctly using the test timezone. - it('correctly formats time_of_week value from numeric input', () => { - expect(formatValue(359739, 'time_of_week', undefined, timeOfWeekRecord)).to.be('Wed 23:55'); - }); - - it('correctly formats time_of_day value from numeric input', () => { - expect(formatValue(73781, 'time_of_day', undefined, timeOfDayRecord)).to.be('15:29'); - }); - - it('correctly formats number values from numeric input', () => { - expect(formatValue(1483228800, 'mean')).to.be(1483228800); - expect(formatValue(1234.5678, 'mean')).to.be(1234.6); - expect(formatValue(0.00012345, 'mean')).to.be(0.000123); - expect(formatValue(0, 'mean')).to.be(0); - expect(formatValue(-0.12345, 'mean')).to.be(-0.123); - expect(formatValue(-1234.5678, 'mean')).to.be(-1234.6); - expect(formatValue(-100000.1, 'mean')).to.be(-100000); - }); - - it('correctly formats time_of_week value from array input', () => { - expect(formatValue([359739], 'time_of_week', undefined, timeOfWeekRecord)).to.be('Wed 23:55'); - }); - - it('correctly formats time_of_day value from array input', () => { - expect(formatValue([73781], 'time_of_day', undefined, timeOfDayRecord)).to.be('15:29'); - }); - - it('correctly formats number values from array input', () => { - expect(formatValue([1483228800], 'mean')).to.be(1483228800); - expect(formatValue([1234.5678], 'mean')).to.be(1234.6); - expect(formatValue([0.00012345], 'mean')).to.be(0.000123); - expect(formatValue([0], 'mean')).to.be(0); - expect(formatValue([-0.12345], 'mean')).to.be(-0.123); - expect(formatValue([-1234.5678], 'mean')).to.be(-1234.6); - expect(formatValue([-100000.1], 'mean')).to.be(-100000); - }); - - it('correctly formats multi-valued array', () => { - expect(formatValue([30.3, 26.2], 'lat_long')).to.be('[30.3,26.2]'); - }); - -}); diff --git a/x-pack/legacy/plugins/ml/public/formatters/__tests__/metric_change_description.js b/x-pack/legacy/plugins/ml/public/formatters/__tests__/metric_change_description.js deleted file mode 100644 index 45864ce58bf83b..00000000000000 --- a/x-pack/legacy/plugins/ml/public/formatters/__tests__/metric_change_description.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - -import expect from '@kbn/expect'; -import { getMetricChangeDescription } from '../metric_change_description'; - - -describe('ML - metricChangeDescription formatter', () => { - - it('returns correct icon and message if actual > typical', () => { - expect(getMetricChangeDescription(1.01, 1)).to.eql({ iconType: 'sortUp', message: 'Unusually high' }); - expect(getMetricChangeDescription(1.123, 1)).to.eql({ iconType: 'sortUp', message: '1.1x higher' }); - expect(getMetricChangeDescription(2, 1)).to.eql({ iconType: 'sortUp', message: '2x higher' }); - expect(getMetricChangeDescription(9.5, 1)).to.eql({ iconType: 'sortUp', message: '10x higher' }); - expect(getMetricChangeDescription(1000, 1)).to.eql({ iconType: 'sortUp', message: 'More than 100x higher' }); - expect(getMetricChangeDescription(1, 0)).to.eql({ iconType: 'sortUp', message: 'Unexpected non-zero value' }); - }); - - it('returns correct icon and message if actual < typical', () => { - expect(getMetricChangeDescription(1, 1.01)).to.eql({ iconType: 'sortDown', message: 'Unusually low' }); - expect(getMetricChangeDescription(1, 1.123)).to.eql({ iconType: 'sortDown', message: '1.1x lower' }); - expect(getMetricChangeDescription(1, 2)).to.eql({ iconType: 'sortDown', message: '2x lower' }); - expect(getMetricChangeDescription(1, 9.5)).to.eql({ iconType: 'sortDown', message: '10x lower' }); - expect(getMetricChangeDescription(1, 1000)).to.eql({ iconType: 'sortDown', message: 'More than 100x lower' }); - expect(getMetricChangeDescription(0, 1)).to.eql({ iconType: 'sortDown', message: 'Unexpected zero value' }); - }); - -}); diff --git a/x-pack/legacy/plugins/ml/public/formatters/abbreviate_whole_number.test.ts b/x-pack/legacy/plugins/ml/public/formatters/abbreviate_whole_number.test.ts new file mode 100644 index 00000000000000..feabaa4064978d --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/formatters/abbreviate_whole_number.test.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { abbreviateWholeNumber } from './abbreviate_whole_number'; + +describe('ML - abbreviateWholeNumber formatter', () => { + test('returns the correct format using default max digits', () => { + expect(abbreviateWholeNumber(1)).toBe(1); + expect(abbreviateWholeNumber(12)).toBe(12); + expect(abbreviateWholeNumber(123)).toBe(123); + expect(abbreviateWholeNumber(1234)).toBe('1k'); + expect(abbreviateWholeNumber(12345)).toBe('12k'); + expect(abbreviateWholeNumber(123456)).toBe('123k'); + expect(abbreviateWholeNumber(1234567)).toBe('1m'); + expect(abbreviateWholeNumber(12345678)).toBe('12m'); + expect(abbreviateWholeNumber(123456789)).toBe('123m'); + expect(abbreviateWholeNumber(1234567890)).toBe('1b'); + expect(abbreviateWholeNumber(5555555555555.55)).toBe('6t'); + }); + + test('returns the correct format using custom max digits', () => { + expect(abbreviateWholeNumber(1, 4)).toBe(1); + expect(abbreviateWholeNumber(12, 4)).toBe(12); + expect(abbreviateWholeNumber(123, 4)).toBe(123); + expect(abbreviateWholeNumber(1234, 4)).toBe(1234); + expect(abbreviateWholeNumber(12345, 4)).toBe('12k'); + expect(abbreviateWholeNumber(123456, 6)).toBe(123456); + expect(abbreviateWholeNumber(1234567, 4)).toBe('1m'); + expect(abbreviateWholeNumber(12345678, 3)).toBe('12m'); + expect(abbreviateWholeNumber(123456789, 9)).toBe(123456789); + expect(abbreviateWholeNumber(1234567890, 3)).toBe('1b'); + expect(abbreviateWholeNumber(5555555555555.55, 5)).toBe('6t'); + }); +}); diff --git a/x-pack/legacy/plugins/ml/public/formatters/abbreviate_whole_number.js b/x-pack/legacy/plugins/ml/public/formatters/abbreviate_whole_number.ts similarity index 89% rename from x-pack/legacy/plugins/ml/public/formatters/abbreviate_whole_number.js rename to x-pack/legacy/plugins/ml/public/formatters/abbreviate_whole_number.ts index 445681be4ff819..6d630c740a359c 100644 --- a/x-pack/legacy/plugins/ml/public/formatters/abbreviate_whole_number.js +++ b/x-pack/legacy/plugins/ml/public/formatters/abbreviate_whole_number.ts @@ -4,15 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ - /* * Formatter to abbreviate large whole numbers with metric prefixes. * Uses numeral.js to format numbers longer than the specified number of * digits with metric abbreviations e.g. 12345 as 12k, or 98000000 as 98m. -*/ + */ import numeral from '@elastic/numeral'; -export function abbreviateWholeNumber(value, maxDigits = 3) { +export function abbreviateWholeNumber(value: number, maxDigits = 3) { if (Math.abs(value) < Math.pow(10, maxDigits)) { return value; } else { diff --git a/x-pack/legacy/plugins/ml/public/formatters/format_value.test.ts b/x-pack/legacy/plugins/ml/public/formatters/format_value.test.ts new file mode 100644 index 00000000000000..5f146aef97fccf --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/formatters/format_value.test.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import moment from 'moment-timezone'; +import { AnomalyRecordDoc } from '../../common/types/anomalies'; +import { formatValue } from './format_value'; + +describe('ML - formatValue formatter', () => { + const timeOfWeekRecord: AnomalyRecordDoc = { + job_id: 'gallery_time_of_week', + result_type: 'record', + probability: 0.012818, + record_score: 53.55134, + initial_record_score: 53, + bucket_span: 900, + detector_index: 0, + is_interim: false, + timestamp: 1530155700000, + by_field_name: 'clientip', + by_field_value: '65.55.215.39', + function: 'time_of_week', + function_description: 'time', + }; + + const timeOfDayRecord: AnomalyRecordDoc = { + job_id: 'gallery_time_of_day', + result_type: 'record', + probability: 0.012818, + record_score: 97.94245, + initial_record_score: 97, + bucket_span: 900, + detector_index: 0, + is_interim: false, + timestamp: 1517472900000, + by_field_name: 'clientip', + by_field_value: '157.56.93.83', + function: 'time_of_day', + function_description: 'time', + }; + + // Set timezone to US/Eastern for time_of_day and time_of_week tests. + beforeEach(() => { + moment.tz.setDefault('US/Eastern'); + }); + + afterEach(() => { + moment.tz.setDefault('Browser'); + }); + + // For time_of_day and time_of_week test values which are offsets in seconds + // from UTC start of week / day are formatted correctly using the test timezone. + test('correctly formats time_of_week value from numeric input', () => { + expect(formatValue(359739, 'time_of_week', undefined, timeOfWeekRecord)).toBe('Wed 23:55'); + }); + + test('correctly formats time_of_day value from numeric input', () => { + expect(formatValue(73781, 'time_of_day', undefined, timeOfDayRecord)).toBe('15:29'); + }); + + test('correctly formats number values from numeric input', () => { + expect(formatValue(1483228800, 'mean')).toBe(1483228800); + expect(formatValue(1234.5678, 'mean')).toBe(1234.6); + expect(formatValue(0.00012345, 'mean')).toBe(0.000123); + expect(formatValue(0, 'mean')).toBe(0); + expect(formatValue(-0.12345, 'mean')).toBe(-0.123); + expect(formatValue(-1234.5678, 'mean')).toBe(-1234.6); + expect(formatValue(-100000.1, 'mean')).toBe(-100000); + }); + + test('correctly formats time_of_week value from array input', () => { + expect(formatValue([359739], 'time_of_week', undefined, timeOfWeekRecord)).toBe('Wed 23:55'); + }); + + test('correctly formats time_of_day value from array input', () => { + expect(formatValue([73781], 'time_of_day', undefined, timeOfDayRecord)).toBe('15:29'); + }); + + test('correctly formats number values from array input', () => { + expect(formatValue([1483228800], 'mean')).toBe(1483228800); + expect(formatValue([1234.5678], 'mean')).toBe(1234.6); + expect(formatValue([0.00012345], 'mean')).toBe(0.000123); + expect(formatValue([0], 'mean')).toBe(0); + expect(formatValue([-0.12345], 'mean')).toBe(-0.123); + expect(formatValue([-1234.5678], 'mean')).toBe(-1234.6); + expect(formatValue([-100000.1], 'mean')).toBe(-100000); + }); + + test('correctly formats multi-valued array', () => { + expect(formatValue([30.3, 26.2], 'lat_long')).toBe('[30.3,26.2]'); + }); +}); diff --git a/x-pack/legacy/plugins/ml/public/formatters/format_value.js b/x-pack/legacy/plugins/ml/public/formatters/format_value.ts similarity index 75% rename from x-pack/legacy/plugins/ml/public/formatters/format_value.js rename to x-pack/legacy/plugins/ml/public/formatters/format_value.ts index 48988b5561e705..9360957c4a9119 100644 --- a/x-pack/legacy/plugins/ml/public/formatters/format_value.js +++ b/x-pack/legacy/plugins/ml/public/formatters/format_value.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ - - /* * Formatter for 'typical' and 'actual' values from machine learning results. * For detectors which use the time_of_week or time_of_day @@ -14,12 +12,8 @@ */ import moment from 'moment'; - -import { uiModules } from 'ui/modules'; -const module = uiModules.get('apps/ml'); - - -const SIGFIGS_IF_ROUNDING = 3; // Number of sigfigs to use for values < 10 +import { AnomalyRecordDoc } from '../../common/types/anomalies'; +const SIGFIGS_IF_ROUNDING = 3; // Number of sigfigs to use for values < 10 // Formats the value of an actual or typical field from a machine learning anomaly record. // mlFunction is the 'function' field from the ML record containing what the user entered e.g. 'high_count', @@ -29,7 +23,12 @@ const SIGFIGS_IF_ROUNDING = 3; // Number of sigfigs to use for values < 10 // For time_of_day or time_of_week functions the anomaly record // containing the timestamp of the anomaly should be supplied in // order to correctly format the day or week offset to the time of the anomaly. -export function formatValue(value, mlFunction, fieldFormat, record) { +export function formatValue( + value: number[] | number, + mlFunction: string, + fieldFormat?: any, + record?: AnomalyRecordDoc +) { // actual and typical values in anomaly record results will be arrays. // Unless the array is multi-valued (as it will be for multi-variate analyses such as lat_long), // simply return the formatted single value. @@ -54,7 +53,12 @@ export function formatValue(value, mlFunction, fieldFormat, record) { // For time_of_day or time_of_week functions the anomaly record // containing the timestamp of the anomaly should be supplied in // order to correctly format the day or week offset to the time of the anomaly. -function formatSingleValue(value, mlFunction, fieldFormat, record) { +function formatSingleValue( + value: number, + mlFunction: string, + fieldFormat?: any, + record?: AnomalyRecordDoc +) { if (value === undefined || value === null) { return ''; } @@ -65,14 +69,24 @@ function formatSingleValue(value, mlFunction, fieldFormat, record) { // that the anomaly occurred using record timestamp if supplied, add on the offset, and finally // revert back to configured timezone for formatting. if (mlFunction === 'time_of_week') { - const d = ((record !== undefined && record.timestamp !== undefined) ? new Date(record.timestamp) : new Date()); - const i = parseInt(value); - const utcMoment = moment.utc(d).startOf('week').add(i, 's'); + const d = + record !== undefined && record.timestamp !== undefined + ? new Date(record.timestamp) + : new Date(); + const utcMoment = moment + .utc(d) + .startOf('week') + .add(value, 's'); return moment(utcMoment.valueOf()).format('ddd HH:mm'); } else if (mlFunction === 'time_of_day') { - const d = ((record !== undefined && record.timestamp !== undefined) ? new Date(record.timestamp) : new Date()); - const i = parseInt(value); - const utcMoment = moment.utc(d).startOf('day').add(i, 's'); + const d = + record !== undefined && record.timestamp !== undefined + ? new Date(record.timestamp) + : new Date(); + const utcMoment = moment + .utc(d) + .startOf('day') + .add(value, 's'); return moment(utcMoment.valueOf()).format('HH:mm'); } else { if (fieldFormat !== undefined) { @@ -81,32 +95,32 @@ function formatSingleValue(value, mlFunction, fieldFormat, record) { // If no Kibana FieldFormat object provided, // format the value depending on its magnitude. const absValue = Math.abs(value); - if (absValue >= 10000 || absValue === Math.floor(absValue)) { + if (absValue >= 10000 || absValue === Math.floor(absValue)) { // Output 0 decimal places if whole numbers or >= 10000 if (fieldFormat !== undefined) { return fieldFormat.convert(value, 'text'); } else { return Number(value.toFixed(0)); } - } else if (absValue >= 10) { // Output to 1 decimal place between 10 and 10000 return Number(value.toFixed(1)); - } - else { + } else { // For values < 10, output to 3 significant figures let multiple; if (value > 0) { - multiple = Math.pow(10, SIGFIGS_IF_ROUNDING - Math.floor(Math.log(value) / Math.LN10) - 1); + multiple = Math.pow( + 10, + SIGFIGS_IF_ROUNDING - Math.floor(Math.log(value) / Math.LN10) - 1 + ); } else { - multiple = Math.pow(10, SIGFIGS_IF_ROUNDING - Math.floor(Math.log(-1 * value) / Math.LN10) - 1); + multiple = Math.pow( + 10, + SIGFIGS_IF_ROUNDING - Math.floor(Math.log(-1 * value) / Math.LN10) - 1 + ); } - return (Math.round(value * multiple)) / multiple; + return Math.round(value * multiple) / multiple; } } } } - -// TODO - remove the filter once all uses of the formatValue Angular filter have been removed. -module.filter('formatValue', () => formatValue); - diff --git a/x-pack/legacy/plugins/ml/public/formatters/metric_change_description.js b/x-pack/legacy/plugins/ml/public/formatters/metric_change_description.js deleted file mode 100644 index 06074e9a842c52..00000000000000 --- a/x-pack/legacy/plugins/ml/public/formatters/metric_change_description.js +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - -/* - * Produces a concise textual description of how the - * actual value compares to the typical value for an anomaly. - */ - -import { i18n } from '@kbn/i18n'; -import { uiModules } from 'ui/modules'; -const module = uiModules.get('apps/ml'); - -// Returns an Object containing a text message and EuiIcon type to -// describe how the actual value compares to the typical. -export function getMetricChangeDescription(actualProp, typicalProp) { - if (actualProp === undefined || typicalProp === undefined) { - return { iconType: 'empty', message: '' }; - } - - let iconType; - let message; - - // For metric functions, actual and typical will be single value arrays. - let actual = actualProp; - let typical = typicalProp; - if (Array.isArray(actualProp)) { - if (actualProp.length === 1) { - actual = actualProp[0]; - } else { - // TODO - do we want to enhance the description depending on detector? - // e.g. 'Unusual location' if using a lat_long detector. - return { - iconType: 'alert', - message: i18n.translate('xpack.ml.formatters.metricChangeDescription.unusualValuesDescription', { - defaultMessage: 'Unusual values', - }), - }; - } - } - - if (Array.isArray(typicalProp)) { - if (typicalProp.length === 1) { - typical = typicalProp[0]; - } - } - - if (actual === typical) { - // Very unlikely, but just in case. - message = i18n.translate('xpack.ml.formatters.metricChangeDescription.actualSameAsTypicalDescription', { - defaultMessage: 'actual same as typical', - }); - } else { - // For actual / typical gives output of the form: - // 4 / 2 2x higher - // 2 / 10 5x lower - // 1000 / 1 More than 100x higher - // 999 / 1000 Unusually low - // 100 / -100 Unusually high - // 0 / 100 Unexpected zero value - // 1 / 0 Unexpected non-zero value - const isHigher = actual > typical; - iconType = isHigher ? 'sortUp' : 'sortDown'; - if (typical !== 0 && actual !== 0) { - const factor = isHigher ? actual / typical : typical / actual; - if (factor > 1.5) { - if (factor <= 100) { - message = isHigher ? i18n.translate('xpack.ml.formatters.metricChangeDescription.moreThanOneAndHalfxHigherDescription', { - defaultMessage: '{factor}x higher', - values: { factor: Math.round(factor) }, - }) : i18n.translate('xpack.ml.formatters.metricChangeDescription.moreThanOneAndHalfxLowerDescription', { - defaultMessage: '{factor}x lower', - values: { factor: Math.round(factor) }, - }); - } else { - message = isHigher ? i18n.translate('xpack.ml.formatters.metricChangeDescription.moreThan100xHigherDescription', { - defaultMessage: 'More than 100x higher', - }) : i18n.translate('xpack.ml.formatters.metricChangeDescription.moreThan100xLowerDescription', { - defaultMessage: 'More than 100x lower', - }); - } - } else if (factor >= 1.05) { - message = isHigher ? i18n.translate('xpack.ml.formatters.metricChangeDescription.moreThanOneAndFiveHundredthsxHigherDescription', { - defaultMessage: '{factor}x higher', - values: { factor: factor.toPrecision(2) }, - }) : i18n.translate('xpack.ml.formatters.metricChangeDescription.moreThanOneAndFiveHundredthsxLowerDescription', { - defaultMessage: '{factor}x lower', - values: { factor: factor.toPrecision(2) }, - }); - } else { - message = isHigher ? i18n.translate('xpack.ml.formatters.metricChangeDescription.unusuallyHighDescription', { - defaultMessage: 'Unusually high', - }) : i18n.translate('xpack.ml.formatters.metricChangeDescription.unusuallyLowDescription', { - defaultMessage: 'Unusually low', - }); - } - - } else { - if (actual === 0) { - message = i18n.translate('xpack.ml.formatters.metricChangeDescription.unexpectedZeroValueDescription', { - defaultMessage: 'Unexpected zero value', - }); - } else { - message = i18n.translate('xpack.ml.formatters.metricChangeDescription.unexpectedNonZeroValueDescription', { - defaultMessage: 'Unexpected non-zero value', - }); - } - } - } - - return { iconType, message }; -} - -// TODO - remove the filter once all uses of the metricChangeDescription Angular filter have been removed. -module.filter('metricChangeDescription', function () { - return function (actual, typical) { - - const { - iconType, - message - } = getMetricChangeDescription(actual, typical); - - switch (iconType) { - case 'sortUp': - return ` ${message}`; - case 'sortDown': - return ` ${message}`; - case 'alert': - return ` ${message}`; - } - - return message; - }; -}); - diff --git a/x-pack/legacy/plugins/ml/public/formatters/metric_change_description.test.ts b/x-pack/legacy/plugins/ml/public/formatters/metric_change_description.test.ts new file mode 100644 index 00000000000000..93533fe155e80f --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/formatters/metric_change_description.test.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getMetricChangeDescription } from './metric_change_description'; + +describe('ML - metricChangeDescription formatter', () => { + test('returns correct icon and message if actual > typical', () => { + expect(getMetricChangeDescription(1.01, 1)).toEqual({ + iconType: 'sortUp', + message: 'Unusually high', + }); + expect(getMetricChangeDescription(1.123, 1)).toEqual({ + iconType: 'sortUp', + message: '1.1x higher', + }); + expect(getMetricChangeDescription(2, 1)).toEqual({ iconType: 'sortUp', message: '2x higher' }); + expect(getMetricChangeDescription(9.5, 1)).toEqual({ + iconType: 'sortUp', + message: '10x higher', + }); + expect(getMetricChangeDescription(1000, 1)).toEqual({ + iconType: 'sortUp', + message: 'More than 100x higher', + }); + expect(getMetricChangeDescription(1, 0)).toEqual({ + iconType: 'sortUp', + message: 'Unexpected non-zero value', + }); + }); + + test('returns correct icon and message if actual < typical', () => { + expect(getMetricChangeDescription(1, 1.01)).toEqual({ + iconType: 'sortDown', + message: 'Unusually low', + }); + expect(getMetricChangeDescription(1, 1.123)).toEqual({ + iconType: 'sortDown', + message: '1.1x lower', + }); + expect(getMetricChangeDescription(1, 2)).toEqual({ iconType: 'sortDown', message: '2x lower' }); + expect(getMetricChangeDescription(1, 9.5)).toEqual({ + iconType: 'sortDown', + message: '10x lower', + }); + expect(getMetricChangeDescription(1, 1000)).toEqual({ + iconType: 'sortDown', + message: 'More than 100x lower', + }); + expect(getMetricChangeDescription(0, 1)).toEqual({ + iconType: 'sortDown', + message: 'Unexpected zero value', + }); + }); +}); diff --git a/x-pack/legacy/plugins/ml/public/formatters/metric_change_description.ts b/x-pack/legacy/plugins/ml/public/formatters/metric_change_description.ts new file mode 100644 index 00000000000000..68f437b5a1436a --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/formatters/metric_change_description.ts @@ -0,0 +1,157 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* + * Produces a concise textual description of how the + * actual value compares to the typical value for an anomaly. + */ + +import { i18n } from '@kbn/i18n'; + +// Returns an Object containing a text message and EuiIcon type to +// describe how the actual value compares to the typical. +export function getMetricChangeDescription( + actualProp: number[] | number, + typicalProp: number[] | number +) { + if (actualProp === undefined || typicalProp === undefined) { + return { iconType: 'empty', message: '' }; + } + + let iconType: string = 'alert'; + let message: string; + + // For metric functions, actual and typical will be single value arrays. + let actual: number = 0; + let typical: number = 0; + if (Array.isArray(actualProp)) { + if (actualProp.length === 1) { + actual = actualProp[0]; + } else { + // lat_long anomalies currently the only multi-value case. + // TODO - do we want to enhance the description depending on detector? + // e.g. 'Unusual location' if using a lat_long detector. + return { + iconType: 'alert', + message: i18n.translate( + 'xpack.ml.formatters.metricChangeDescription.unusualValuesDescription', + { + defaultMessage: 'Unusual values', + } + ), + }; + } + } else { + actual = actualProp; + } + + if (Array.isArray(typicalProp)) { + if (typicalProp.length === 1) { + typical = typicalProp[0]; + } + } else { + typical = typicalProp; + } + + if (actual === typical) { + // Very unlikely, but just in case. + message = i18n.translate( + 'xpack.ml.formatters.metricChangeDescription.actualSameAsTypicalDescription', + { + defaultMessage: 'actual same as typical', + } + ); + } else { + // For actual / typical gives output of the form: + // 4 / 2 2x higher + // 2 / 10 5x lower + // 1000 / 1 More than 100x higher + // 999 / 1000 Unusually low + // 100 / -100 Unusually high + // 0 / 100 Unexpected zero value + // 1 / 0 Unexpected non-zero value + const isHigher = actual > typical; + iconType = isHigher ? 'sortUp' : 'sortDown'; + if (typical !== 0 && actual !== 0) { + const factor: number = isHigher ? actual / typical : typical / actual; + if (factor > 1.5) { + if (factor <= 100) { + message = isHigher + ? i18n.translate( + 'xpack.ml.formatters.metricChangeDescription.moreThanOneAndHalfxHigherDescription', + { + defaultMessage: '{factor}x higher', + values: { factor: Math.round(factor) }, + } + ) + : i18n.translate( + 'xpack.ml.formatters.metricChangeDescription.moreThanOneAndHalfxLowerDescription', + { + defaultMessage: '{factor}x lower', + values: { factor: Math.round(factor) }, + } + ); + } else { + message = isHigher + ? i18n.translate( + 'xpack.ml.formatters.metricChangeDescription.moreThan100xHigherDescription', + { + defaultMessage: 'More than 100x higher', + } + ) + : i18n.translate( + 'xpack.ml.formatters.metricChangeDescription.moreThan100xLowerDescription', + { + defaultMessage: 'More than 100x lower', + } + ); + } + } else if (factor >= 1.05) { + message = isHigher + ? i18n.translate( + 'xpack.ml.formatters.metricChangeDescription.moreThanOneAndFiveHundredthsxHigherDescription', + { + defaultMessage: '{factor}x higher', + values: { factor: factor.toPrecision(2) }, + } + ) + : i18n.translate( + 'xpack.ml.formatters.metricChangeDescription.moreThanOneAndFiveHundredthsxLowerDescription', + { + defaultMessage: '{factor}x lower', + values: { factor: factor.toPrecision(2) }, + } + ); + } else { + message = isHigher + ? i18n.translate('xpack.ml.formatters.metricChangeDescription.unusuallyHighDescription', { + defaultMessage: 'Unusually high', + }) + : i18n.translate('xpack.ml.formatters.metricChangeDescription.unusuallyLowDescription', { + defaultMessage: 'Unusually low', + }); + } + } else { + if (actual === 0) { + message = i18n.translate( + 'xpack.ml.formatters.metricChangeDescription.unexpectedZeroValueDescription', + { + defaultMessage: 'Unexpected zero value', + } + ); + } else { + message = i18n.translate( + 'xpack.ml.formatters.metricChangeDescription.unexpectedNonZeroValueDescription', + { + defaultMessage: 'Unexpected non-zero value', + } + ); + } + } + } + + return { iconType, message }; +} diff --git a/x-pack/legacy/plugins/ml/public/util/chart_utils.test.js b/x-pack/legacy/plugins/ml/public/util/chart_utils.test.js index 6d13f1bc668089..a229113826a2e9 100644 --- a/x-pack/legacy/plugins/ml/public/util/chart_utils.test.js +++ b/x-pack/legacy/plugins/ml/public/util/chart_utils.test.js @@ -6,12 +6,6 @@ import seriesConfig from '../explorer/explorer_charts/__mocks__/mock_series_config_filebeat'; -jest.mock('ui/registry/field_formats', () => ({ - fieldFormats: { - getDefaultInstance: jest.fn(), - }, -})); - jest.mock('ui/timefilter', () => { const dateMath = require('@elastic/datemath'); let _time = undefined; diff --git a/x-pack/legacy/plugins/ml/public/util/time_buckets.js b/x-pack/legacy/plugins/ml/public/util/time_buckets.js index 6933ee6935e807..98cb677a4851af 100644 --- a/x-pack/legacy/plugins/ml/public/util/time_buckets.js +++ b/x-pack/legacy/plugins/ml/public/util/time_buckets.js @@ -9,10 +9,11 @@ import _ from 'lodash'; import moment from 'moment'; import dateMath from '@elastic/datemath'; import chrome from 'ui/chrome'; -import { fieldFormats } from 'ui/registry/field_formats'; +import { npStart } from 'ui/new_platform'; import { timeBucketsCalcAutoIntervalProvider } from './calc_auto_interval'; import { parseInterval } from '../../common/util/parse_interval'; +import { FIELD_FORMAT_IDS } from '../../../../../../src/plugins/data/public'; const unitsDesc = dateMath.unitsDesc; const largeMax = unitsDesc.indexOf('w'); // Multiple units of week or longer converted to days for ES intervals. @@ -316,7 +317,8 @@ TimeBuckets.prototype.getScaledDateFormat = function () { }; TimeBuckets.prototype.getScaledDateFormatter = function () { - const DateFieldFormat = fieldFormats.getType('date'); + const fieldFormats = npStart.plugins.data.fieldFormats; + const DateFieldFormat = fieldFormats.getType(FIELD_FORMAT_IDS.DATE); return new DateFieldFormat({ pattern: this.getScaledDateFormat() }, getConfig); diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap index cf8cf37d2e4ee6..bf309c65556a81 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap +++ b/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap @@ -249,7 +249,7 @@ Array [ > +
+

+ Unable to fetch report info +

+
+
+
+
+ Could not fetch the job info +
+
+
+
+
} - tabIndex={1} - /> -
- + +
+ - - -
- } - onActivation={[Function]} - onDeactivation={[Function]} - persistentFocus={false} - /> - -
- - - - + + + + + +
-
- -

- Unable to fetch report info -

-
-
- - + Unable to fetch report info + + +
+
+ +
-
- -
- Could not fetch the job info -
-
-
+ +
+ Could not fetch the job info +
+
- -
+
+
-
+
- -
- - - - + } + tabIndex={0} + /> + +
+ + + ,
- - + + + } /> - - - } - /> - - + + - - -
- -
+
-
+
+ + } - tabIndex={1} - /> -
- + +
+ - - - } - onActivation={[Function]} - onDeactivation={[Function]} - persistentFocus={false} - /> - -
- - - - + + + + + +
-
- -

- Job Info -

-
-
- - + Job Info + + +
+
+ +
-
- -
- -
+ +
+
- -
+
+
-
+
- -
- - - - + } + tabIndex={0} + /> + +
+ + + ,
+ + } + > +
+
+ + + + API keys not enabled in Elasticsearch + + +
+ +
+ + + , + } + } + > + Contact your system administrator and refer to the + + + + docs + + + + to enable API keys. + +
+
+
+
+ +`; + +exports[`ApiKeysGridPage renders permission denied if user does not have required permissions 1`] = ` + + +
+ + +
+ + +

+ } + iconType="securityApp" + title={ +

+ +

+ } + > +
+ + + + + + + + + +
+ + + + +

+ + You need permission to manage API keys + +

+
+ +
+ + +
+

+ + Contact your system administrator. + +

+
+
+ + +
+ +
+ + +
+ + +`; diff --git a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/api_keys_grid_page.test.tsx b/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/api_keys_grid_page.test.tsx new file mode 100644 index 00000000000000..19ac3881f78d93 --- /dev/null +++ b/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/api_keys_grid_page.test.tsx @@ -0,0 +1,180 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +let mockSimulate403 = false; +let mockSimulate500 = false; +let mockAreApiKeysEnabled = true; +let mockIsAdmin = true; + +const mock403 = () => ({ body: { statusCode: 403 } }); +const mock500 = () => ({ body: { error: 'Internal Server Error', message: '', statusCode: 500 } }); + +jest.mock('../../../../lib/api_keys_api', () => { + return { + ApiKeysApi: { + async checkPrivileges() { + if (mockSimulate403) { + throw mock403(); + } + + return { + isAdmin: mockIsAdmin, + areApiKeysEnabled: mockAreApiKeysEnabled, + }; + }, + async getApiKeys() { + if (mockSimulate500) { + throw mock500(); + } + + return { + apiKeys: [ + { + creation: 1571322182082, + expiration: 1571408582082, + id: '0QQZ2m0BO2XZwgJFuWTT', + invalidated: false, + name: 'my-api-key', + realm: 'reserved', + username: 'elastic', + }, + ], + }; + }, + }, + }; +}); + +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { ApiKeysGridPage } from './api_keys_grid_page'; +import React from 'react'; +import { ReactWrapper } from 'enzyme'; +import { EuiCallOut } from '@elastic/eui'; + +import { NotEnabled } from './not_enabled'; +import { PermissionDenied } from './permission_denied'; + +const waitForRender = async ( + wrapper: ReactWrapper, + condition: (wrapper: ReactWrapper) => boolean +) => { + return new Promise((resolve, reject) => { + const interval = setInterval(async () => { + await Promise.resolve(); + wrapper.update(); + if (condition(wrapper)) { + resolve(); + } + }, 10); + + setTimeout(() => { + clearInterval(interval); + reject(new Error('waitForRender timeout after 2000ms')); + }, 2000); + }); +}; + +describe('ApiKeysGridPage', () => { + beforeEach(() => { + mockSimulate403 = false; + mockSimulate500 = false; + mockAreApiKeysEnabled = true; + mockIsAdmin = true; + }); + + it('renders a loading state when fetching API keys', async () => { + const wrapper = mountWithIntl(); + + expect(wrapper.find('[data-test-subj="apiKeysSectionLoading"]')).toHaveLength(1); + }); + + it('renders a callout when API keys are not enabled', async () => { + mockAreApiKeysEnabled = false; + const wrapper = mountWithIntl(); + + await waitForRender(wrapper, updatedWrapper => { + return updatedWrapper.find(NotEnabled).length > 0; + }); + + expect(wrapper.find(NotEnabled)).toMatchSnapshot(); + }); + + it('renders permission denied if user does not have required permissions', async () => { + mockSimulate403 = true; + const wrapper = mountWithIntl(); + + await waitForRender(wrapper, updatedWrapper => { + return updatedWrapper.find(PermissionDenied).length > 0; + }); + + expect(wrapper.find(PermissionDenied)).toMatchSnapshot(); + }); + + it('renders error callout if error fetching API keys', async () => { + mockSimulate500 = true; + const wrapper = mountWithIntl(); + + await waitForRender(wrapper, updatedWrapper => { + return updatedWrapper.find(EuiCallOut).length > 0; + }); + + expect(wrapper.find('EuiCallOut[data-test-subj="apiKeysError"]')).toHaveLength(1); + }); + + describe('Admin view', () => { + const wrapper = mountWithIntl(); + + it('renders a callout indicating the user is an administrator', async () => { + const calloutEl = 'EuiCallOut[data-test-subj="apiKeyAdminDescriptionCallOut"]'; + + await waitForRender(wrapper, updatedWrapper => { + return updatedWrapper.find(calloutEl).length > 0; + }); + + expect(wrapper.find(calloutEl).text()).toEqual('You are an API Key administrator.'); + }); + + it('renders the correct description text', async () => { + const descriptionEl = 'EuiText[data-test-subj="apiKeysDescriptionText"]'; + + await waitForRender(wrapper, updatedWrapper => { + return updatedWrapper.find(descriptionEl).length > 0; + }); + + expect(wrapper.find(descriptionEl).text()).toEqual( + 'View and invalidate API keys. An API key sends requests on behalf of a user.' + ); + }); + }); + + describe('Non-admin view', () => { + mockIsAdmin = false; + const wrapper = mountWithIntl(); + + it('does NOT render a callout indicating the user is an administrator', async () => { + const descriptionEl = 'EuiText[data-test-subj="apiKeysDescriptionText"]'; + const calloutEl = 'EuiCallOut[data-test-subj="apiKeyAdminDescriptionCallOut"]'; + + await waitForRender(wrapper, updatedWrapper => { + return updatedWrapper.find(descriptionEl).length > 0; + }); + + expect(wrapper.find(calloutEl).length).toEqual(0); + }); + + it('renders the correct description text', async () => { + const descriptionEl = 'EuiText[data-test-subj="apiKeysDescriptionText"]'; + + await waitForRender(wrapper, updatedWrapper => { + return updatedWrapper.find(descriptionEl).length > 0; + }); + + expect(wrapper.find(descriptionEl).text()).toEqual( + 'View and invalidate your API keys. An API key sends requests on your behalf.' + ); + }); + }); +}); diff --git a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/api_keys_grid_page.tsx b/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/api_keys_grid_page.tsx index 6bebf17c943a49..37838cfdb950d9 100644 --- a/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/api_keys_grid_page.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/api_keys_grid/components/api_keys_grid_page.tsx @@ -86,7 +86,7 @@ export class ApiKeysGridPage extends Component { if (isLoadingApp) { return ( - + { } color="danger" iconType="alert" + data-test-subj="apiKeysError" > {statusCode}: {errorTitle} - {message} @@ -136,7 +137,7 @@ export class ApiKeysGridPage extends Component { } const description = ( - +

{isAdmin ? ( { color="success" iconType="user" size="s" + data-test-subj="apiKeyAdminDescriptionCallOut" /> diff --git a/x-pack/legacy/plugins/security/server/lib/__tests__/__fixtures__/server.ts b/x-pack/legacy/plugins/security/server/lib/__tests__/__fixtures__/server.ts index 82f91483dc60de..55b6f735cfced8 100644 --- a/x-pack/legacy/plugins/security/server/lib/__tests__/__fixtures__/server.ts +++ b/x-pack/legacy/plugins/security/server/lib/__tests__/__fixtures__/server.ts @@ -37,6 +37,9 @@ export function serverFixture() { getUser: stub(), authenticate: stub(), deauthenticate: stub(), + authorization: { + application: stub(), + }, }, xpack_main: { diff --git a/x-pack/legacy/plugins/security/server/routes/api/v1/api_keys/get.test.js b/x-pack/legacy/plugins/security/server/routes/api/v1/api_keys/get.test.js new file mode 100644 index 00000000000000..400e5b705aeb2a --- /dev/null +++ b/x-pack/legacy/plugins/security/server/routes/api/v1/api_keys/get.test.js @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import Hapi from 'hapi'; +import Boom from 'boom'; + +import { initGetApiKeysApi } from './get'; +import { INTERNAL_API_BASE_PATH } from '../../../../../common/constants'; + +const createMockServer = () => new Hapi.Server({ debug: false, port: 8080 }); + +describe('GET API keys', () => { + const getApiKeysTest = ( + description, + { + preCheckLicenseImpl = () => null, + callWithRequestImpl, + asserts, + isAdmin = true, + } + ) => { + test(description, async () => { + const mockServer = createMockServer(); + const pre = jest.fn().mockImplementation(preCheckLicenseImpl); + const mockCallWithRequest = jest.fn(); + + if (callWithRequestImpl) { + mockCallWithRequest.mockImplementation(callWithRequestImpl); + } + + initGetApiKeysApi(mockServer, mockCallWithRequest, pre); + + const headers = { + authorization: 'foo', + }; + + const request = { + method: 'GET', + url: `${INTERNAL_API_BASE_PATH}/api_key?isAdmin=${isAdmin}`, + headers, + }; + + const { result, statusCode } = await mockServer.inject(request); + + expect(pre).toHaveBeenCalled(); + + if (callWithRequestImpl) { + expect(mockCallWithRequest).toHaveBeenCalledWith( + expect.objectContaining({ + headers: expect.objectContaining({ + authorization: headers.authorization, + }), + }), + 'shield.getAPIKeys', + { + owner: !isAdmin, + }, + ); + } else { + expect(mockCallWithRequest).not.toHaveBeenCalled(); + } + + expect(statusCode).toBe(asserts.statusCode); + expect(result).toEqual(asserts.result); + }); + }; + + describe('failure', () => { + getApiKeysTest('returns result of routePreCheckLicense', { + preCheckLicenseImpl: () => Boom.forbidden('test forbidden message'), + asserts: { + statusCode: 403, + result: { + error: 'Forbidden', + statusCode: 403, + message: 'test forbidden message', + }, + }, + }); + + getApiKeysTest('returns error from callWithRequest', { + callWithRequestImpl: async () => { + throw Boom.notAcceptable('test not acceptable message'); + }, + asserts: { + statusCode: 406, + result: { + error: 'Not Acceptable', + statusCode: 406, + message: 'test not acceptable message', + }, + }, + }); + }); + + describe('success', () => { + getApiKeysTest('returns API keys', { + callWithRequestImpl: async () => ({ + api_keys: + [{ + id: 'YCLV7m0BJ3xI4hhWB648', + name: 'test-api-key', + creation: 1571670001452, + expiration: 1571756401452, + invalidated: false, + username: 'elastic', + realm: 'reserved' + }] + }), + asserts: { + statusCode: 200, + result: { + apiKeys: + [{ + id: 'YCLV7m0BJ3xI4hhWB648', + name: 'test-api-key', + creation: 1571670001452, + expiration: 1571756401452, + invalidated: false, + username: 'elastic', + realm: 'reserved' + }] + }, + }, + }); + getApiKeysTest('returns only valid API keys', { + callWithRequestImpl: async () => ({ + api_keys: + [{ + id: 'YCLV7m0BJ3xI4hhWB648', + name: 'test-api-key1', + creation: 1571670001452, + expiration: 1571756401452, + invalidated: true, + username: 'elastic', + realm: 'reserved' + }, { + id: 'YCLV7m0BJ3xI4hhWB648', + name: 'test-api-key2', + creation: 1571670001452, + expiration: 1571756401452, + invalidated: false, + username: 'elastic', + realm: 'reserved' + }], + }), + asserts: { + statusCode: 200, + result: { + apiKeys: + [{ + id: 'YCLV7m0BJ3xI4hhWB648', + name: 'test-api-key2', + creation: 1571670001452, + expiration: 1571756401452, + invalidated: false, + username: 'elastic', + realm: 'reserved' + }] + }, + }, + }); + }); +}); diff --git a/x-pack/legacy/plugins/security/server/routes/api/v1/api_keys/invalidate.test.js b/x-pack/legacy/plugins/security/server/routes/api/v1/api_keys/invalidate.test.js new file mode 100644 index 00000000000000..3ed7ca94eb782a --- /dev/null +++ b/x-pack/legacy/plugins/security/server/routes/api/v1/api_keys/invalidate.test.js @@ -0,0 +1,200 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import Hapi from 'hapi'; +import Boom from 'boom'; + +import { initInvalidateApiKeysApi } from './invalidate'; +import { INTERNAL_API_BASE_PATH } from '../../../../../common/constants'; + +const createMockServer = () => new Hapi.Server({ debug: false, port: 8080 }); + +describe('POST invalidate', () => { + const postInvalidateTest = ( + description, + { + preCheckLicenseImpl = () => null, + callWithRequestImpls = [], + asserts, + payload, + } + ) => { + test(description, async () => { + const mockServer = createMockServer(); + const pre = jest.fn().mockImplementation(preCheckLicenseImpl); + const mockCallWithRequest = jest.fn(); + + for (const impl of callWithRequestImpls) { + mockCallWithRequest.mockImplementationOnce(impl); + } + + initInvalidateApiKeysApi(mockServer, mockCallWithRequest, pre); + + const headers = { + authorization: 'foo', + }; + + const request = { + method: 'POST', + url: `${INTERNAL_API_BASE_PATH}/api_key/invalidate`, + headers, + payload, + }; + + const { result, statusCode } = await mockServer.inject(request); + + expect(pre).toHaveBeenCalled(); + + if (asserts.callWithRequests) { + for (const args of asserts.callWithRequests) { + expect(mockCallWithRequest).toHaveBeenCalledWith( + expect.objectContaining({ + headers: expect.objectContaining({ + authorization: headers.authorization, + }), + }), + ...args + ); + } + } else { + expect(mockCallWithRequest).not.toHaveBeenCalled(); + } + + expect(statusCode).toBe(asserts.statusCode); + expect(result).toEqual(asserts.result); + }); + }; + + describe('failure', () => { + postInvalidateTest('returns result of routePreCheckLicense', { + preCheckLicenseImpl: () => Boom.forbidden('test forbidden message'), + payload: { + apiKeys: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key' }], + isAdmin: true + }, + asserts: { + statusCode: 403, + result: { + error: 'Forbidden', + statusCode: 403, + message: 'test forbidden message', + }, + }, + }); + + postInvalidateTest('returns errors array from callWithRequest', { + callWithRequestImpls: [async () => { + throw Boom.notAcceptable('test not acceptable message'); + }], + payload: { + apiKeys: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key', }], + isAdmin: true + }, + asserts: { + callWithRequests: [ + ['shield.invalidateAPIKey', { + body: { + id: 'si8If24B1bKsmSLTAhJV', + }, + }], + ], + statusCode: 200, + result: { + itemsInvalidated: [], + errors: [{ + id: 'si8If24B1bKsmSLTAhJV', + name: 'my-api-key', + error: Boom.notAcceptable('test not acceptable message'), + }] + }, + }, + }); + }); + + describe('success', () => { + postInvalidateTest('invalidates API keys', { + callWithRequestImpls: [async () => null], + payload: { + apiKeys: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key', }], + isAdmin: true + }, + asserts: { + callWithRequests: [ + ['shield.invalidateAPIKey', { + body: { + id: 'si8If24B1bKsmSLTAhJV', + }, + }], + ], + statusCode: 200, + result: { + itemsInvalidated: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key', }], + errors: [], + }, + }, + }); + + postInvalidateTest('adds "owner" to body if isAdmin=false', { + callWithRequestImpls: [async () => null], + payload: { + apiKeys: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key', }], + isAdmin: false + }, + asserts: { + callWithRequests: [ + ['shield.invalidateAPIKey', { + body: { + id: 'si8If24B1bKsmSLTAhJV', + owner: true, + }, + }], + ], + statusCode: 200, + result: { + itemsInvalidated: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key' }], + errors: [], + }, + }, + }); + + postInvalidateTest('returns only successful invalidation requests', { + callWithRequestImpls: [ + async () => null, + async () => { + throw Boom.notAcceptable('test not acceptable message'); + }], + payload: { + apiKeys: [ + { id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key1' }, + { id: 'ab8If24B1bKsmSLTAhNC', name: 'my-api-key2' } + ], + isAdmin: true + }, + asserts: { + callWithRequests: [ + ['shield.invalidateAPIKey', { + body: { + id: 'si8If24B1bKsmSLTAhJV', + }, + }], + ['shield.invalidateAPIKey', { + body: { + id: 'ab8If24B1bKsmSLTAhNC', + }, + }], + ], + statusCode: 200, + result: { + itemsInvalidated: [{ id: 'si8If24B1bKsmSLTAhJV', name: 'my-api-key1' }], + errors: [{ + id: 'ab8If24B1bKsmSLTAhNC', + name: 'my-api-key2', + error: Boom.notAcceptable('test not acceptable message'), + }] + }, + }, + }); + }); +}); diff --git a/x-pack/legacy/plugins/security/server/routes/api/v1/api_keys/privileges.test.js b/x-pack/legacy/plugins/security/server/routes/api/v1/api_keys/privileges.test.js new file mode 100644 index 00000000000000..2a6f935e005950 --- /dev/null +++ b/x-pack/legacy/plugins/security/server/routes/api/v1/api_keys/privileges.test.js @@ -0,0 +1,254 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import Hapi from 'hapi'; +import Boom from 'boom'; + +import { initCheckPrivilegesApi } from './privileges'; +import { INTERNAL_API_BASE_PATH } from '../../../../../common/constants'; + +const createMockServer = () => new Hapi.Server({ debug: false, port: 8080 }); + +describe('GET privileges', () => { + const getPrivilegesTest = ( + description, + { + preCheckLicenseImpl = () => null, + callWithRequestImpls = [], + asserts, + } + ) => { + test(description, async () => { + const mockServer = createMockServer(); + const pre = jest.fn().mockImplementation(preCheckLicenseImpl); + const mockCallWithRequest = jest.fn(); + + for (const impl of callWithRequestImpls) { + mockCallWithRequest.mockImplementationOnce(impl); + } + + initCheckPrivilegesApi(mockServer, mockCallWithRequest, pre); + + const headers = { + authorization: 'foo', + }; + + const request = { + method: 'GET', + url: `${INTERNAL_API_BASE_PATH}/api_key/privileges`, + headers, + }; + + const { result, statusCode } = await mockServer.inject(request); + + expect(pre).toHaveBeenCalled(); + + if (asserts.callWithRequests) { + for (const args of asserts.callWithRequests) { + expect(mockCallWithRequest).toHaveBeenCalledWith( + expect.objectContaining({ + headers: expect.objectContaining({ + authorization: headers.authorization, + }), + }), + ...args + ); + } + } else { + expect(mockCallWithRequest).not.toHaveBeenCalled(); + } + + expect(statusCode).toBe(asserts.statusCode); + expect(result).toEqual(asserts.result); + }); + }; + + describe('failure', () => { + getPrivilegesTest('returns result of routePreCheckLicense', { + preCheckLicenseImpl: () => Boom.forbidden('test forbidden message'), + asserts: { + statusCode: 403, + result: { + error: 'Forbidden', + statusCode: 403, + message: 'test forbidden message', + }, + }, + }); + + getPrivilegesTest('returns error from first callWithRequest', { + callWithRequestImpls: [async () => { + throw Boom.notAcceptable('test not acceptable message'); + }, async () => { }], + asserts: { + callWithRequests: [ + ['shield.hasPrivileges', { + body: { + cluster: [ + 'manage_security', + 'manage_api_key', + ], + }, + }], + ['shield.getAPIKeys', { owner: true }], + ], + statusCode: 406, + result: { + error: 'Not Acceptable', + statusCode: 406, + message: 'test not acceptable message', + }, + }, + }); + + getPrivilegesTest('returns error from second callWithRequest', { + callWithRequestImpls: [async () => { }, async () => { + throw Boom.notAcceptable('test not acceptable message'); + }], + asserts: { + callWithRequests: [ + ['shield.hasPrivileges', { + body: { + cluster: [ + 'manage_security', + 'manage_api_key', + ], + }, + }], + ['shield.getAPIKeys', { owner: true }], + ], + statusCode: 406, + result: { + error: 'Not Acceptable', + statusCode: 406, + message: 'test not acceptable message', + }, + }, + }); + }); + + describe('success', () => { + getPrivilegesTest('returns areApiKeysEnabled and isAdmin', { + callWithRequestImpls: [ + async () => ({ + username: 'elastic', + has_all_requested: true, + cluster: { manage_api_key: true, manage_security: true }, + index: {}, + application: {} + }), + async () => ( + { + api_keys: + [{ + id: 'si8If24B1bKsmSLTAhJV', + name: 'my-api-key', + creation: 1574089261632, + expiration: 1574175661632, + invalidated: false, + username: 'elastic', + realm: 'reserved' + }] + } + ), + ], + asserts: { + callWithRequests: [ + ['shield.getAPIKeys', { owner: true }], + ['shield.hasPrivileges', { + body: { + cluster: [ + 'manage_security', + 'manage_api_key', + ], + }, + }], + ], + statusCode: 200, + result: { + areApiKeysEnabled: true, + isAdmin: true, + }, + }, + }); + + getPrivilegesTest('returns areApiKeysEnabled=false when getAPIKeys error message includes "api keys are not enabled"', { + callWithRequestImpls: [ + async () => ({ + username: 'elastic', + has_all_requested: true, + cluster: { manage_api_key: true, manage_security: true }, + index: {}, + application: {} + }), + async () => { + throw Boom.unauthorized('api keys are not enabled'); + }, + ], + asserts: { + callWithRequests: [ + ['shield.getAPIKeys', { owner: true }], + ['shield.hasPrivileges', { + body: { + cluster: [ + 'manage_security', + 'manage_api_key', + ], + }, + }], + ], + statusCode: 200, + result: { + areApiKeysEnabled: false, + isAdmin: true, + }, + }, + }); + + getPrivilegesTest('returns isAdmin=false when user has insufficient privileges', { + callWithRequestImpls: [ + async () => ({ + username: 'elastic', + has_all_requested: true, + cluster: { manage_api_key: false, manage_security: false }, + index: {}, + application: {} + }), + async () => ( + { + api_keys: + [{ + id: 'si8If24B1bKsmSLTAhJV', + name: 'my-api-key', + creation: 1574089261632, + expiration: 1574175661632, + invalidated: false, + username: 'elastic', + realm: 'reserved' + }] + } + ), + ], + asserts: { + callWithRequests: [ + ['shield.getAPIKeys', { owner: true }], + ['shield.hasPrivileges', { + body: { + cluster: [ + 'manage_security', + 'manage_api_key', + ], + }, + }], + ], + statusCode: 200, + result: { + areApiKeysEnabled: true, + isAdmin: false, + }, + }, + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts index 400f82bf811886..57a1f318a7e31a 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts @@ -57,7 +57,7 @@ describe('Events Viewer', () => { .should('eq', 'Customize Columns'); }); - it.skip('closes the fields browser when the user clicks outside of it', () => { + it('closes the fields browser when the user clicks outside of it', () => { openEventsViewerFieldsBrowser(); clickOutsideFieldsBrowser(); @@ -81,7 +81,7 @@ describe('Events Viewer', () => { ); }); - it.skip('removes the message field from the timeline when the user un-checks the field', () => { + it('removes the message field from the timeline when the user un-checks the field', () => { const toggleField = 'message'; cy.get(`${EVENTS_VIEWER_PANEL} [data-test-subj="header-text-${toggleField}"]`).should('exist'); @@ -99,7 +99,7 @@ describe('Events Viewer', () => { ); }); - it.skip('filters the events by applying filter criteria from the search bar at the top of the page', () => { + it('filters the events by applying filter criteria from the search bar at the top of the page', () => { const filterInput = '4bf34c1c-eaa9-46de-8921-67a4ccc49829'; // this will never match real data cy.get(HEADER_SUBTITLE) @@ -119,7 +119,7 @@ describe('Events Viewer', () => { }); }); - it.skip('adds a field to the events viewer when the user clicks the checkbox', () => { + it('adds a field to the events viewer when the user clicks the checkbox', () => { const filterInput = 'host.geo.c'; const toggleField = 'host.geo.city_name'; @@ -158,7 +158,7 @@ describe('Events Viewer', () => { }); }); - it.skip('launches the inspect query modal when the inspect button is clicked', () => { + it('launches the inspect query modal when the inspect button is clicked', () => { // wait for data to load cy.get(HEADER_SUBTITLE) .invoke('text') @@ -171,7 +171,7 @@ describe('Events Viewer', () => { cy.get(INSPECT_MODAL, { timeout: DEFAULT_TIMEOUT }).should('exist'); }); - it.skip('resets all fields in the events viewer when `Reset Fields` is clicked', () => { + it('resets all fields in the events viewer when `Reset Fields` is clicked', () => { const filterInput = 'host.geo.c'; const toggleField = 'host.geo.city_name'; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts index cec5d6d88d86db..a03ff0c1845f85 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts @@ -28,7 +28,7 @@ describe('ml conditional links', () => { return logout(); }); - it.skip('sets the KQL from a single IP with a value for the query', () => { + it('sets the KQL from a single IP with a value for the query', () => { loginAndWaitForPage(mlNetworkSingleIpKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', @@ -37,7 +37,7 @@ describe('ml conditional links', () => { ); }); - it.skip('sets the KQL from a multiple IPs with a null for the query', () => { + it('sets the KQL from a multiple IPs with a null for the query', () => { loginAndWaitForPage(mlNetworkMultipleIpNullKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', @@ -46,7 +46,7 @@ describe('ml conditional links', () => { ); }); - it.skip('sets the KQL from a multiple IPs with a value for the query', () => { + it('sets the KQL from a multiple IPs with a value for the query', () => { loginAndWaitForPage(mlNetworkMultipleIpKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', @@ -55,7 +55,7 @@ describe('ml conditional links', () => { ); }); - it.skip('sets the KQL from a $ip$ with a value for the query', () => { + it('sets the KQL from a $ip$ with a value for the query', () => { loginAndWaitForPage(mlNetworkKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', @@ -64,7 +64,7 @@ describe('ml conditional links', () => { ); }); - it.skip('sets the KQL from a single host name with a value for query', () => { + it('sets the KQL from a single host name with a value for query', () => { loginAndWaitForPage(mlHostSingleHostKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', @@ -73,7 +73,7 @@ describe('ml conditional links', () => { ); }); - it.skip('sets the KQL from a multiple host names with null for query', () => { + it('sets the KQL from a multiple host names with null for query', () => { loginAndWaitForPage(mlHostMultiHostNullKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', @@ -82,7 +82,7 @@ describe('ml conditional links', () => { ); }); - it.skip('sets the KQL from a multiple host names with a value for query', () => { + it('sets the KQL from a multiple host names with a value for query', () => { loginAndWaitForPage(mlHostMultiHostKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', @@ -91,7 +91,7 @@ describe('ml conditional links', () => { ); }); - it.skip('sets the KQL from a undefined/null host name but with a value for query', () => { + it('sets the KQL from a undefined/null host name but with a value for query', () => { loginAndWaitForPage(mlHostVariableHostKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', @@ -100,7 +100,7 @@ describe('ml conditional links', () => { ); }); - it.skip('redirects from a single IP with a null for the query', () => { + it('redirects from a single IP with a null for the query', () => { loginAndWaitForPage(mlNetworkSingleIpNullKqlQuery); cy.url().should( 'include', @@ -108,7 +108,7 @@ describe('ml conditional links', () => { ); }); - it.skip('redirects from a single IP with a value for the query', () => { + it('redirects from a single IP with a value for the query', () => { loginAndWaitForPage(mlNetworkSingleIpKqlQuery); cy.url().should( 'include', @@ -116,7 +116,7 @@ describe('ml conditional links', () => { ); }); - it.skip('redirects from a multiple IPs with a null for the query', () => { + it('redirects from a multiple IPs with a null for the query', () => { loginAndWaitForPage(mlNetworkMultipleIpNullKqlQuery); cy.url().should( 'include', diff --git a/x-pack/legacy/plugins/siem/public/components/autocomplete_field/index.tsx b/x-pack/legacy/plugins/siem/public/components/autocomplete_field/index.tsx index 408743d2617971..b78f2b0d07d39e 100644 --- a/x-pack/legacy/plugins/siem/public/components/autocomplete_field/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/autocomplete_field/index.tsx @@ -307,7 +307,7 @@ const withUnfocused = (state: AutocompleteFieldState) => ({ isFocused: false, }); -export const FixedEuiFieldSearch: React.SFC & +export const FixedEuiFieldSearch: React.FC & EuiFieldSearchProps & { inputRef?: (element: HTMLInputElement | null) => void; onSearch: (value: string) => void; diff --git a/x-pack/legacy/plugins/siem/public/components/help_menu/help_menu.tsx b/x-pack/legacy/plugins/siem/public/components/help_menu/help_menu.tsx deleted file mode 100644 index b59753e8add6a7..00000000000000 --- a/x-pack/legacy/plugins/siem/public/components/help_menu/help_menu.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { EuiButton, EuiHorizontalRule, EuiIcon, EuiTitle, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import theme from '@elastic/eui/dist/eui_theme_light.json'; -import styled from 'styled-components'; - -export const Icon = styled(EuiIcon)` - margin-right: ${theme.euiSizeS}; -`; - -Icon.displayName = 'Icon'; - -export const HelpMenuComponent = React.memo(() => ( - <> - - - - - -

- - -
- - - - - - - - -)); - -HelpMenuComponent.displayName = 'HelpMenuComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/help_menu/index.tsx b/x-pack/legacy/plugins/siem/public/components/help_menu/index.tsx index d145769319d5bd..43fd8e653f3d8d 100644 --- a/x-pack/legacy/plugins/siem/public/components/help_menu/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/help_menu/index.tsx @@ -4,20 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import { render, unmountComponentAtNode } from 'react-dom'; -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import { pure } from 'recompose'; import chrome from 'ui/chrome'; - -import { HelpMenuComponent } from './help_menu'; +import { i18n } from '@kbn/i18n'; export const HelpMenu = pure<{}>(() => { useEffect(() => { - chrome.helpExtension.set(domNode => { - render(, domNode); - return () => { - unmountComponentAtNode(domNode); - }; + chrome.helpExtension.set({ + appName: i18n.translate('xpack.siem.chrome.help.appName', { + defaultMessage: 'SIEM', + }), + links: [ + { + linkType: 'discuss', + href: 'https://discuss.elastic.co/c/siem', + }, + ], }); }, []); diff --git a/x-pack/legacy/plugins/siem/public/components/query_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/query_bar/index.tsx index c7e58532fc7e5b..8b5f3b0f4d4258 100644 --- a/x-pack/legacy/plugins/siem/public/components/query_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/query_bar/index.tsx @@ -20,6 +20,7 @@ import { SavedQueryTimeFilter } from '../../../../../../../src/legacy/core_plugi import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; export interface QueryBarComponentProps { + dataTestSubj?: string; dateRangeFrom?: string; dateRangeTo?: string; hideSavedQuery?: boolean; @@ -50,6 +51,7 @@ export const QueryBar = memo( refreshInterval, savedQuery, onSavedQuery, + dataTestSubj, }) => { const [draftQuery, setDraftQuery] = useState(filterQuery); @@ -139,6 +141,7 @@ export const QueryBar = memo( showQueryInput={true} showSaveQuery={true} timeHistory={new TimeHistory(new Storage(localStorage))} + dataTestSubj={dataTestSubj} {...searchBarProps} /> ); diff --git a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx index c16c3a33872d56..33fb2b9239a6a4 100644 --- a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx @@ -71,6 +71,7 @@ interface SiemSearchBarProps { id: InputsModelId; indexPattern: StaticIndexPattern; timelineId?: string; + dataTestSubj?: string; } const SearchBarContainer = styled.div` @@ -95,6 +96,7 @@ const SearchBarComponent = memo { const { timefilter } = npStart.plugins.data.query.timefilter; if (fromStr != null && toStr != null) { @@ -275,6 +277,7 @@ const SearchBarComponent = memo ); diff --git a/x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx b/x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx index 123e14d2391828..1b7e042e90dbf2 100644 --- a/x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx @@ -31,15 +31,26 @@ Wrapper.displayName = 'Wrapper'; interface SubtitleItemProps { children: string | React.ReactNode; + dataTestSubj?: string; } -const SubtitleItem = React.memo(({ children }) => { - if (typeof children === 'string') { - return

{children}

; - } else { - return
{children}
; +const SubtitleItem = React.memo( + ({ children, dataTestSubj = 'header-panel-subtitle' }) => { + if (typeof children === 'string') { + return ( +

+ {children} +

+ ); + } else { + return ( +
+ {children} +
+ ); + } } -}); +); SubtitleItem.displayName = 'SubtitleItem'; export interface SubtitleProps { diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/query_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/query_bar/index.tsx index 6d76c277711d7f..f24ee3155c9242 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/query_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/query_bar/index.tsx @@ -288,6 +288,7 @@ export const QueryBarTimeline = memo( refreshInterval={refreshInterval} savedQuery={savedQuery} onSavedQuery={onSavedQuery} + dataTestSubj={'timelineQueryInput'} /> ); } diff --git a/x-pack/legacy/plugins/spaces/public/views/components/space_card.test.tsx b/x-pack/legacy/plugins/spaces/public/views/components/space_card.test.tsx index 7f04189b50d5ec..cb70daead1bd23 100644 --- a/x-pack/legacy/plugins/spaces/public/views/components/space_card.test.tsx +++ b/x-pack/legacy/plugins/spaces/public/views/components/space_card.test.tsx @@ -30,7 +30,7 @@ test('it is clickable', () => { const clickHandler = jest.fn(); const wrapper = mount(); - wrapper.simulate('click'); + wrapper.find('button').simulate('click'); expect(clickHandler).toHaveBeenCalledTimes(1); }); diff --git a/x-pack/legacy/plugins/upgrade_assistant/common/types.ts b/x-pack/legacy/plugins/upgrade_assistant/common/types.ts index 60018173781a0d..ce653e461e13b8 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/common/types.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/common/types.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Legacy } from 'kibana'; import { SavedObject, SavedObjectAttributes } from 'src/core/server'; export enum ReindexStep { @@ -79,15 +78,6 @@ export interface UIReindex { stop: boolean; } -export interface UpgradeAssistantTelemetryServer extends Legacy.Server { - usage: { - collectorSet: { - makeUsageCollector: any; - register: any; - }; - }; -} - export interface UpgradeAssistantTelemetrySavedObject { ui_open: { overview: number; diff --git a/x-pack/legacy/plugins/upgrade_assistant/index.ts b/x-pack/legacy/plugins/upgrade_assistant/index.ts index 7c38fbf02a5647..f1762498246c76 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/index.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/index.ts @@ -3,18 +3,23 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import _ from 'lodash'; import Joi from 'joi'; import { Legacy } from 'kibana'; import { resolve } from 'path'; import mappings from './mappings.json'; -import { initServer } from './server'; +import { plugin } from './server/np_ready'; export function upgradeAssistant(kibana: any) { - return new kibana.Plugin({ + const publicSrc = resolve(__dirname, 'public'); + const npSrc = resolve(publicSrc, 'np_ready'); + + const config: Legacy.PluginSpecOptions = { id: 'upgrade_assistant', configPrefix: 'xpack.upgrade_assistant', require: ['elasticsearch'], uiExports: { + // @ts-ignore managementSections: ['plugins/upgrade_assistant'], savedObjectSchemas: { 'upgrade-assistant-reindex-operation': { @@ -24,10 +29,10 @@ export function upgradeAssistant(kibana: any) { isNamespaceAgnostic: true, }, }, - styleSheetPaths: resolve(__dirname, 'public/index.scss'), + styleSheetPaths: resolve(npSrc, 'application/index.scss'), mappings, }, - publicDir: resolve(__dirname, 'public'), + publicDir: publicSrc, config() { return Joi.object({ @@ -37,7 +42,30 @@ export function upgradeAssistant(kibana: any) { init(server: Legacy.Server) { // Add server routes and initialize the plugin here - initServer(server); + const instance = plugin({} as any); + instance.setup(server.newPlatform.setup.core, { + __LEGACY: { + // Legacy objects + events: server.events, + usage: server.usage, + savedObjects: server.savedObjects, + + // Legacy functions + log: server.log.bind(server), + + // Legacy plugins + plugins: { + elasticsearch: server.plugins.elasticsearch, + xpack_main: server.plugins.xpack_main, + cloud: { + config: { + isCloudEnabled: _.get(server.plugins, 'cloud.config.isCloudEnabled', false), + }, + }, + }, + }, + }); }, - }); + }; + return new kibana.Plugin(config); } diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/app.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/app.tsx deleted file mode 100644 index 3bf3375c43bd98..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/public/app.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; - -import { EuiPageHeader, EuiPageHeaderSection, EuiTitle } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { NEXT_MAJOR_VERSION } from '../common/version'; -import { UpgradeAssistantTabs } from './components/tabs'; - -export const RootComponent: React.StatelessComponent = () => ( -
- - - -

- -

-
-
-
- -
-); diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/components/_index.scss deleted file mode 100644 index 80260319223018..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './tabs/checkup/index'; -@import './tabs/overview/index'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/_index.scss deleted file mode 100644 index 430d1e0aedf7bd..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './deprecations/index'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/_index.scss deleted file mode 100644 index 2d52575cffbbbe..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './button'; -@import './flyout/index'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/flyout/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/flyout/_index.scss deleted file mode 100644 index f695ae175fecad..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/flyout/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './step_progress'; -@import './warnings_step'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/_index.scss deleted file mode 100644 index a6bbc6ba132988..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './steps'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/index.ts b/x-pack/legacy/plugins/upgrade_assistant/public/index.ts new file mode 100644 index 00000000000000..d22b5d64b6b469 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './legacy'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/index.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/index.tsx deleted file mode 100644 index 2f631d3771ecb0..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/public/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; -import { wrapInI18nContext } from 'ui/i18n'; -// @ts-ignore -import { management } from 'ui/management'; -// @ts-ignore -import { uiModules } from 'ui/modules'; -// @ts-ignore -import routes from 'ui/routes'; -import { NEXT_MAJOR_VERSION } from '../common/version'; -import { RootComponent } from './app'; - -const BASE_PATH = `/management/elasticsearch/upgrade_assistant`; - -function startApp() { - management.getSection('elasticsearch').register('upgrade_assistant', { - visible: true, - display: i18n.translate('xpack.upgradeAssistant.appTitle', { - defaultMessage: '{version} Upgrade Assistant', - values: { version: `${NEXT_MAJOR_VERSION}.0` }, - }), - order: 100, - url: `#${BASE_PATH}`, - }); - - uiModules.get('kibana').directive('upgradeAssistant', (reactDirective: any) => { - return reactDirective(wrapInI18nContext(RootComponent)); - }); - - routes.when(`${BASE_PATH}/:view?`, { - template: - '', - }); -} - -startApp(); diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/legacy.ts b/x-pack/legacy/plugins/upgrade_assistant/public/legacy.ts new file mode 100644 index 00000000000000..3d5144249dfef9 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/legacy.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ComponentType } from 'react'; +import { i18n } from '@kbn/i18n'; + +/* LEGACY IMPORTS */ +import { npSetup } from 'ui/new_platform'; +import { wrapInI18nContext } from 'ui/i18n'; +import { management } from 'ui/management'; +// @ts-ignore +import { uiModules } from 'ui/modules'; +import routes from 'ui/routes'; +import chrome from 'ui/chrome'; +/* LEGACY IMPORTS */ + +import { NEXT_MAJOR_VERSION } from '../common/version'; +import { plugin } from './np_ready'; + +const BASE_PATH = `/management/elasticsearch/upgrade_assistant`; + +export interface LegacyAppMountParameters { + __LEGACY: { renderToElement: (RootComponent: ComponentType) => void }; +} + +export interface LegacyApp { + mount(ctx: any, params: LegacyAppMountParameters): void; +} + +export interface LegacyManagementPlugin { + sections: { + get( + name: string + ): { + registerApp(app: LegacyApp): void; + }; + }; +} + +// Based on /rfcs/text/0006_management_section_service.md +export interface LegacyPlugins { + management: LegacyManagementPlugin; + __LEGACY: { + XSRF: string; + isCloudEnabled: boolean; + }; +} + +function startApp() { + routes.when(`${BASE_PATH}/:view?`, { + template: + '', + }); + + const legacyPluginsShim: LegacyPlugins = { + __LEGACY: { + XSRF: chrome.getXsrfToken(), + isCloudEnabled: chrome.getInjected('isCloudEnabled', false), + }, + management: { + sections: { + get(_: string) { + return { + registerApp(app) { + management.getSection('elasticsearch').register('upgrade_assistant', { + visible: true, + display: i18n.translate('xpack.upgradeAssistant.appTitle', { + defaultMessage: '{version} Upgrade Assistant', + values: { version: `${NEXT_MAJOR_VERSION}.0` }, + }), + order: 100, + url: `#${BASE_PATH}`, + }); + + app.mount( + {}, + { + __LEGACY: { + // While there is not an NP API for registering management section apps yet + renderToElement: RootComponent => { + uiModules + .get('kibana') + .directive('upgradeAssistant', (reactDirective: any) => { + return reactDirective(wrapInI18nContext(RootComponent)); + }); + }, + }, + } + ); + }, + }; + }, + }, + }, + }; + + const pluginInstance = plugin(); + + pluginInstance.setup(npSetup.core, legacyPluginsShim); +} + +startApp(); diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/app.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/app.tsx new file mode 100644 index 00000000000000..571967ab114c90 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/app.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { EuiPageHeader, EuiPageHeaderSection, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { NEXT_MAJOR_VERSION } from '../../../common/version'; +import { UpgradeAssistantTabs } from './components/tabs'; +import { AppContextProvider, ContextValue, AppContext } from './app_context'; + +type AppDependencies = ContextValue; + +export const RootComponent = (deps: AppDependencies) => { + return ( + +
+ + + +

+ +

+
+
+
+ + {({ http }) => } + +
+
+ ); +}; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/app_context.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/app_context.tsx new file mode 100644 index 00000000000000..a48a4efa3bbdfb --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/app_context.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { HttpSetup } from 'src/core/public'; +import React, { createContext, useContext } from 'react'; + +export interface ContextValue { + http: HttpSetup; + isCloudEnabled: boolean; + XSRF: string; +} + +export const AppContext = createContext({} as any); + +export const AppContextProvider = ({ + children, + value, +}: { + children: React.ReactNode; + value: ContextValue; +}) => { + return {children}; +}; + +export const useAppContext = () => { + const ctx = useContext(AppContext); + if (!ctx) { + throw new Error('useAppContext must be called from inside AppContext'); + } + return ctx; +}; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/_index.scss new file mode 100644 index 00000000000000..bb01107f334f65 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/_index.scss @@ -0,0 +1,2 @@ +@import 'tabs/checkup/index'; +@import 'tabs/overview/index'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/error_banner.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/error_banner.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/error_banner.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/error_banner.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/latest_minor_banner.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/latest_minor_banner.tsx similarity index 98% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/latest_minor_banner.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/latest_minor_banner.tsx index 1f24377eba6cd1..02f090565ebe6b 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/latest_minor_banner.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/latest_minor_banner.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { EuiCallOut, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { CURRENT_MAJOR_VERSION, NEXT_MAJOR_VERSION } from '../../common/version'; +import { CURRENT_MAJOR_VERSION, NEXT_MAJOR_VERSION } from '../../../../common/version'; export const LatestMinorBanner: React.StatelessComponent = () => ( ({ get: jest.fn(), create: jest.fn(), @@ -23,6 +21,13 @@ import { OverviewTab } from './tabs/overview'; // Used to wait for promises to resolve and renders to finish before reading updates const promisesToResolve = () => new Promise(resolve => setTimeout(resolve, 0)); +const mockHttp = { + basePath: { + prepend: () => 'test', + }, + fetch() {}, +}; + describe('UpgradeAssistantTabs', () => { test('renders loading state', async () => { // @ts-ignore @@ -31,7 +36,7 @@ describe('UpgradeAssistantTabs', () => { /* never resolve */ }) ); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); // Should pass down loading status to child component expect(wrapper.find(OverviewTab).prop('loadingState')).toEqual(LoadingState.Loading); }); @@ -44,7 +49,7 @@ describe('UpgradeAssistantTabs', () => { indices: [], }, }); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); expect(axios.get).toHaveBeenCalled(); await promisesToResolve(); wrapper.update(); @@ -55,7 +60,7 @@ describe('UpgradeAssistantTabs', () => { test('network failure', async () => { // @ts-ignore axios.get.mockRejectedValue(new Error(`oh no!`)); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); await promisesToResolve(); wrapper.update(); // Should pass down error status to child component @@ -65,7 +70,7 @@ describe('UpgradeAssistantTabs', () => { it('upgrade error', async () => { // @ts-ignore axios.get.mockRejectedValue({ response: { status: 426 } }); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); await promisesToResolve(); wrapper.update(); // Should display an informative message if the cluster is currently mid-upgrade diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs.tsx similarity index 93% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs.tsx index 76cc1e33ca06b2..0b154fb20404dc 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs.tsx @@ -16,11 +16,9 @@ import { EuiTabbedContentTab, } from '@elastic/eui'; import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; +import { HttpSetup } from 'src/core/public'; -import chrome from 'ui/chrome'; -import { kfetch } from 'ui/kfetch'; - -import { UpgradeAssistantStatus } from '../../server/lib/es_migration_apis'; +import { UpgradeAssistantStatus } from '../../../../server/np_ready/lib/es_migration_apis'; import { LatestMinorBanner } from './latest_minor_banner'; import { CheckupTab } from './tabs/checkup'; import { OverviewTab } from './tabs/overview'; @@ -41,13 +39,11 @@ interface TabsState { clusterUpgradeState: ClusterUpgradeState; } -export class UpgradeAssistantTabsUI extends React.Component< - ReactIntl.InjectedIntlProps, - TabsState -> { - constructor(props: ReactIntl.InjectedIntlProps) { - super(props); +type Props = ReactIntl.InjectedIntlProps & { http: HttpSetup }; +export class UpgradeAssistantTabsUI extends React.Component { + constructor(props: Props) { + super(props); this.state = { loadingState: LoadingState.Loading, clusterUpgradeState: ClusterUpgradeState.needsUpgrade, @@ -157,7 +153,9 @@ export class UpgradeAssistantTabsUI extends React.Component< private loadData = async () => { try { this.setState({ loadingState: LoadingState.Loading }); - const resp = await axios.get(chrome.addBasePath('/api/upgrade_assistant/status')); + const resp = await axios.get( + this.props.http.basePath.prepend('/api/upgrade_assistant/status') + ); this.setState({ loadingState: LoadingState.Success, checkupData: resp.data, @@ -246,8 +244,7 @@ export class UpgradeAssistantTabsUI extends React.Component< this.setState({ telemetryState: TelemetryState.Running }); - await kfetch({ - pathname: '/api/upgrade_assistant/telemetry/ui_open', + await this.props.http.fetch('/api/upgrade_assistant/telemetry/ui_open', { method: 'PUT', body: JSON.stringify(set({}, tabName, true)), }); diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/__fixtures__/checkup_api_response.json b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/__fixtures__/checkup_api_response.json similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/__fixtures__/checkup_api_response.json rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/__fixtures__/checkup_api_response.json diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/__snapshots__/filter_bar.test.tsx.snap b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/__snapshots__/filter_bar.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/__snapshots__/filter_bar.test.tsx.snap rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/__snapshots__/filter_bar.test.tsx.snap diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/__snapshots__/group_by_bar.test.tsx.snap b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/__snapshots__/group_by_bar.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/__snapshots__/group_by_bar.test.tsx.snap rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/__snapshots__/group_by_bar.test.tsx.snap diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/_index.scss new file mode 100644 index 00000000000000..d64400a8abdcf4 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/_index.scss @@ -0,0 +1 @@ +@import 'deprecations/index'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/checkup_tab.test.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/checkup_tab.test.tsx similarity index 98% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/checkup_tab.test.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/checkup_tab.test.tsx index 1805cb49e6ee66..9ba5441604ddc0 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/checkup_tab.test.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/checkup_tab.test.tsx @@ -11,8 +11,6 @@ import { LoadingState } from '../../types'; import AssistanceData from '../__fixtures__/checkup_api_response.json'; import { CheckupTab } from './checkup_tab'; -jest.mock('ui/kfetch'); - const defaultProps = { checkupLabel: 'index', deprecations: AssistanceData.indices, diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/checkup_tab.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/checkup_tab.tsx similarity index 99% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/checkup_tab.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/checkup_tab.tsx index 5b8fd21f9c1fc1..9aa40663125aed 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/checkup_tab.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/checkup_tab.tsx @@ -20,7 +20,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { NEXT_MAJOR_VERSION } from '../../../../common/version'; +import { NEXT_MAJOR_VERSION } from '../../../../../../common/version'; import { LoadingErrorBanner } from '../../error_banner'; import { GroupByOption, diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/constants.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/constants.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/constants.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/constants.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/controls.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/controls.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/controls.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/controls.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/_cell.scss b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/_cell.scss similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/_cell.scss rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/_cell.scss diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/_deprecations.scss similarity index 88% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/_index.scss rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/_deprecations.scss index e370aeac0dfa23..445ef6269afb93 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/_index.scss +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/_deprecations.scss @@ -1,6 +1,3 @@ -@import './cell'; -@import './reindex/index'; - .upgDeprecations { // Pull the container through the padding of EuiPageContent margin-left: -$euiSizeL; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/_index.scss new file mode 100644 index 00000000000000..55aff6b379db54 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/_index.scss @@ -0,0 +1,3 @@ +@import 'cell'; +@import 'deprecations'; +@import 'reindex/index'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/cell.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/cell.tsx similarity index 89% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/cell.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/cell.tsx index 29a0076d00d0ea..98672eb249c9bd 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/cell.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/cell.tsx @@ -17,6 +17,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { ReindexButton } from './reindex'; +import { AppContext } from '../../../../app_context'; interface DeprecationCellProps { items?: Array<{ title?: string; body: string }>; @@ -77,7 +78,11 @@ export const DeprecationCell: StatelessComponent = ({ {reindexIndexName && ( - + + {({ http, XSRF }) => ( + + )} + )} diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/count_summary.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/count_summary.tsx similarity index 93% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/count_summary.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/count_summary.tsx index a66244b0e7886c..d3166292822d76 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/count_summary.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/count_summary.tsx @@ -8,7 +8,7 @@ import React, { Fragment, StatelessComponent } from 'react'; import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EnrichedDeprecationInfo } from '../../../../../server/lib/es_migration_apis'; +import { EnrichedDeprecationInfo } from '../../../../../../../server/np_ready/lib/es_migration_apis'; export const DeprecationCountSummary: StatelessComponent<{ deprecations: EnrichedDeprecationInfo[]; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/grouped.test.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/grouped.test.tsx similarity index 98% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/grouped.test.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/grouped.test.tsx index 6c541742a539b2..28f5f6894b78f2 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/grouped.test.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/grouped.test.tsx @@ -11,12 +11,10 @@ import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import { EuiBadge, EuiPagination } from '@elastic/eui'; import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch'; -import { EnrichedDeprecationInfo } from '../../../../../server/lib/es_migration_apis'; +import { EnrichedDeprecationInfo } from '../../../../../../../server/np_ready/lib/es_migration_apis'; import { GroupByOption, LevelFilterOption } from '../../../types'; import { DeprecationAccordion, filterDeps, GroupedDeprecations } from './grouped'; -jest.mock('ui/kfetch'); - describe('filterDeps', () => { test('filters on levels', () => { const fd = filterDeps(LevelFilterOption.critical); diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/grouped.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/grouped.tsx similarity index 98% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/grouped.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/grouped.tsx index 9ce0b8a8cefd36..9f53c96bce5c6e 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/grouped.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/grouped.tsx @@ -19,7 +19,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch'; -import { EnrichedDeprecationInfo } from '../../../../../server/lib/es_migration_apis'; +import { EnrichedDeprecationInfo } from '../../../../../../../server/np_ready/lib/es_migration_apis'; import { GroupByOption, LevelFilterOption } from '../../../types'; import { DeprecationCountSummary } from './count_summary'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/health.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/health.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/health.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/health.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/index.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/index.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/index.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/index.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/index_table.test.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/index_table.test.tsx similarity index 98% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/index_table.test.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/index_table.test.tsx index 040459309f7285..8c211704c7aff7 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/index_table.test.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/index_table.test.tsx @@ -9,8 +9,6 @@ import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { IndexDeprecationTableProps, IndexDeprecationTableUI } from './index_table'; -jest.mock('ui/kfetch'); - describe('IndexDeprecationTable', () => { const defaultProps = { indices: [ diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/index_table.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/index_table.tsx similarity index 92% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/index_table.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/index_table.tsx index 5a1deb59c270ee..eba906edc05095 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/index_table.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/index_table.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { EuiBasicTable } from '@elastic/eui'; import { injectI18n } from '@kbn/i18n/react'; import { ReindexButton } from './reindex'; +import { AppContext } from '../../../../app_context'; const PAGE_SIZES = [10, 25, 50, 100, 250, 500, 1000]; @@ -143,7 +144,13 @@ export class IndexDeprecationTableUI extends React.Component< actions: [ { render(indexDep: IndexDeprecationDetails) { - return ; + return ( + + {({ XSRF, http }) => ( + + )} + + ); }, }, ], diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/list.test.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/list.test.tsx similarity index 96% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/list.test.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/list.test.tsx index 6be89f411f5804..78ded735934641 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/list.test.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/list.test.tsx @@ -7,12 +7,10 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { EnrichedDeprecationInfo } from '../../../../../server/lib/es_migration_apis'; +import { EnrichedDeprecationInfo } from '../../../../../../../server/np_ready/lib/es_migration_apis'; import { GroupByOption } from '../../../types'; import { DeprecationList } from './list'; -jest.mock('ui/kfetch'); - describe('DeprecationList', () => { describe('group by message', () => { const defaultProps = { diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/list.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/list.tsx similarity index 97% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/list.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/list.tsx index cb38b848b3bd7c..256a0580c7617d 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/list.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/list.tsx @@ -7,7 +7,7 @@ import React, { StatelessComponent } from 'react'; import { DeprecationInfo } from 'src/legacy/core_plugins/elasticsearch'; -import { EnrichedDeprecationInfo } from '../../../../../server/lib/es_migration_apis'; +import { EnrichedDeprecationInfo } from '../../../../../../../server/np_ready/lib/es_migration_apis'; import { GroupByOption } from '../../../types'; import { COLOR_MAP, LEVEL_MAP } from '../constants'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/_button.scss b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/_button.scss similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/_button.scss rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/_button.scss diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/_index.scss new file mode 100644 index 00000000000000..014edc96b05652 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/_index.scss @@ -0,0 +1,2 @@ +@import 'button'; +@import 'flyout/index'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/button.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/button.tsx similarity index 95% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/button.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/button.tsx index 6addd3dae642ab..2a28018a3ae81e 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/button.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/button.tsx @@ -10,14 +10,16 @@ import { Subscription } from 'rxjs'; import { EuiButton, EuiLoadingSpinner } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { kfetch } from 'ui/kfetch'; -import { ReindexStatus, UIReindexOption } from '../../../../../../common/types'; +import { HttpSetup } from 'src/core/public'; +import { ReindexStatus, UIReindexOption } from '../../../../../../../../common/types'; import { LoadingState } from '../../../../types'; import { ReindexFlyout } from './flyout'; import { ReindexPollingService, ReindexState } from './polling_service'; interface ReindexButtonProps { indexName: string; + xsrf: string; + http: HttpSetup; } interface ReindexButtonState { @@ -152,7 +154,8 @@ export class ReindexButton extends React.Component { diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx similarity index 99% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx index 1465812c4803d8..b6e577d913cacf 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/flyout/warnings_step.tsx @@ -21,7 +21,7 @@ import { EuiText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ReindexWarning } from '../../../../../../../common/types'; +import { ReindexWarning } from '../../../../../../../../../common/types'; interface CheckedIds { [id: string]: boolean; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/index.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/index.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/index.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/index.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/polling_service.test.mocks.ts b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/polling_service.test.mocks.ts similarity index 87% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/polling_service.test.mocks.ts rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/polling_service.test.mocks.ts index f70822aef102cb..dc7a758839fe56 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/polling_service.test.mocks.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/polling_service.test.mocks.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ReindexStatus, ReindexStep } from '../../../../../../common/types'; +import { ReindexStatus, ReindexStep } from '../../../../../../../../common/types'; export const mockClient = { post: jest.fn().mockResolvedValue({ diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/polling_service.test.ts b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/polling_service.test.ts similarity index 75% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/polling_service.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/polling_service.test.ts index 9f47f51bc8cde6..cb2a0856f0f2e9 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/polling_service.test.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/polling_service.test.ts @@ -6,8 +6,9 @@ import { mockClient } from './polling_service.test.mocks'; -import { ReindexStatus, ReindexStep } from '../../../../../../common/types'; +import { ReindexStatus, ReindexStep } from '../../../../../../../../common/types'; import { ReindexPollingService } from './polling_service'; +import { httpServiceMock } from 'src/core/public/http/http_service.mock'; describe('ReindexPollingService', () => { beforeEach(() => { @@ -24,7 +25,11 @@ describe('ReindexPollingService', () => { }, }); - const service = new ReindexPollingService('myIndex'); + const service = new ReindexPollingService( + 'myIndex', + 'myXsrf', + httpServiceMock.createSetupContract() + ); service.updateStatus(); await new Promise(resolve => setTimeout(resolve, 1200)); // wait for poll interval @@ -45,7 +50,11 @@ describe('ReindexPollingService', () => { }, }); - const service = new ReindexPollingService('myIndex'); + const service = new ReindexPollingService( + 'myIndex', + 'myXsrf', + httpServiceMock.createSetupContract() + ); service.updateStatus(); await new Promise(resolve => setTimeout(resolve, 1200)); // wait for poll interval @@ -66,7 +75,11 @@ describe('ReindexPollingService', () => { }, }); - const service = new ReindexPollingService('myIndex'); + const service = new ReindexPollingService( + 'myIndex', + 'myXsrf', + httpServiceMock.createSetupContract() + ); service.updateStatus(); await new Promise(resolve => setTimeout(resolve, 1200)); // wait for poll interval @@ -76,7 +89,11 @@ describe('ReindexPollingService', () => { describe('startReindex', () => { it('posts to endpoint', async () => { - const service = new ReindexPollingService('myIndex'); + const service = new ReindexPollingService( + 'myIndex', + 'myXsrf', + httpServiceMock.createSetupContract() + ); await service.startReindex(); expect(mockClient.post).toHaveBeenCalledWith('/api/upgrade_assistant/reindex/myIndex'); @@ -85,7 +102,11 @@ describe('ReindexPollingService', () => { describe('cancelReindex', () => { it('posts to cancel endpoint', async () => { - const service = new ReindexPollingService('myIndex'); + const service = new ReindexPollingService( + 'myIndex', + 'myXsrf', + httpServiceMock.createSetupContract() + ); await service.cancelReindex(); expect(mockClient.post).toHaveBeenCalledWith('/api/upgrade_assistant/reindex/myIndex/cancel'); diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/polling_service.ts b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/polling_service.ts similarity index 82% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/polling_service.ts rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/polling_service.ts index 3977a169f4acab..879fafe610982b 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/deprecations/reindex/polling_service.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/deprecations/reindex/polling_service.ts @@ -3,31 +3,21 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import axios from 'axios'; -import chrome from 'ui/chrome'; +import axios, { AxiosInstance } from 'axios'; import { BehaviorSubject } from 'rxjs'; + +import { HttpSetup } from 'src/core/public'; import { IndexGroup, ReindexOperation, ReindexStatus, ReindexStep, ReindexWarning, -} from '../../../../../../common/types'; +} from '../../../../../../../../common/types'; import { LoadingState } from '../../../../types'; const POLL_INTERVAL = 1000; -const XSRF = chrome.getXsrfToken(); - -export const APIClient = axios.create({ - headers: { - Accept: 'application/json', - credentials: 'same-origin', - 'Content-Type': 'application/json', - 'kbn-version': XSRF, - 'kbn-xsrf': XSRF, - }, -}); export interface ReindexState { loadingState: LoadingState; @@ -55,13 +45,24 @@ interface StatusResponse { export class ReindexPollingService { public status$: BehaviorSubject; private pollTimeout?: NodeJS.Timeout; + private APIClient: AxiosInstance; - constructor(private indexName: string) { + constructor(private indexName: string, private xsrf: string, private http: HttpSetup) { this.status$ = new BehaviorSubject({ loadingState: LoadingState.Loading, errorMessage: null, reindexTaskPercComplete: null, }); + + this.APIClient = axios.create({ + headers: { + Accept: 'application/json', + credentials: 'same-origin', + 'Content-Type': 'application/json', + 'kbn-version': this.xsrf, + 'kbn-xsrf': this.xsrf, + }, + }); } public updateStatus = async () => { @@ -69,8 +70,8 @@ export class ReindexPollingService { this.stopPolling(); try { - const { data } = await APIClient.get( - chrome.addBasePath(`/api/upgrade_assistant/reindex/${this.indexName}`) + const { data } = await this.APIClient.get( + this.http.basePath.prepend(`/api/upgrade_assistant/reindex/${this.indexName}`) ); this.updateWithResponse(data); @@ -106,8 +107,8 @@ export class ReindexPollingService { errorMessage: null, cancelLoadingState: undefined, }); - const { data } = await APIClient.post( - chrome.addBasePath(`/api/upgrade_assistant/reindex/${this.indexName}`) + const { data } = await this.APIClient.post( + this.http.basePath.prepend(`/api/upgrade_assistant/reindex/${this.indexName}`) ); this.updateWithResponse({ reindexOp: data }); @@ -124,8 +125,8 @@ export class ReindexPollingService { cancelLoadingState: LoadingState.Loading, }); - await APIClient.post( - chrome.addBasePath(`/api/upgrade_assistant/reindex/${this.indexName}/cancel`) + await this.APIClient.post( + this.http.basePath.prepend(`/api/upgrade_assistant/reindex/${this.indexName}/cancel`) ); } catch (e) { this.status$.next({ diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/filter_bar.test.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/filter_bar.test.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/filter_bar.test.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/filter_bar.test.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/filter_bar.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/filter_bar.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/filter_bar.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/filter_bar.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/group_by_bar.test.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/group_by_bar.test.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/group_by_bar.test.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/group_by_bar.test.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/group_by_bar.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/group_by_bar.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/group_by_bar.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/group_by_bar.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/index.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/index.tsx similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/checkup/index.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/index.tsx diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/_index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/_index.scss new file mode 100644 index 00000000000000..c64a8f5a5326d0 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/_index.scss @@ -0,0 +1 @@ +@import 'steps'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/_steps.scss b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/_steps.scss similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/_steps.scss rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/_steps.scss diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/deprecation_logging_toggle.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/deprecation_logging_toggle.tsx similarity index 87% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/deprecation_logging_toggle.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/deprecation_logging_toggle.tsx index 8d107331eb65f0..97eb284c7b771f 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/deprecation_logging_toggle.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/deprecation_logging_toggle.tsx @@ -10,19 +10,25 @@ import React from 'react'; import { EuiLoadingSpinner, EuiSwitch } from '@elastic/eui'; import { injectI18n } from '@kbn/i18n/react'; -import chrome from 'ui/chrome'; +import { HttpSetup } from 'src/core/public'; + import { LoadingState } from '../../types'; +interface DeprecationLoggingTabProps extends ReactIntl.InjectedIntlProps { + xsrf: string; + http: HttpSetup; +} + interface DeprecationLoggingTabState { loadingState: LoadingState; loggingEnabled?: boolean; } export class DeprecationLoggingToggleUI extends React.Component< - ReactIntl.InjectedIntlProps, + DeprecationLoggingTabProps, DeprecationLoggingTabState > { - constructor(props: ReactIntl.InjectedIntlProps) { + constructor(props: DeprecationLoggingTabProps) { super(props); this.state = { @@ -83,7 +89,7 @@ export class DeprecationLoggingToggleUI extends React.Component< try { this.setState({ loadingState: LoadingState.Loading }); const resp = await axios.get( - chrome.addBasePath('/api/upgrade_assistant/deprecation_logging') + this.props.http.basePath.prepend('/api/upgrade_assistant/deprecation_logging') ); this.setState({ loadingState: LoadingState.Success, @@ -96,18 +102,19 @@ export class DeprecationLoggingToggleUI extends React.Component< private toggleLogging = async () => { try { + const { http, xsrf } = this.props; // Optimistically toggle the UI const newEnabled = !this.state.loggingEnabled; this.setState({ loadingState: LoadingState.Loading, loggingEnabled: newEnabled }); const resp = await axios.put( - chrome.addBasePath('/api/upgrade_assistant/deprecation_logging'), + http.basePath.prepend('/api/upgrade_assistant/deprecation_logging'), { isEnabled: newEnabled, }, { headers: { - 'kbn-xsrf': chrome.getXsrfToken(), + 'kbn-xsrf': xsrf, }, } ); diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/index.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/index.tsx similarity index 96% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/index.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/index.tsx index 834f5f4afe5f66..51968a9b864aef 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/index.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/index.tsx @@ -19,7 +19,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { NEXT_MAJOR_VERSION } from '../../../../common/version'; +import { NEXT_MAJOR_VERSION } from '../../../../../../common/version'; import { LoadingErrorBanner } from '../../error_banner'; import { LoadingState, UpgradeAssistantTabProps } from '../../types'; import { Steps } from './steps'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/steps.tsx b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/steps.tsx similarity index 96% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/steps.tsx rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/steps.tsx index d43a86d2b0e061..d1e88ba8b4b0ce 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/tabs/overview/steps.tsx +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/overview/steps.tsx @@ -18,11 +18,11 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; -import chrome from 'ui/chrome'; -import { CURRENT_MAJOR_VERSION, NEXT_MAJOR_VERSION } from '../../../../common/version'; +import { CURRENT_MAJOR_VERSION, NEXT_MAJOR_VERSION } from '../../../../../../common/version'; import { UpgradeAssistantTabProps } from '../../types'; import { DeprecationLoggingToggle } from './deprecation_logging_toggle'; +import { useAppContext } from '../../../app_context'; // Leaving these here even if unused so they are picked up for i18n static analysis // Keep this until last minor release (when next major is also released). @@ -54,7 +54,7 @@ const WAIT_FOR_RELEASE_STEP = { // Swap in this step for the one above it on the last minor release. // @ts-ignore -const START_UPGRADE_STEP = { +const START_UPGRADE_STEP = (isCloudEnabled: boolean) => ({ title: i18n.translate('xpack.upgradeAssistant.overviewTab.steps.startUpgradeStep.stepTitle', { defaultMessage: 'Start your upgrade', }), @@ -62,7 +62,7 @@ const START_UPGRADE_STEP = {

- {chrome.getInjected('isCloudEnabled', false) ? ( + {isCloudEnabled ? ( ), -}; +}); export const StepsUI: StatelessComponent = ({ checkupData, setSelectedTabIndex, intl }) => { @@ -100,6 +100,9 @@ export const StepsUI: StatelessComponent - + ), @@ -264,6 +267,7 @@ export const StepsUI: StatelessComponent ); diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/components/types.ts b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/types.ts similarity index 94% rename from x-pack/legacy/plugins/upgrade_assistant/public/components/types.ts rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/types.ts index dc31308a1ea340..2d9a373f20b7e9 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/components/types.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/types.ts @@ -9,7 +9,7 @@ import React from 'react'; import { EnrichedDeprecationInfo, UpgradeAssistantStatus, -} from '../../server/lib/es_migration_apis'; +} from '../../../../server/np_ready/lib/es_migration_apis'; export interface UpgradeAssistantTabProps { alertBanner?: React.ReactNode; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/index.scss b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/index.scss similarity index 66% rename from x-pack/legacy/plugins/upgrade_assistant/public/index.scss rename to x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/index.scss index 999cca93fcd7a0..6000af5498cd6e 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/index.scss +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/index.scss @@ -1,3 +1,3 @@ @import 'src/legacy/ui/public/styles/_styling_constants'; -@import './components/index'; +@import 'components/index'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/index.ts b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/index.ts new file mode 100644 index 00000000000000..020d6972f8280c --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { UpgradeAssistantUIPlugin } from './plugin'; + +export const plugin = () => { + return new UpgradeAssistantUIPlugin(); +}; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/plugin.ts b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/plugin.ts new file mode 100644 index 00000000000000..16a0c9632fb25c --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/plugin.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Plugin, CoreSetup } from 'src/core/public'; +import { RootComponent } from './application/app'; +import { LegacyPlugins } from '../legacy'; + +export class UpgradeAssistantUIPlugin implements Plugin { + async setup( + { http }: CoreSetup, + { management, __LEGACY: { XSRF, isCloudEnabled } }: LegacyPlugins + ) { + const appRegistrar = management.sections.get('kibana'); + return appRegistrar.registerApp({ + mount(__, { __LEGACY: { renderToElement } }) { + return renderToElement(() => RootComponent({ http, XSRF, isCloudEnabled })); + }, + }); + } + async start() {} + async stop() {} +} diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/index.ts b/x-pack/legacy/plugins/upgrade_assistant/server/index.ts index 5fcdbe136a4f2a..8b0704283509d2 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/index.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/index.ts @@ -4,31 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Legacy } from 'kibana'; -import { UpgradeAssistantTelemetryServer } from '../common/types'; -import { credentialStoreFactory } from './lib/reindexing/credential_store'; -import { makeUpgradeAssistantUsageCollector } from './lib/telemetry'; -import { registerClusterCheckupRoutes } from './routes/cluster_checkup'; -import { registerDeprecationLoggingRoutes } from './routes/deprecation_logging'; -import { registerReindexIndicesRoutes, registerReindexWorker } from './routes/reindex_indices'; -import { registerTelemetryRoutes } from './routes/telemetry'; - -export function initServer(server: Legacy.Server) { - registerClusterCheckupRoutes(server); - registerDeprecationLoggingRoutes(server); - - // The ReindexWorker uses a map of request headers that contain the authentication credentials - // for a given reindex. We cannot currently store these in an the .kibana index b/c we do not - // want to expose these credentials to any unauthenticated users. We also want to avoid any need - // to add a user for a special index just for upgrading. This in-memory cache allows us to - // process jobs without the browser staying on the page, but will require that jobs go into - // a paused state if no Kibana nodes have the required credentials. - const credentialStore = credentialStoreFactory(); - - const worker = registerReindexWorker(server, credentialStore); - registerReindexIndicesRoutes(server, worker, credentialStore); - - // Bootstrap the needed routes and the collector for the telemetry - registerTelemetryRoutes(server as UpgradeAssistantTelemetryServer); - makeUpgradeAssistantUsageCollector(server as UpgradeAssistantTelemetryServer); -} +export { plugin } from './np_ready'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts deleted file mode 100644 index 9a0fca6d4139c2..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SemVer } from 'semver'; -import { CURRENT_VERSION } from '../../common/version'; -import { - EsVersionPrecheck, - getAllNodeVersions, - verifyAllMatchKibanaVersion, -} from './es_version_precheck'; - -describe('getAllNodeVersions', () => { - it('returns a list of unique node versions', async () => { - const callCluster = jest.fn().mockResolvedValue({ - nodes: { - node1: { version: '7.0.0' }, - node2: { version: '7.0.0' }, - node3: { version: '6.0.0' }, - }, - }); - - await expect(getAllNodeVersions(callCluster)).resolves.toEqual([ - new SemVer('6.0.0'), - new SemVer('7.0.0'), - ]); - }); -}); - -describe('verifyAllMatchKibanaVersion', () => { - it('throws if any are higher version', () => { - expect(() => - verifyAllMatchKibanaVersion([new SemVer('99999.0.0')]) - ).toThrowErrorMatchingInlineSnapshot( - `"There are some nodes running a different version of Elasticsearch"` - ); - }); - - it('throws if any are lower version', () => { - expect(() => - verifyAllMatchKibanaVersion([new SemVer('0.0.0')]) - ).toThrowErrorMatchingInlineSnapshot( - `"There are some nodes running a different version of Elasticsearch"` - ); - }); - - it('does not throw if all are same major', () => { - const versions = [ - CURRENT_VERSION, - CURRENT_VERSION.inc('minor'), - CURRENT_VERSION.inc('minor').inc('minor'), - ]; - - expect(() => verifyAllMatchKibanaVersion(versions)).not.toThrow(); - }); -}); - -describe('EsVersionPrecheck', () => { - it('throws a 403 when callCluster fails with a 403', async () => { - const fakeCallWithRequest = jest.fn().mockRejectedValue({ status: 403 }); - const fakeGetCluster = jest.fn(() => ({ callWithRequest: fakeCallWithRequest })); - const fakeRequest = { - server: { plugins: { elasticsearch: { getCluster: fakeGetCluster } } }, - } as any; - - await expect(EsVersionPrecheck.method(fakeRequest, {} as any)).rejects.toHaveProperty( - 'output.statusCode', - 403 - ); - }); - - it('throws a 426 message w/ allNodesUpgraded = false when nodes are not on same version', async () => { - const fakeCallWithRequest = jest.fn().mockResolvedValue({ - nodes: { - node1: { version: CURRENT_VERSION.raw }, - node2: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, - }, - }); - const fakeGetCluster = jest.fn(() => ({ callWithRequest: fakeCallWithRequest })); - const fakeRequest = { - server: { plugins: { elasticsearch: { getCluster: fakeGetCluster } } }, - } as any; - - const result = EsVersionPrecheck.method(fakeRequest, {} as any); - await expect(result).rejects.toHaveProperty('output.statusCode', 426); - await expect(result).rejects.toHaveProperty( - 'output.payload.attributes.allNodesUpgraded', - false - ); - }); - - it('throws a 426 message w/ allNodesUpgraded = true when nodes are on next version', async () => { - const fakeCallWithRequest = jest.fn().mockResolvedValue({ - nodes: { - node1: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, - node2: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, - }, - }); - const fakeGetCluster = jest.fn(() => ({ callWithRequest: fakeCallWithRequest })); - const fakeRequest = { - server: { plugins: { elasticsearch: { getCluster: fakeGetCluster } } }, - } as any; - - const result = EsVersionPrecheck.method(fakeRequest, {} as any); - await expect(result).rejects.toHaveProperty('output.statusCode', 426); - await expect(result).rejects.toHaveProperty('output.payload.attributes.allNodesUpgraded', true); - }); - - it('returns true when nodes are on same version', async () => { - const fakeCallWithRequest = jest.fn().mockResolvedValue({ - nodes: { - node1: { version: CURRENT_VERSION.raw }, - node2: { version: CURRENT_VERSION.raw }, - }, - }); - const fakeGetCluster = jest.fn(() => ({ callWithRequest: fakeCallWithRequest })); - const fakeRequest = { - server: { plugins: { elasticsearch: { getCluster: fakeGetCluster } } }, - } as any; - - await expect(EsVersionPrecheck.method(fakeRequest, {} as any)).resolves.toBe(true); - }); -}); diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_version_precheck.ts b/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_version_precheck.ts deleted file mode 100644 index d84d5f54444723..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_version_precheck.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; -import { Request, RouteOptionsPreObject } from 'hapi'; -import { uniq } from 'lodash'; -import { SemVer } from 'semver'; - -import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; -import { CURRENT_VERSION } from '../../common/version'; - -/** - * Returns an array of all the unique Elasticsearch Node Versions in the Elasticsearch cluster. - * @param request - */ -export const getAllNodeVersions = async (callCluster: CallCluster) => { - // Get the version information for all nodes in the cluster. - const { nodes } = (await callCluster('nodes.info', { - filterPath: 'nodes.*.version', - })) as { nodes: { [nodeId: string]: { version: string } } }; - - const versionStrings = Object.values(nodes).map(({ version }) => version); - - return uniq(versionStrings) - .sort() - .map(version => new SemVer(version)); -}; - -export const verifyAllMatchKibanaVersion = (allNodeVersions: SemVer[]) => { - // Determine if all nodes in the cluster are running the same major version as Kibana. - const numDifferentVersion = allNodeVersions.filter( - esNodeVersion => esNodeVersion.major !== CURRENT_VERSION.major - ).length; - const numSameVersion = allNodeVersions.filter( - esNodeVersion => esNodeVersion.major === CURRENT_VERSION.major - ).length; - - if (numDifferentVersion) { - const error = new Boom(`There are some nodes running a different version of Elasticsearch`, { - // 426 means "Upgrade Required" and is used when semver compatibility is not met. - statusCode: 426, - }); - - error.output.payload.attributes = { allNodesUpgraded: !numSameVersion }; - throw error; - } -}; - -export const EsVersionPrecheck = { - assign: 'esVersionCheck', - async method(request: Request) { - const { callWithRequest } = request.server.plugins.elasticsearch.getCluster('admin'); - const callCluster = callWithRequest.bind(callWithRequest, request) as CallCluster; - - let allNodeVersions: SemVer[]; - - try { - allNodeVersions = await getAllNodeVersions(callCluster); - } catch (e) { - if (e.status === 403) { - throw Boom.forbidden(e.message); - } - - throw e; - } - - // This will throw if there is an issue - verifyAllMatchKibanaVersion(allNodeVersions); - - return true; - }, -} as RouteOptionsPreObject; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/index.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/index.ts new file mode 100644 index 00000000000000..cf1b78e1e3920f --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { PluginInitializerContext } from 'src/core/server'; +import { UpgradeAssistantServerPlugin } from './plugin'; + +export const plugin = (ctx: PluginInitializerContext) => { + return new UpgradeAssistantServerPlugin(); +}; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/__fixtures__/fake_deprecations.json b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/__fixtures__/fake_deprecations.json similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/__fixtures__/fake_deprecations.json rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/__fixtures__/fake_deprecations.json diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/__mocks__/es_version_precheck.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/__mocks__/es_version_precheck.ts similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/__mocks__/es_version_precheck.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/__mocks__/es_version_precheck.ts diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/__snapshots__/es_migration_apis.test.ts.snap b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/__snapshots__/es_migration_apis.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/__snapshots__/es_migration_apis.test.ts.snap rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/__snapshots__/es_migration_apis.test.ts.snap diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_deprecation_logging_apis.test.ts similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_deprecation_logging_apis.test.ts diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_deprecation_logging_apis.ts similarity index 94% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_deprecation_logging_apis.ts index 2cf0a8f5020fdf..199d3894084427 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_deprecation_logging_apis.ts @@ -5,8 +5,8 @@ */ import { get } from 'lodash'; -import { Legacy } from 'kibana'; import { CallClusterWithRequest } from 'src/legacy/core_plugins/elasticsearch'; +import { RequestShim } from '../types'; interface DeprecationLoggingStatus { isEnabled: boolean; @@ -14,7 +14,7 @@ interface DeprecationLoggingStatus { export async function getDeprecationLoggingStatus( callWithRequest: CallClusterWithRequest, - req: Legacy.Request + req: RequestShim ): Promise { const response = await callWithRequest(req, 'cluster.getSettings', { includeDefaults: true, @@ -27,7 +27,7 @@ export async function getDeprecationLoggingStatus( export async function setDeprecationLogging( callWithRequest: CallClusterWithRequest, - req: Legacy.Request, + req: RequestShim, isEnabled: boolean ): Promise { const response = await callWithRequest(req, 'cluster.putSettings', { diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_migration_apis.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_migration_apis.test.ts similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/es_migration_apis.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_migration_apis.test.ts diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_migration_apis.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_migration_apis.ts similarity index 96% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/es_migration_apis.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_migration_apis.ts index 8ed85d19c411a0..b52c4c374266f7 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/es_migration_apis.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_migration_apis.ts @@ -4,15 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; - -import { Request } from 'hapi'; import { CallClusterWithRequest, DeprecationAPIResponse, DeprecationInfo, } from 'src/legacy/core_plugins/elasticsearch'; +import { RequestShim } from '../types'; + export interface EnrichedDeprecationInfo extends DeprecationInfo { index?: string; node?: string; @@ -27,7 +26,7 @@ export interface UpgradeAssistantStatus { export async function getUpgradeAssistantStatus( callWithRequest: CallClusterWithRequest, - req: Request, + req: RequestShim, isCloudEnabled: boolean ): Promise { const deprecations = await callWithRequest(req, 'transport.request', { diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_version_precheck.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_version_precheck.test.ts new file mode 100644 index 00000000000000..bbabe557df4d4a --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_version_precheck.test.ts @@ -0,0 +1,151 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SemVer } from 'semver'; +import { IScopedClusterClient, kibanaResponseFactory } from 'src/core/server'; +import { CURRENT_VERSION } from '../../../common/version'; +import { + esVersionCheck, + getAllNodeVersions, + verifyAllMatchKibanaVersion, +} from './es_version_precheck'; + +describe('getAllNodeVersions', () => { + it('returns a list of unique node versions', async () => { + const adminClient = ({ + callAsInternalUser: jest.fn().mockResolvedValue({ + nodes: { + node1: { version: '7.0.0' }, + node2: { version: '7.0.0' }, + node3: { version: '6.0.0' }, + }, + }), + } as unknown) as IScopedClusterClient; + + await expect(getAllNodeVersions(adminClient)).resolves.toEqual([ + new SemVer('6.0.0'), + new SemVer('7.0.0'), + ]); + }); +}); + +describe('verifyAllMatchKibanaVersion', () => { + it('detects higher version nodes', () => { + const result = verifyAllMatchKibanaVersion([new SemVer('99999.0.0')]); + expect(result.allNodesMatch).toBe(false); + expect(result.allNodesUpgraded).toBe(true); + }); + + it('detects lower version nodes', () => { + const result = verifyAllMatchKibanaVersion([new SemVer('0.0.0')]); + expect(result.allNodesMatch).toBe(false); + expect(result.allNodesUpgraded).toBe(true); + }); + + it('detects if all are on same major correctly', () => { + const versions = [ + CURRENT_VERSION, + CURRENT_VERSION.inc('minor'), + CURRENT_VERSION.inc('minor').inc('minor'), + ]; + + const result = verifyAllMatchKibanaVersion(versions); + expect(result.allNodesMatch).toBe(true); + expect(result.allNodesUpgraded).toBe(false); + }); + + it('detects partial matches', () => { + const versions = [ + new SemVer('0.0.0'), + CURRENT_VERSION.inc('minor'), + CURRENT_VERSION.inc('minor').inc('minor'), + ]; + + const result = verifyAllMatchKibanaVersion(versions); + expect(result.allNodesMatch).toBe(false); + expect(result.allNodesUpgraded).toBe(false); + }); +}); + +describe('EsVersionPrecheck', () => { + it('returns a 403 when callCluster fails with a 403', async () => { + const fakeCall = jest.fn().mockRejectedValue({ status: 403 }); + + const ctx = { + core: { + elasticsearch: { + adminClient: { + callAsInternalUser: fakeCall, + }, + }, + }, + } as any; + + const result = await esVersionCheck(ctx, kibanaResponseFactory); + expect(result).toHaveProperty('status', 403); + }); + + it('returns a 426 message w/ allNodesUpgraded = false when nodes are not on same version', async () => { + const ctx = { + core: { + elasticsearch: { + adminClient: { + callAsInternalUser: jest.fn().mockResolvedValue({ + nodes: { + node1: { version: CURRENT_VERSION.raw }, + node2: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, + }, + }), + }, + }, + }, + } as any; + + const result = await esVersionCheck(ctx, kibanaResponseFactory); + expect(result).toHaveProperty('status', 426); + expect(result).toHaveProperty('payload.attributes.allNodesUpgraded', false); + }); + + it('returns a 426 message w/ allNodesUpgraded = true when nodes are on next version', async () => { + const ctx = { + core: { + elasticsearch: { + adminClient: { + callAsInternalUser: jest.fn().mockResolvedValue({ + nodes: { + node1: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, + node2: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, + }, + }), + }, + }, + }, + } as any; + + const result = await esVersionCheck(ctx, kibanaResponseFactory); + expect(result).toHaveProperty('status', 426); + expect(result).toHaveProperty('payload.attributes.allNodesUpgraded', true); + }); + + it('returns undefined when nodes are on same version', async () => { + const ctx = { + core: { + elasticsearch: { + adminClient: { + callAsInternalUser: jest.fn().mockResolvedValue({ + nodes: { + node1: { version: CURRENT_VERSION.raw }, + node2: { version: CURRENT_VERSION.raw }, + }, + }), + }, + }, + }, + } as any; + + await expect(esVersionCheck(ctx, kibanaResponseFactory)).resolves.toBe(undefined); + }); +}); diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_version_precheck.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_version_precheck.ts new file mode 100644 index 00000000000000..2fb3effe437938 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/es_version_precheck.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { uniq } from 'lodash'; +import { SemVer } from 'semver'; +import { + IScopedClusterClient, + KibanaRequest, + KibanaResponseFactory, + RequestHandler, + RequestHandlerContext, +} from 'src/core/server'; +import { CURRENT_VERSION } from '../../../common/version'; + +/** + * Returns an array of all the unique Elasticsearch Node Versions in the Elasticsearch cluster. + */ +export const getAllNodeVersions = async (adminClient: IScopedClusterClient) => { + // Get the version information for all nodes in the cluster. + const { nodes } = (await adminClient.callAsInternalUser('nodes.info', { + filterPath: 'nodes.*.version', + })) as { nodes: { [nodeId: string]: { version: string } } }; + + const versionStrings = Object.values(nodes).map(({ version }) => version); + + return uniq(versionStrings) + .sort() + .map(version => new SemVer(version)); +}; + +export const verifyAllMatchKibanaVersion = (allNodeVersions: SemVer[]) => { + // Determine if all nodes in the cluster are running the same major version as Kibana. + const numDifferentVersion = allNodeVersions.filter( + esNodeVersion => esNodeVersion.major !== CURRENT_VERSION.major + ).length; + + const numSameVersion = allNodeVersions.filter( + esNodeVersion => esNodeVersion.major === CURRENT_VERSION.major + ).length; + + if (numDifferentVersion) { + return { + allNodesMatch: false, + // If Kibana is talking to nodes and none have the same major version as Kibana, they must a be of + // a higher major version. + allNodesUpgraded: numSameVersion === 0, + }; + } + return { + allNodesMatch: true, + allNodesUpgraded: false, + }; +}; + +/** + * This is intended as controller/handler level code so it knows about HTTP + */ +export const esVersionCheck = async ( + ctx: RequestHandlerContext, + response: KibanaResponseFactory +) => { + const { adminClient } = ctx.core.elasticsearch; + let allNodeVersions: SemVer[]; + + try { + allNodeVersions = await getAllNodeVersions(adminClient); + } catch (e) { + if (e.status === 403) { + return response.forbidden({ body: e.message }); + } + + throw e; + } + + const result = verifyAllMatchKibanaVersion(allNodeVersions); + if (!result.allNodesMatch) { + return response.customError({ + // 426 means "Upgrade Required" and is used when semver compatibility is not met. + statusCode: 426, + body: { + message: 'There are some nodes running a different version of Elasticsearch', + attributes: { + allNodesUpgraded: result.allNodesUpgraded, + }, + }, + }); + } +}; + +export const versionCheckHandlerWrapper = (handler: RequestHandler) => async ( + ctx: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory +) => { + const errorResponse = await esVersionCheck(ctx, response); + if (errorResponse) { + return errorResponse; + } + return handler(ctx, request, response); +}; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/credential_store.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/credential_store.test.ts similarity index 95% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/credential_store.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/credential_store.test.ts index ce892df0de9469..06fa755472238f 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/credential_store.test.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/credential_store.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ReindexSavedObject } from '../../../common/types'; +import { ReindexSavedObject } from '../../../../common/types'; import { Credential, credentialStoreFactory } from './credential_store'; describe('credentialStore', () => { diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/credential_store.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/credential_store.ts similarity index 96% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/credential_store.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/credential_store.ts index 32f5ec9977b72e..a051d16b5779f8 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/credential_store.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/credential_store.ts @@ -8,7 +8,7 @@ import { createHash } from 'crypto'; import { Request } from 'hapi'; import stringify from 'json-stable-stringify'; -import { ReindexSavedObject } from '../../../common/types'; +import { ReindexSavedObject } from '../../../../common/types'; export type Credential = Request['headers']; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/index.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/index.ts similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/index.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/index.ts diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/index_settings.test.ts similarity index 99% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/index_settings.test.ts index 9ec06b72f02e2c..7b346cc87edf6d 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/index_settings.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CURRENT_MAJOR_VERSION, PREV_MAJOR_VERSION } from '../../../common/version'; +import { CURRENT_MAJOR_VERSION, PREV_MAJOR_VERSION } from '../../../../common/version'; import { generateNewIndexName, getReindexWarnings, diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/index_settings.ts similarity index 97% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/index_settings.ts index f6dc471d0945d3..0b95bc628fbb41 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/index_settings.ts @@ -5,8 +5,8 @@ */ import { flow, omit } from 'lodash'; -import { ReindexWarning } from '../../../common/types'; -import { CURRENT_MAJOR_VERSION, PREV_MAJOR_VERSION } from '../../../common/version'; +import { ReindexWarning } from '../../../../common/types'; +import { CURRENT_MAJOR_VERSION, PREV_MAJOR_VERSION } from '../../../../common/version'; import { FlatSettings } from './types'; export interface ParsedIndexName { diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_actions.test.ts similarity index 99% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_actions.test.ts index 4569fdfa33a83b..3fb855958a5d08 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_actions.test.ts @@ -13,8 +13,8 @@ import { ReindexSavedObject, ReindexStatus, ReindexStep, -} from '../../../common/types'; -import { CURRENT_MAJOR_VERSION, PREV_MAJOR_VERSION } from '../../../common/version'; +} from '../../../../common/types'; +import { CURRENT_MAJOR_VERSION, PREV_MAJOR_VERSION } from '../../../../common/version'; import { LOCK_WINDOW, ReindexActions, reindexActionsFactory } from './reindex_actions'; describe('ReindexActions', () => { diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_actions.ts similarity index 99% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_actions.ts index a162186ff0059c..6683f80c8e7791 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_actions.ts @@ -7,7 +7,7 @@ import moment from 'moment'; import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; -import { SavedObjectsFindResponse, SavedObjectsClientContract } from 'src/core/server'; +import { SavedObjectsFindResponse, SavedObjectsClientContract } from 'kibana/server'; import { IndexGroup, REINDEX_OP_TYPE, @@ -15,7 +15,7 @@ import { ReindexSavedObject, ReindexStatus, ReindexStep, -} from '../../../common/types'; +} from '../../../../common/types'; import { generateNewIndexName } from './index_settings'; import { FlatSettings } from './types'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_service.test.ts similarity index 99% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_service.test.ts index 1216a8d2c4c24e..9cd41c8cbe8264 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_service.test.ts @@ -10,8 +10,8 @@ import { ReindexSavedObject, ReindexStatus, ReindexStep, -} from '../../../common/types'; -import { CURRENT_MAJOR_VERSION, PREV_MAJOR_VERSION } from '../../../common/version'; +} from '../../../../common/types'; +import { CURRENT_MAJOR_VERSION, PREV_MAJOR_VERSION } from '../../../../common/version'; import { isMlIndex, isWatcherIndex, diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_service.ts similarity index 99% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_service.ts index 41a4552b722de6..0e6095f98b6ff0 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/reindex_service.ts @@ -8,14 +8,14 @@ import Boom from 'boom'; import { Server } from 'hapi'; import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; -import { XPackInfo } from '../../../../xpack_main/server/lib/xpack_info'; +import { XPackInfo } from '../../../../../xpack_main/server/lib/xpack_info'; import { IndexGroup, ReindexSavedObject, ReindexStatus, ReindexStep, ReindexWarning, -} from '../../../common/types'; +} from '../../../../common/types'; import { generateNewIndexName, getReindexWarnings, diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/types.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/types.ts similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/types.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/types.ts diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/worker.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/worker.ts similarity index 97% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/worker.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/worker.ts index 669eea623851ce..628a47be9f5e77 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/reindexing/worker.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/reindexing/worker.ts @@ -5,11 +5,11 @@ */ import { CallCluster, CallClusterWithRequest } from 'src/legacy/core_plugins/elasticsearch'; import { Request, Server } from 'src/legacy/server/kbn_server'; -import { SavedObjectsClientContract } from 'src/core/server'; +import { SavedObjectsClientContract } from 'kibana/server'; import moment from 'moment'; -import { XPackInfo } from '../../../../xpack_main/server/lib/xpack_info'; -import { ReindexSavedObject, ReindexStatus } from '../../../common/types'; +import { XPackInfo } from '../../../../../xpack_main/server/lib/xpack_info'; +import { ReindexSavedObject, ReindexStatus } from '../../../../common/types'; import { CredentialStore } from './credential_store'; import { reindexActionsFactory } from './reindex_actions'; import { ReindexService, reindexServiceFactory } from './reindex_service'; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_open_apis.test.ts similarity index 98% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_open_apis.test.ts index 94df444e2c1bbe..4c378ba25430e4 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.test.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_open_apis.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE } from '../../../common/types'; +import { UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE } from '../../../../common/types'; import { upsertUIOpenOption } from './es_ui_open_apis'; /** diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_open_apis.ts similarity index 75% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_open_apis.ts index 7036dcc4ea1a71..b52b3b812b7f9b 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_open_apis.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_open_apis.ts @@ -4,19 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Legacy } from 'kibana'; import { UIOpen, UIOpenOption, UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE, - UpgradeAssistantTelemetryServer, -} from '../../../common/types'; +} from '../../../../common/types'; +import { RequestShim, ServerShim } from '../../types'; -async function incrementUIOpenOptionCounter( - server: UpgradeAssistantTelemetryServer, - uiOpenOptionCounter: UIOpenOption -) { +async function incrementUIOpenOptionCounter(server: ServerShim, uiOpenOptionCounter: UIOpenOption) { const { getSavedObjectsRepository } = server.savedObjects; const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin'); const internalRepository = getSavedObjectsRepository(callWithInternalUser); @@ -28,10 +24,7 @@ async function incrementUIOpenOptionCounter( ); } -export async function upsertUIOpenOption( - server: UpgradeAssistantTelemetryServer, - req: Legacy.Request -): Promise { +export async function upsertUIOpenOption(server: ServerShim, req: RequestShim): Promise { const { overview, cluster, indices } = req.payload as UIOpen; if (overview) { diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_reindex_apis.test.ts similarity index 98% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_reindex_apis.test.ts index a8a78470aabbe4..26302de74743f1 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.test.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_reindex_apis.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE } from '../../../common/types'; +import { UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE } from '../../../../common/types'; import { upsertUIReindexOption } from './es_ui_reindex_apis'; /** diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_reindex_apis.ts similarity index 86% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_reindex_apis.ts index 3cb965523a80b5..626d51b298e72a 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/es_ui_reindex_apis.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/es_ui_reindex_apis.ts @@ -4,17 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Legacy } from 'kibana'; import { UIReindex, UIReindexOption, UPGRADE_ASSISTANT_DOC_ID, UPGRADE_ASSISTANT_TYPE, - UpgradeAssistantTelemetryServer, -} from '../../../common/types'; +} from '../../../../common/types'; +import { RequestShim, ServerShim } from '../../types'; async function incrementUIReindexOptionCounter( - server: UpgradeAssistantTelemetryServer, + server: ServerShim, uiOpenOptionCounter: UIReindexOption ) { const { getSavedObjectsRepository } = server.savedObjects; @@ -29,8 +28,8 @@ async function incrementUIReindexOptionCounter( } export async function upsertUIReindexOption( - server: UpgradeAssistantTelemetryServer, - req: Legacy.Request + server: ServerShim, + req: RequestShim ): Promise { const { close, open, start, stop } = req.payload as UIReindex; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/index.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/index.ts similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/index.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/index.ts diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/usage_collector.test.ts similarity index 100% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/usage_collector.test.ts diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/usage_collector.ts similarity index 87% rename from x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/usage_collector.ts index 3a9b11a57f070c..47a2cd5d51fd4e 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/usage_collector.ts @@ -13,8 +13,8 @@ import { UpgradeAssistantTelemetry, UpgradeAssistantTelemetrySavedObject, UpgradeAssistantTelemetrySavedObjectAttributes, - UpgradeAssistantTelemetryServer, -} from '../../../common/types'; +} from '../../../../common/types'; +import { ServerShim } from '../../types'; import { isDeprecationLoggingEnabled } from '../es_deprecation_logging_apis'; async function getSavedObjectAttributesFromRepo( @@ -43,7 +43,7 @@ async function getDeprecationLoggingStatusValue(callCluster: any): Promise { const { getSavedObjectsRepository } = server.savedObjects; const savedObjectsRepository = getSavedObjectsRepository(callCluster); @@ -97,13 +97,12 @@ export async function fetchUpgradeAssistantMetrics( }; } -export function makeUpgradeAssistantUsageCollector(server: UpgradeAssistantTelemetryServer) { - const kbnServer = server as UpgradeAssistantTelemetryServer; - const upgradeAssistantUsageCollector = kbnServer.usage.collectorSet.makeUsageCollector({ +export function makeUpgradeAssistantUsageCollector(server: ServerShim) { + const upgradeAssistantUsageCollector = server.usage.collectorSet.makeUsageCollector({ type: UPGRADE_ASSISTANT_TYPE, isReady: () => true, fetch: async (callCluster: any) => fetchUpgradeAssistantMetrics(callCluster, server), }); - kbnServer.usage.collectorSet.register(upgradeAssistantUsageCollector); + server.usage.collectorSet.register(upgradeAssistantUsageCollector); } diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/plugin.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/plugin.ts new file mode 100644 index 00000000000000..7bc33142ca3213 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/plugin.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Plugin, CoreSetup, CoreStart } from 'src/core/server'; +import { ServerShim, ServerShimWithRouter } from './types'; +import { credentialStoreFactory } from './lib/reindexing/credential_store'; +import { makeUpgradeAssistantUsageCollector } from './lib/telemetry'; +import { registerClusterCheckupRoutes } from './routes/cluster_checkup'; +import { registerDeprecationLoggingRoutes } from './routes/deprecation_logging'; +import { registerReindexIndicesRoutes, registerReindexWorker } from './routes/reindex_indices'; + +import { registerTelemetryRoutes } from './routes/telemetry'; + +export class UpgradeAssistantServerPlugin implements Plugin { + setup({ http }: CoreSetup, { __LEGACY }: { __LEGACY: ServerShim }) { + const router = http.createRouter(); + const shimWithRouter: ServerShimWithRouter = { ...__LEGACY, router }; + registerClusterCheckupRoutes(shimWithRouter); + registerDeprecationLoggingRoutes(shimWithRouter); + + // The ReindexWorker uses a map of request headers that contain the authentication credentials + // for a given reindex. We cannot currently store these in an the .kibana index b/c we do not + // want to expose these credentials to any unauthenticated users. We also want to avoid any need + // to add a user for a special index just for upgrading. This in-memory cache allows us to + // process jobs without the browser staying on the page, but will require that jobs go into + // a paused state if no Kibana nodes have the required credentials. + const credentialStore = credentialStoreFactory(); + + const worker = registerReindexWorker(__LEGACY, credentialStore); + registerReindexIndicesRoutes(shimWithRouter, worker, credentialStore); + + // Bootstrap the needed routes and the collector for the telemetry + registerTelemetryRoutes(shimWithRouter); + makeUpgradeAssistantUsageCollector(__LEGACY); + } + + start(core: CoreStart, plugins: any) {} + + stop(): void {} +} diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/__mocks__/request.mock.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/__mocks__/request.mock.ts new file mode 100644 index 00000000000000..d09a66dbb43269 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/__mocks__/request.mock.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export const createRequestMock = (opts?: { + headers?: any; + params?: Record; + payload?: Record; +}) => { + return Object.assign({ headers: {} }, opts || {}); +}; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/__mocks__/routes.mock.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/__mocks__/routes.mock.ts new file mode 100644 index 00000000000000..3769bc389123e2 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/__mocks__/routes.mock.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { RequestHandler } from 'kibana/server'; + +/** + * Creates a very crude mock of the new platform router implementation. This enables use to test + * controller/handler logic without making HTTP requests to an actual server. This does not enable + * us to test whether our paths actual match, only the response codes of controllers given certain + * inputs. This should be replaced by a more wholistic solution (like functional tests) eventually. + * + * This also bypasses any validation installed on the route. + */ +export const createMockRouter = () => { + const paths: Record>> = {}; + + const assign = (method: string) => ( + { path }: { path: string }, + handler: RequestHandler + ) => { + paths[method] = { + ...(paths[method] || {}), + ...{ [path]: handler }, + }; + }; + + return { + getHandler({ method, pathPattern }: { method: string; pathPattern: string }) { + return paths[method][pathPattern]; + }, + get: assign('get'), + post: assign('post'), + put: assign('put'), + patch: assign('patch'), + }; +}; + +export type MockRouter = ReturnType; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/routes/cluster_checkup.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/cluster_checkup.test.ts similarity index 53% rename from x-pack/legacy/plugins/upgrade_assistant/server/routes/cluster_checkup.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/cluster_checkup.test.ts index 04a05dc2b0e9cb..6afb9d2a5e9357 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/routes/cluster_checkup.test.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/cluster_checkup.test.ts @@ -3,33 +3,20 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { kibanaResponseFactory } from 'src/core/server'; +import { createMockRouter, MockRouter } from './__mocks__/routes.mock'; +import { createRequestMock } from './__mocks__/request.mock'; -import Boom from 'boom'; -import { Server } from 'hapi'; - -jest.mock('../lib/es_version_precheck'); -import { EsVersionPrecheck } from '../lib/es_version_precheck'; -import { registerClusterCheckupRoutes } from './cluster_checkup'; +jest.mock('../lib/es_version_precheck', () => ({ + versionCheckHandlerWrapper: (a: any) => a, +})); // Need to require to get mock on named export to work. // eslint-disable-next-line @typescript-eslint/no-var-requires const MigrationApis = require('../lib/es_migration_apis'); MigrationApis.getUpgradeAssistantStatus = jest.fn(); -function register(plugins = {}) { - const server = new Server(); - server.plugins = { - elasticsearch: { - getCluster: () => ({ callWithRequest: jest.fn() } as any), - } as any, - ...plugins, - } as any; - server.config = () => ({ get: () => '' } as any); - - registerClusterCheckupRoutes(server); - - return server; -} +import { registerClusterCheckupRoutes } from './cluster_checkup'; /** * Since these route callbacks are so thin, these serve simply as integration tests @@ -37,50 +24,65 @@ function register(plugins = {}) { * more thoroughly in the es_migration_apis test. */ describe('cluster checkup API', () => { - const spy = jest.spyOn(MigrationApis, 'getUpgradeAssistantStatus'); - afterEach(() => jest.clearAllMocks()); - describe('with cloud enabled', () => { - it('is provided to getUpgradeAssistantStatus', async () => { - const server = register({ + let mockRouter: MockRouter; + let serverShim: any; + let ctxMock: any; + + beforeEach(() => { + mockRouter = createMockRouter(); + ctxMock = { + core: {}, + }; + serverShim = { + router: mockRouter, + plugins: { cloud: { config: { isCloudEnabled: true, }, }, - }); + elasticsearch: { + getCluster: () => ({ callWithRequest: jest.fn() } as any), + } as any, + }, + }; + registerClusterCheckupRoutes(serverShim); + }); + + describe('with cloud enabled', () => { + it('is provided to getUpgradeAssistantStatus', async () => { + const spy = jest.spyOn(MigrationApis, 'getUpgradeAssistantStatus'); MigrationApis.getUpgradeAssistantStatus.mockResolvedValue({ cluster: [], indices: [], nodes: [], }); - await server.inject({ - method: 'GET', - url: '/api/upgrade_assistant/status', - }); + await serverShim.router.getHandler({ + method: 'get', + pathPattern: '/api/upgrade_assistant/status', + })(ctxMock, createRequestMock(), kibanaResponseFactory); expect(spy.mock.calls[0][2]).toBe(true); }); }); describe('GET /api/upgrade_assistant/reindex/{indexName}.json', () => { - const server = register(); - it('returns state', async () => { MigrationApis.getUpgradeAssistantStatus.mockResolvedValue({ cluster: [], indices: [], nodes: [], }); - const resp = await server.inject({ - method: 'GET', - url: '/api/upgrade_assistant/status', - }); + const resp = await serverShim.router.getHandler({ + method: 'get', + pathPattern: '/api/upgrade_assistant/status', + })(ctxMock, createRequestMock(), kibanaResponseFactory); - expect(resp.statusCode).toEqual(200); - expect(resp.payload).toMatchInlineSnapshot( + expect(resp.status).toEqual(200); + expect(JSON.stringify(resp.payload)).toMatchInlineSnapshot( `"{\\"cluster\\":[],\\"indices\\":[],\\"nodes\\":[]}"` ); }); @@ -90,35 +92,23 @@ describe('cluster checkup API', () => { e.status = 403; MigrationApis.getUpgradeAssistantStatus.mockRejectedValue(e); - const resp = await server.inject({ - method: 'GET', - url: '/api/upgrade_assistant/status', - }); + const resp = await serverShim.router.getHandler({ + method: 'get', + pathPattern: '/api/upgrade_assistant/status', + })(ctxMock, createRequestMock(), kibanaResponseFactory); - expect(resp.statusCode).toEqual(403); + expect(resp.status).toEqual(403); }); it('returns an 500 error if it throws', async () => { MigrationApis.getUpgradeAssistantStatus.mockRejectedValue(new Error(`scary error!`)); - const resp = await server.inject({ - method: 'GET', - url: '/api/upgrade_assistant/status', - }); - expect(resp.statusCode).toEqual(500); - }); - - it('returns a 426 if EsVersionCheck throws', async () => { - (EsVersionPrecheck.method as jest.Mock).mockRejectedValue( - new Boom(`blah`, { statusCode: 426 }) - ); - - const resp = await server.inject({ - method: 'GET', - url: '/api/upgrade_assistant/status', - }); + const resp = await serverShim.router.getHandler({ + method: 'get', + pathPattern: '/api/upgrade_assistant/status', + })(ctxMock, createRequestMock(), kibanaResponseFactory); - expect(resp.statusCode).toEqual(426); + expect(resp.status).toEqual(500); }); }); }); diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/cluster_checkup.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/cluster_checkup.ts new file mode 100644 index 00000000000000..3cfa567755b0fc --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/cluster_checkup.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import { ServerShimWithRouter } from '../types'; +import { getUpgradeAssistantStatus } from '../lib/es_migration_apis'; +import { versionCheckHandlerWrapper } from '../lib/es_version_precheck'; + +import { createRequestShim } from './create_request_shim'; + +export function registerClusterCheckupRoutes(server: ServerShimWithRouter) { + const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin'); + const isCloudEnabled = _.get(server.plugins, 'cloud.config.isCloudEnabled', false); + + server.router.get( + { + path: '/api/upgrade_assistant/status', + validate: false, + }, + versionCheckHandlerWrapper(async (ctx, request, response) => { + const reqShim = createRequestShim(request); + try { + return response.ok({ + body: await getUpgradeAssistantStatus(callWithRequest, reqShim, isCloudEnabled), + }); + } catch (e) { + if (e.status === 403) { + return response.forbidden(e.message); + } + + return response.internalError({ body: e }); + } + }) + ); +} diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/create_request_shim.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/create_request_shim.ts new file mode 100644 index 00000000000000..b1a5c8b72d0e0a --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/create_request_shim.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { KibanaRequest } from 'kibana/server'; +import { RequestShim } from '../types'; + +export const createRequestShim = (req: KibanaRequest): RequestShim => { + return { + headers: req.headers as Record, + payload: req.body || (req as any).payload, + params: req.params, + }; +}; diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/deprecation_logging.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/deprecation_logging.test.ts new file mode 100644 index 00000000000000..c488f999b538ef --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/deprecation_logging.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { kibanaResponseFactory } from 'src/core/server'; +import { createMockRouter, MockRouter } from './__mocks__/routes.mock'; +import { createRequestMock } from './__mocks__/request.mock'; + +jest.mock('../lib/es_version_precheck', () => ({ + versionCheckHandlerWrapper: (a: any) => a, +})); + +import { registerDeprecationLoggingRoutes } from './deprecation_logging'; + +/** + * Since these route callbacks are so thin, these serve simply as integration tests + * to ensure they're wired up to the lib functions correctly. Business logic is tested + * more thoroughly in the es_deprecation_logging_apis test. + */ +describe('deprecation logging API', () => { + let mockRouter: MockRouter; + let serverShim: any; + let callWithRequest: any; + const ctxMock: any = {}; + + beforeEach(() => { + mockRouter = createMockRouter(); + callWithRequest = jest.fn(); + serverShim = { + router: mockRouter, + plugins: { + cloud: { + config: { + isCloudEnabled: true, + }, + }, + elasticsearch: { + getCluster: () => ({ callWithRequest } as any), + } as any, + }, + }; + registerDeprecationLoggingRoutes(serverShim); + }); + + describe('GET /api/upgrade_assistant/deprecation_logging', () => { + it('returns isEnabled', async () => { + callWithRequest.mockResolvedValue({ default: { logger: { deprecation: 'WARN' } } }); + const resp = await serverShim.router.getHandler({ + method: 'get', + pathPattern: '/api/upgrade_assistant/deprecation_logging', + })(ctxMock, createRequestMock(), kibanaResponseFactory); + + expect(resp.status).toEqual(200); + expect(resp.payload).toEqual({ isEnabled: true }); + }); + + it('returns an error if it throws', async () => { + callWithRequest.mockRejectedValue(new Error(`scary error!`)); + const resp = await serverShim.router.getHandler({ + method: 'get', + pathPattern: '/api/upgrade_assistant/deprecation_logging', + })(ctxMock, createRequestMock(), kibanaResponseFactory); + + expect(resp.status).toEqual(500); + }); + }); + + describe('PUT /api/upgrade_assistant/deprecation_logging', () => { + it('returns isEnabled', async () => { + callWithRequest.mockResolvedValue({ default: { logger: { deprecation: 'ERROR' } } }); + const resp = await serverShim.router.getHandler({ + method: 'get', + pathPattern: '/api/upgrade_assistant/deprecation_logging', + })(ctxMock, createRequestMock(), kibanaResponseFactory); + + expect(resp.payload).toEqual({ isEnabled: false }); + }); + + it('returns an error if it throws', async () => { + callWithRequest.mockRejectedValue(new Error(`scary error!`)); + const resp = await serverShim.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/deprecation_logging', + })(ctxMock, { body: { isEnabled: false } }, kibanaResponseFactory); + + expect(resp.status).toEqual(500); + }); + }); +}); diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/deprecation_logging.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/deprecation_logging.ts new file mode 100644 index 00000000000000..7e19ef3fb60472 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/deprecation_logging.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; + +import { + getDeprecationLoggingStatus, + setDeprecationLogging, +} from '../lib/es_deprecation_logging_apis'; +import { versionCheckHandlerWrapper } from '../lib/es_version_precheck'; +import { ServerShimWithRouter } from '../types'; +import { createRequestShim } from './create_request_shim'; + +export function registerDeprecationLoggingRoutes(server: ServerShimWithRouter) { + const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin'); + + server.router.get( + { + path: '/api/upgrade_assistant/deprecation_logging', + validate: false, + }, + versionCheckHandlerWrapper(async (ctx, request, response) => { + const reqShim = createRequestShim(request); + try { + const result = await getDeprecationLoggingStatus(callWithRequest, reqShim); + return response.ok({ body: result }); + } catch (e) { + return response.internalError({ body: e }); + } + }) + ); + + server.router.put( + { + path: '/api/upgrade_assistant/deprecation_logging', + validate: { + body: schema.object({ + isEnabled: schema.boolean(), + }), + }, + }, + versionCheckHandlerWrapper(async (ctx, request, response) => { + const reqShim = createRequestShim(request); + try { + const { isEnabled } = reqShim.payload as { isEnabled: boolean }; + return response.ok({ + body: await setDeprecationLogging(callWithRequest, reqShim, isEnabled), + }); + } catch (e) { + return response.internalError({ body: e }); + } + }) + ); +} diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/routes/reindex_indices.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/reindex_indices.test.ts similarity index 58% rename from x-pack/legacy/plugins/upgrade_assistant/server/routes/reindex_indices.test.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/reindex_indices.test.ts index 264c98526622c3..d520324239656b 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/routes/reindex_indices.test.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/reindex_indices.test.ts @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Server } from 'hapi'; +import { savedObjectsClientMock } from 'src/core/server/mocks'; +import { kibanaResponseFactory } from 'src/core/server'; +import { createMockRouter, MockRouter } from './__mocks__/routes.mock'; +import { createRequestMock } from './__mocks__/request.mock'; const mockReindexService = { hasRequiredPrivileges: jest.fn(), @@ -18,14 +21,22 @@ const mockReindexService = { cancelReindexing: jest.fn(), }; -jest.mock('../lib/es_version_precheck'); +jest.mock('../lib/es_version_precheck', () => ({ + versionCheckHandlerWrapper: (a: any) => a, +})); + jest.mock('../lib/reindexing', () => { return { reindexServiceFactory: () => mockReindexService, }; }); -import { IndexGroup, ReindexSavedObject, ReindexStatus, ReindexWarning } from '../../common/types'; +import { + IndexGroup, + ReindexSavedObject, + ReindexStatus, + ReindexWarning, +} from '../../../common/types'; import { credentialStoreFactory } from '../lib/reindexing/credential_store'; import { registerReindexIndicesRoutes } from './reindex_indices'; @@ -35,28 +46,36 @@ import { registerReindexIndicesRoutes } from './reindex_indices'; * more thoroughly in the es_migration_apis test. */ describe('reindex API', () => { - const server = new Server(); - server.plugins = { - elasticsearch: { - getCluster: () => ({ callWithRequest: jest.fn() } as any), - } as any, - xpack_main: { - info: {}, - }, - } as any; - server.config = () => ({ get: () => '' } as any); - server.decorate('request', 'getSavedObjectsClient', () => jest.fn()); + let serverShim: any; + let mockRouter: MockRouter; + let ctxMock: any; const credentialStore = credentialStoreFactory(); - const worker = { includes: jest.fn(), forceRefresh: jest.fn(), } as any; - registerReindexIndicesRoutes(server, worker, credentialStore); - beforeEach(() => { + ctxMock = { + core: { + savedObjects: savedObjectsClientMock.create(), + }, + }; + mockRouter = createMockRouter(); + serverShim = { + router: mockRouter, + plugins: { + xpack_main: { + info: jest.fn(), + }, + elasticsearch: { + getCluster: () => ({ callWithRequest: jest.fn() } as any), + } as any, + }, + }; + registerReindexIndicesRoutes(serverShim, worker, credentialStore); + mockReindexService.hasRequiredPrivileges.mockResolvedValue(true); mockReindexService.detectReindexWarnings.mockReset(); mockReindexService.getIndexGroup.mockReset(); @@ -73,6 +92,8 @@ describe('reindex API', () => { credentialStore.clear(); }); + afterEach(() => jest.clearAllMocks()); + describe('GET /api/upgrade_assistant/reindex/{indexName}', () => { it('returns the attributes of the reindex operation and reindex warnings', async () => { mockReindexService.findReindexOperation.mockResolvedValueOnce({ @@ -80,18 +101,18 @@ describe('reindex API', () => { }); mockReindexService.detectReindexWarnings.mockResolvedValueOnce([ReindexWarning.allField]); - const resp = await server.inject({ - method: 'GET', - url: `/api/upgrade_assistant/reindex/wowIndex`, - }); + const resp = await serverShim.router.getHandler({ + method: 'get', + pathPattern: '/api/upgrade_assistant/reindex/{indexName}', + })(ctxMock, createRequestMock({ params: { indexName: 'wowIndex' } }), kibanaResponseFactory); // It called into the service correctly expect(mockReindexService.findReindexOperation).toHaveBeenCalledWith('wowIndex'); expect(mockReindexService.detectReindexWarnings).toHaveBeenCalledWith('wowIndex'); // It returned the right results - expect(resp.statusCode).toEqual(200); - const data = JSON.parse(resp.payload); + expect(resp.status).toEqual(200); + const data = resp.payload; expect(data.reindexOp).toEqual({ indexName: 'wowIndex', status: ReindexStatus.inProgress }); expect(data.warnings).toEqual([0]); }); @@ -100,13 +121,13 @@ describe('reindex API', () => { mockReindexService.findReindexOperation.mockResolvedValueOnce(null); mockReindexService.detectReindexWarnings.mockResolvedValueOnce(null); - const resp = await server.inject({ - method: 'GET', - url: `/api/upgrade_assistant/reindex/anIndex`, - }); + const resp = await serverShim.router.getHandler({ + method: 'get', + pathPattern: '/api/upgrade_assistant/reindex/{indexName}', + })(ctxMock, createRequestMock({ params: { indexName: 'anIndex' } }), kibanaResponseFactory); - expect(resp.statusCode).toEqual(200); - const data = JSON.parse(resp.payload); + expect(resp.status).toEqual(200); + const data = resp.payload; expect(data.reindexOp).toBeNull(); expect(data.warnings).toBeNull(); }); @@ -116,13 +137,13 @@ describe('reindex API', () => { mockReindexService.detectReindexWarnings.mockResolvedValueOnce([]); mockReindexService.getIndexGroup.mockReturnValue(IndexGroup.ml); - const resp = await server.inject({ - method: 'GET', - url: `/api/upgrade_assistant/reindex/.ml-state`, - }); + const resp = await serverShim.router.getHandler({ + method: 'get', + pathPattern: '/api/upgrade_assistant/reindex/{indexName}', + })(ctxMock, createRequestMock({ params: { indexName: 'anIndex' } }), kibanaResponseFactory); - expect(resp.statusCode).toEqual(200); - const data = JSON.parse(resp.payload); + expect(resp.status).toEqual(200); + const data = resp.payload; expect(data.indexGroup).toEqual(IndexGroup.ml); }); }); @@ -133,17 +154,17 @@ describe('reindex API', () => { attributes: { indexName: 'theIndex' }, }); - const resp = await server.inject({ - method: 'POST', - url: '/api/upgrade_assistant/reindex/theIndex', - }); + const resp = await serverShim.router.getHandler({ + method: 'post', + pathPattern: '/api/upgrade_assistant/reindex/{indexName}', + })(ctxMock, createRequestMock({ params: { indexName: 'theIndex' } }), kibanaResponseFactory); // It called create correctly expect(mockReindexService.createReindexOperation).toHaveBeenCalledWith('theIndex'); // It returned the right results - expect(resp.statusCode).toEqual(200); - const data = JSON.parse(resp.payload); + expect(resp.status).toEqual(200); + const data = resp.payload; expect(data).toEqual({ indexName: 'theIndex' }); }); @@ -152,10 +173,10 @@ describe('reindex API', () => { attributes: { indexName: 'theIndex' }, }); - await server.inject({ - method: 'POST', - url: '/api/upgrade_assistant/reindex/theIndex', - }); + await serverShim.router.getHandler({ + method: 'post', + pathPattern: '/api/upgrade_assistant/reindex/{indexName}', + })(ctxMock, createRequestMock({ params: { indexName: 'theIndex' } }), kibanaResponseFactory); expect(worker.forceRefresh).toHaveBeenCalled(); }); @@ -166,13 +187,19 @@ describe('reindex API', () => { } as ReindexSavedObject; mockReindexService.createReindexOperation.mockResolvedValueOnce(reindexOp); - await server.inject({ - method: 'POST', - url: '/api/upgrade_assistant/reindex/theIndex', - headers: { - 'kbn-auth-x': 'HERE!', - }, - }); + await serverShim.router.getHandler({ + method: 'post', + pathPattern: '/api/upgrade_assistant/reindex/{indexName}', + })( + ctxMock, + createRequestMock({ + headers: { + 'kbn-auth-x': 'HERE!', + }, + params: { indexName: 'theIndex' }, + }), + kibanaResponseFactory + ); expect(credentialStore.get(reindexOp)!['kbn-auth-x']).toEqual('HERE!'); }); @@ -185,30 +212,41 @@ describe('reindex API', () => { attributes: { indexName: 'theIndex', status: ReindexStatus.inProgress }, }); - const resp = await server.inject({ - method: 'POST', - url: '/api/upgrade_assistant/reindex/theIndex', - }); - + const resp = await serverShim.router.getHandler({ + method: 'post', + pathPattern: '/api/upgrade_assistant/reindex/{indexName}', + })( + ctxMock, + createRequestMock({ + params: { indexName: 'theIndex' }, + }), + kibanaResponseFactory + ); // It called resume correctly expect(mockReindexService.resumeReindexOperation).toHaveBeenCalledWith('theIndex'); expect(mockReindexService.createReindexOperation).not.toHaveBeenCalled(); // It returned the right results - expect(resp.statusCode).toEqual(200); - const data = JSON.parse(resp.payload); + expect(resp.status).toEqual(200); + const data = resp.payload; expect(data).toEqual({ indexName: 'theIndex', status: ReindexStatus.inProgress }); }); it('returns a 403 if required privileges fails', async () => { mockReindexService.hasRequiredPrivileges.mockResolvedValueOnce(false); - const resp = await server.inject({ - method: 'POST', - url: '/api/upgrade_assistant/reindex/theIndex', - }); - - expect(resp.statusCode).toEqual(403); + const resp = await serverShim.router.getHandler({ + method: 'post', + pathPattern: '/api/upgrade_assistant/reindex/{indexName}', + })( + ctxMock, + createRequestMock({ + params: { indexName: 'theIndex' }, + }), + kibanaResponseFactory + ); + + expect(resp.status).toEqual(403); }); }); @@ -216,13 +254,19 @@ describe('reindex API', () => { it('returns a 501', async () => { mockReindexService.cancelReindexing.mockResolvedValueOnce({}); - const resp = await server.inject({ - method: 'POST', - url: '/api/upgrade_assistant/reindex/cancelMe/cancel', - }); - - expect(resp.statusCode).toEqual(200); - expect(resp.payload).toMatchInlineSnapshot(`"{\\"acknowledged\\":true}"`); + const resp = await serverShim.router.getHandler({ + method: 'post', + pathPattern: '/api/upgrade_assistant/reindex/{indexName}/cancel', + })( + ctxMock, + createRequestMock({ + params: { indexName: 'cancelMe' }, + }), + kibanaResponseFactory + ); + + expect(resp.status).toEqual(200); + expect(resp.payload).toEqual({ acknowledged: true }); expect(mockReindexService.cancelReindexing).toHaveBeenCalledWith('cancelMe'); }); }); diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/routes/reindex_indices.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/reindex_indices.ts similarity index 56% rename from x-pack/legacy/plugins/upgrade_assistant/server/routes/reindex_indices.ts rename to x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/reindex_indices.ts index 43e4c9899d2334..c22f12316bd027 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/server/routes/reindex_indices.ts +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/reindex_indices.ts @@ -4,18 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import Boom from 'boom'; -import { Server } from 'hapi'; - +import { schema } from '@kbn/config-schema'; import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; -import { SavedObjectsClientContract } from 'src/core/server'; -import { ReindexStatus } from '../../common/types'; -import { EsVersionPrecheck } from '../lib/es_version_precheck'; +import { SavedObjectsClientContract } from 'kibana/server'; +import { ReindexStatus } from '../../../common/types'; +import { versionCheckHandlerWrapper } from '../lib/es_version_precheck'; import { reindexServiceFactory, ReindexWorker } from '../lib/reindexing'; import { CredentialStore } from '../lib/reindexing/credential_store'; import { reindexActionsFactory } from '../lib/reindexing/reindex_actions'; +import { ServerShim, ServerShimWithRouter } from '../types'; +import { createRequestShim } from './create_request_shim'; -export function registerReindexWorker(server: Server, credentialStore: CredentialStore) { +export function registerReindexWorker(server: ServerShim, credentialStore: CredentialStore) { const { callWithRequest, callWithInternalUser } = server.plugins.elasticsearch.getCluster( 'admin' ); @@ -29,11 +29,8 @@ export function registerReindexWorker(server: Server, credentialStore: Credentia // Cannot pass server.log directly because it's value changes during startup (?). // Use this function to proxy through. - const log: Server['log'] = ( - tags: string | string[], - data?: string | object | (() => any), - timestamp?: number - ) => server.log(tags, data, timestamp); + const log = (tags: string | string[], data?: string | object | (() => any), timestamp?: number) => + server.log(tags, data, timestamp); const worker = new ReindexWorker( savedObjectsClient, @@ -54,7 +51,7 @@ export function registerReindexWorker(server: Server, credentialStore: Credentia } export function registerReindexIndicesRoutes( - server: Server, + server: ServerShimWithRouter, worker: ReindexWorker, credentialStore: CredentialStore ) { @@ -63,16 +60,20 @@ export function registerReindexIndicesRoutes( const BASE_PATH = '/api/upgrade_assistant/reindex'; // Start reindex for an index - server.route({ - path: `${BASE_PATH}/{indexName}`, - method: 'POST', - options: { - pre: [EsVersionPrecheck], + server.router.post( + { + path: `${BASE_PATH}/{indexName}`, + validate: { + params: schema.object({ + indexName: schema.string(), + }), + }, }, - async handler(request) { - const client = request.getSavedObjectsClient(); - const { indexName } = request.params; - const callCluster = callWithRequest.bind(null, request) as CallCluster; + versionCheckHandlerWrapper(async (ctx, request, response) => { + const reqShim = createRequestShim(request); + const { indexName } = reqShim.params; + const { client } = ctx.core.savedObjects; + const callCluster = callWithRequest.bind(null, reqShim) as CallCluster; const reindexActions = reindexActionsFactory(client, callCluster); const reindexService = reindexServiceFactory( callCluster, @@ -83,7 +84,9 @@ export function registerReindexIndicesRoutes( try { if (!(await reindexService.hasRequiredPrivileges(indexName))) { - throw Boom.forbidden(`You do not have adequate privileges to reindex this index.`); + return response.forbidden({ + body: `You do not have adequate privileges to reindex this index.`, + }); } const existingOp = await reindexService.findReindexOperation(indexName); @@ -95,33 +98,33 @@ export function registerReindexIndicesRoutes( : await reindexService.createReindexOperation(indexName); // Add users credentials for the worker to use - credentialStore.set(reindexOp, request.headers); + credentialStore.set(reindexOp, reqShim.headers); // Kick the worker on this node to immediately pickup the new reindex operation. worker.forceRefresh(); - return reindexOp.attributes; + return response.ok({ body: reindexOp.attributes }); } catch (e) { - if (!e.isBoom) { - return Boom.boomify(e, { statusCode: 500 }); - } - - return e; + return response.internalError({ body: e }); } - }, - }); + }) + ); // Get status - server.route({ - path: `${BASE_PATH}/{indexName}`, - method: 'GET', - options: { - pre: [EsVersionPrecheck], + server.router.get( + { + path: `${BASE_PATH}/{indexName}`, + validate: { + params: schema.object({ + indexName: schema.string(), + }), + }, }, - async handler(request) { - const client = request.getSavedObjectsClient(); - const { indexName } = request.params; - const callCluster = callWithRequest.bind(null, request) as CallCluster; + versionCheckHandlerWrapper(async (ctx, request, response) => { + const reqShim = createRequestShim(request); + const { client } = ctx.core.savedObjects; + const { indexName } = reqShim.params; + const callCluster = callWithRequest.bind(null, reqShim) as CallCluster; const reindexActions = reindexActionsFactory(client, callCluster); const reindexService = reindexServiceFactory( callCluster, @@ -139,33 +142,43 @@ export function registerReindexIndicesRoutes( : []; const indexGroup = reindexService.getIndexGroup(indexName); - return { - reindexOp: reindexOp ? reindexOp.attributes : null, - warnings, - indexGroup, - hasRequiredPrivileges, - }; + return response.ok({ + body: { + reindexOp: reindexOp ? reindexOp.attributes : null, + warnings, + indexGroup, + hasRequiredPrivileges, + }, + }); } catch (e) { if (!e.isBoom) { - return Boom.boomify(e, { statusCode: 500 }); + return response.internalError({ body: e }); } - - return e; + return response.customError({ + body: { + message: e.message, + }, + statusCode: e.statusCode, + }); } - }, - }); + }) + ); // Cancel reindex - server.route({ - path: `${BASE_PATH}/{indexName}/cancel`, - method: 'POST', - options: { - pre: [EsVersionPrecheck], + server.router.post( + { + path: `${BASE_PATH}/{indexName}/cancel`, + validate: { + params: schema.object({ + indexName: schema.string(), + }), + }, }, - async handler(request) { - const client = request.getSavedObjectsClient(); - const { indexName } = request.params; - const callCluster = callWithRequest.bind(null, request) as CallCluster; + versionCheckHandlerWrapper(async (ctx, request, response) => { + const reqShim = createRequestShim(request); + const { indexName } = reqShim.params; + const { client } = ctx.core.savedObjects; + const callCluster = callWithRequest.bind(null, reqShim) as CallCluster; const reindexActions = reindexActionsFactory(client, callCluster); const reindexService = reindexServiceFactory( callCluster, @@ -177,14 +190,18 @@ export function registerReindexIndicesRoutes( try { await reindexService.cancelReindexing(indexName); - return { acknowledged: true }; + return response.ok({ body: { acknowledged: true } }); } catch (e) { if (!e.isBoom) { - return Boom.boomify(e, { statusCode: 500 }); + return response.internalError({ body: e }); } - - return e; + return response.customError({ + body: { + message: e.message, + }, + statusCode: e.statusCode, + }); } - }, - }); + }) + ); } diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/telemetry.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/telemetry.test.ts new file mode 100644 index 00000000000000..582c75e3701b64 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/telemetry.test.ts @@ -0,0 +1,190 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { kibanaResponseFactory } from 'src/core/server'; +import { createMockRouter, MockRouter } from './__mocks__/routes.mock'; +import { createRequestMock } from './__mocks__/request.mock'; + +jest.mock('../lib/telemetry/es_ui_open_apis', () => ({ + upsertUIOpenOption: jest.fn(), +})); + +jest.mock('../lib/telemetry/es_ui_reindex_apis', () => ({ + upsertUIReindexOption: jest.fn(), +})); + +import { upsertUIOpenOption } from '../lib/telemetry/es_ui_open_apis'; +import { upsertUIReindexOption } from '../lib/telemetry/es_ui_reindex_apis'; +import { registerTelemetryRoutes } from './telemetry'; + +/** + * Since these route callbacks are so thin, these serve simply as integration tests + * to ensure they're wired up to the lib functions correctly. Business logic is tested + * more thoroughly in the lib/telemetry tests. + */ +describe('Upgrade Assistant Telemetry API', () => { + let serverShim: any; + let mockRouter: MockRouter; + let ctxMock: any; + beforeEach(() => { + ctxMock = {}; + mockRouter = createMockRouter(); + serverShim = { + router: mockRouter, + plugins: { + xpack_main: { + info: jest.fn(), + }, + elasticsearch: { + getCluster: () => ({ callWithRequest: jest.fn() } as any), + } as any, + }, + }; + registerTelemetryRoutes(serverShim); + }); + afterEach(() => jest.clearAllMocks()); + + describe('PUT /api/upgrade_assistant/telemetry/ui_open', () => { + it('returns correct payload with single option', async () => { + const returnPayload = { + overview: true, + cluster: false, + indices: false, + }; + + (upsertUIOpenOption as jest.Mock).mockResolvedValue(returnPayload); + + const resp = await serverShim.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/telemetry/ui_open', + })(ctxMock, createRequestMock(), kibanaResponseFactory); + + expect(resp.payload).toEqual(returnPayload); + }); + + it('returns correct payload with multiple option', async () => { + const returnPayload = { + overview: true, + cluster: true, + indices: true, + }; + + (upsertUIOpenOption as jest.Mock).mockResolvedValue(returnPayload); + + const resp = await serverShim.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/telemetry/ui_open', + })( + ctxMock, + createRequestMock({ + payload: { + overview: true, + cluster: true, + indices: true, + }, + }), + kibanaResponseFactory + ); + + expect(resp.payload).toEqual(returnPayload); + }); + + it('returns an error if it throws', async () => { + (upsertUIOpenOption as jest.Mock).mockRejectedValue(new Error(`scary error!`)); + + const resp = await serverShim.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/telemetry/ui_open', + })( + ctxMock, + createRequestMock({ + payload: { + overview: false, + }, + }), + kibanaResponseFactory + ); + + expect(resp.status).toEqual(500); + }); + }); + + describe('PUT /api/upgrade_assistant/telemetry/ui_reindex', () => { + it('returns correct payload with single option', async () => { + const returnPayload = { + close: false, + open: false, + start: true, + stop: false, + }; + + (upsertUIReindexOption as jest.Mock).mockRejectedValue(returnPayload); + + const resp = await serverShim.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/telemetry/ui_reindex', + })( + ctxMock, + createRequestMock({ + payload: { + overview: false, + }, + }), + kibanaResponseFactory + ); + + expect(resp.payload).toEqual(returnPayload); + }); + + it('returns correct payload with multiple option', async () => { + const returnPayload = { + close: true, + open: true, + start: true, + stop: true, + }; + + (upsertUIReindexOption as jest.Mock).mockRejectedValue(returnPayload); + + const resp = await serverShim.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/telemetry/ui_reindex', + })( + ctxMock, + createRequestMock({ + payload: { + close: true, + open: true, + start: true, + stop: true, + }, + }), + kibanaResponseFactory + ); + + expect(resp.payload).toEqual(returnPayload); + }); + + it('returns an error if it throws', async () => { + (upsertUIReindexOption as jest.Mock).mockRejectedValue(new Error(`scary error!`)); + + const resp = await serverShim.router.getHandler({ + method: 'put', + pathPattern: '/api/upgrade_assistant/telemetry/ui_reindex', + })( + ctxMock, + createRequestMock({ + payload: { + start: false, + }, + }), + kibanaResponseFactory + ); + + expect(resp.status).toEqual(500); + }); + }); +}); diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/telemetry.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/telemetry.ts new file mode 100644 index 00000000000000..f08c49809033db --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/routes/telemetry.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { upsertUIOpenOption } from '../lib/telemetry/es_ui_open_apis'; +import { upsertUIReindexOption } from '../lib/telemetry/es_ui_reindex_apis'; +import { ServerShimWithRouter } from '../types'; +import { createRequestShim } from './create_request_shim'; + +export function registerTelemetryRoutes(server: ServerShimWithRouter) { + server.router.put( + { + path: '/api/upgrade_assistant/telemetry/ui_open', + validate: { + body: schema.object({ + overview: schema.boolean({ defaultValue: false }), + cluster: schema.boolean({ defaultValue: false }), + indices: schema.boolean({ defaultValue: false }), + }), + }, + }, + async (ctx, request, response) => { + const reqShim = createRequestShim(request); + try { + return response.ok({ body: await upsertUIOpenOption(server, reqShim) }); + } catch (e) { + return response.internalError({ body: e }); + } + } + ); + + server.router.put( + { + path: '/api/upgrade_assistant/telemetry/ui_reindex', + validate: { + body: schema.object({ + close: schema.boolean({ defaultValue: false }), + open: schema.boolean({ defaultValue: false }), + start: schema.boolean({ defaultValue: false }), + stop: schema.boolean({ defaultValue: false }), + }), + }, + }, + async (ctx, request, response) => { + const reqShim = createRequestShim(request); + try { + return response.ok({ body: await upsertUIReindexOption(server, reqShim) }); + } catch (e) { + return response.internalError({ body: e }); + } + } + ); +} diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/types.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/types.ts new file mode 100644 index 00000000000000..dc4ecbc806320b --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/types.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Legacy } from 'kibana'; +import { IRouter } from 'src/core/server'; +import { ElasticsearchPlugin } from 'src/legacy/core_plugins/elasticsearch'; +import { XPackMainPlugin } from '../../../xpack_main/xpack_main'; + +export interface ServerShim { + usage: { + collectorSet: { + makeUsageCollector: any; + register: any; + }; + }; + plugins: { + elasticsearch: ElasticsearchPlugin; + xpack_main: XPackMainPlugin; + cloud: { + config: { + isCloudEnabled: boolean; + }; + }; + }; + log: any; + events: any; + savedObjects: Legacy.SavedObjectsService; +} + +export interface ServerShimWithRouter extends ServerShim { + router: IRouter; +} + +export interface RequestShim { + headers: Record; + payload: any; + params: any; +} diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/routes/cluster_checkup.ts b/x-pack/legacy/plugins/upgrade_assistant/server/routes/cluster_checkup.ts deleted file mode 100644 index 21c7bc4e5e65d7..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/server/routes/cluster_checkup.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; -import { Legacy } from 'kibana'; -import _ from 'lodash'; - -import { getUpgradeAssistantStatus } from '../lib/es_migration_apis'; -import { EsVersionPrecheck } from '../lib/es_version_precheck'; - -export function registerClusterCheckupRoutes(server: Legacy.Server) { - const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin'); - const isCloudEnabled = _.get(server.plugins, 'cloud.config.isCloudEnabled', false); - - server.route({ - path: '/api/upgrade_assistant/status', - method: 'GET', - options: { - pre: [EsVersionPrecheck], - }, - async handler(request) { - try { - return await getUpgradeAssistantStatus(callWithRequest, request, isCloudEnabled); - } catch (e) { - if (e.status === 403) { - return Boom.forbidden(e.message); - } - - return Boom.boomify(e, { - statusCode: 500, - }); - } - }, - }); -} diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts b/x-pack/legacy/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts deleted file mode 100644 index e7918835d461db..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Server } from 'hapi'; - -jest.mock('../lib/es_version_precheck'); -import { registerDeprecationLoggingRoutes } from './deprecation_logging'; - -/** - * Since these route callbacks are so thin, these serve simply as integration tests - * to ensure they're wired up to the lib functions correctly. Business logic is tested - * more thoroughly in the es_deprecation_logging_apis test. - */ -describe('deprecation logging API', () => { - const callWithRequest = jest.fn(); - const server = new Server(); - server.plugins = { - elasticsearch: { - getCluster: () => ({ callWithRequest } as any), - } as any, - } as any; - - registerDeprecationLoggingRoutes(server); - - describe('GET /api/upgrade_assistant/deprecation_logging', () => { - it('returns isEnabled', async () => { - callWithRequest.mockResolvedValue({ default: { logger: { deprecation: 'WARN' } } }); - const resp = await server.inject({ - method: 'GET', - url: '/api/upgrade_assistant/deprecation_logging', - }); - - expect(resp.statusCode).toEqual(200); - expect(JSON.parse(resp.payload)).toEqual({ isEnabled: true }); - }); - - it('returns an error if it throws', async () => { - callWithRequest.mockRejectedValue(new Error(`scary error!`)); - const resp = await server.inject({ - method: 'GET', - url: '/api/upgrade_assistant/deprecation_logging', - }); - - expect(resp.statusCode).toEqual(500); - }); - }); - - describe('PUT /api/upgrade_assistant/deprecation_logging', () => { - it('returns isEnabled', async () => { - callWithRequest.mockResolvedValue({ default: { logger: { deprecation: 'ERROR' } } }); - const resp = await server.inject({ - method: 'GET', - url: '/api/upgrade_assistant/deprecation_logging', - payload: { - isEnabled: false, - }, - }); - - expect(JSON.parse(resp.payload)).toEqual({ isEnabled: false }); - }); - - it('returns an error if it throws', async () => { - callWithRequest.mockRejectedValue(new Error(`scary error!`)); - const resp = await server.inject({ - method: 'PUT', - url: '/api/upgrade_assistant/deprecation_logging', - payload: { - isEnabled: false, - }, - }); - - expect(resp.statusCode).toEqual(500); - }); - }); -}); diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/routes/deprecation_logging.ts b/x-pack/legacy/plugins/upgrade_assistant/server/routes/deprecation_logging.ts deleted file mode 100644 index d16a87916ad7d2..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/server/routes/deprecation_logging.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; -import Joi from 'joi'; -import { Legacy } from 'kibana'; - -import { - getDeprecationLoggingStatus, - setDeprecationLogging, -} from '../lib/es_deprecation_logging_apis'; -import { EsVersionPrecheck } from '../lib/es_version_precheck'; - -export function registerDeprecationLoggingRoutes(server: Legacy.Server) { - const { callWithRequest } = server.plugins.elasticsearch.getCluster('admin'); - - server.route({ - path: '/api/upgrade_assistant/deprecation_logging', - method: 'GET', - options: { - pre: [EsVersionPrecheck], - }, - async handler(request) { - try { - return await getDeprecationLoggingStatus(callWithRequest, request); - } catch (e) { - return Boom.boomify(e, { statusCode: 500 }); - } - }, - }); - - server.route({ - path: '/api/upgrade_assistant/deprecation_logging', - method: 'PUT', - options: { - pre: [EsVersionPrecheck], - validate: { - payload: Joi.object({ - isEnabled: Joi.boolean(), - }), - }, - }, - async handler(request) { - try { - const { isEnabled } = request.payload as { isEnabled: boolean }; - return await setDeprecationLogging(callWithRequest, request, isEnabled); - } catch (e) { - return Boom.boomify(e, { statusCode: 500 }); - } - }, - }); -} diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/routes/telemetry.test.js b/x-pack/legacy/plugins/upgrade_assistant/server/routes/telemetry.test.js deleted file mode 100644 index a3706231f2297a..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/server/routes/telemetry.test.js +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -jest.mock('../lib/telemetry/es_ui_open_apis', () => ({ - upsertUIOpenOption: jest.fn(), -})); - -jest.mock('../lib/telemetry/es_ui_reindex_apis', () => ({ - upsertUIReindexOption: jest.fn(), -})); - -import { Server } from 'hapi'; -import { upsertUIOpenOption } from '../lib/telemetry/es_ui_open_apis'; -import { upsertUIReindexOption } from '../lib/telemetry/es_ui_reindex_apis'; -import { registerTelemetryRoutes } from './telemetry'; - -/** - * Since these route callbacks are so thin, these serve simply as integration tests - * to ensure they're wired up to the lib functions correctly. Business logic is tested - * more thoroughly in the lib/telemetry tests. - */ -describe('Upgrade Assistant Telemetry API', () => { - const server = new Server(); - - registerTelemetryRoutes(server); - - describe('PUT /api/upgrade_assistant/telemetry/ui_open', () => { - it('returns correct payload with single option', async () => { - const returnPayload = { - overview: true, - cluster: false, - indices: false, - }; - - upsertUIOpenOption.mockResolvedValue(returnPayload); - - const resp = await server.inject({ - method: 'PUT', - url: '/api/upgrade_assistant/telemetry/ui_open', - payload: { - overview: true, - }, - }); - - expect(JSON.parse(resp.payload)).toEqual(returnPayload); - }); - - it('returns correct payload with multiple option', async () => { - const returnPayload = { - overview: true, - cluster: true, - indices: true, - }; - - upsertUIOpenOption.mockResolvedValue(returnPayload); - - const resp = await server.inject({ - method: 'PUT', - url: '/api/upgrade_assistant/telemetry/ui_open', - payload: { - overview: true, - cluster: true, - indices: true, - }, - }); - - expect(JSON.parse(resp.payload)).toEqual(returnPayload); - }); - - it('returns an error if it throws', async () => { - upsertUIOpenOption.mockRejectedValue(new Error(`scary error!`)); - const resp = await server.inject({ - method: 'PUT', - url: '/api/upgrade_assistant/telemetry/ui_open', - payload: { - overview: false, - }, - }); - - expect(resp.statusCode).toEqual(500); - }); - }); - - describe('PUT /api/upgrade_assistant/telemetry/ui_reindex', () => { - it('returns correct payload with single option', async () => { - const returnPayload = { - close: false, - open: false, - start: true, - stop: false, - }; - - upsertUIReindexOption.mockResolvedValue(returnPayload); - - const resp = await server.inject({ - method: 'PUT', - url: '/api/upgrade_assistant/telemetry/ui_reindex', - payload: { - start: true, - }, - }); - - expect(JSON.parse(resp.payload)).toEqual(returnPayload); - }); - - it('returns correct payload with multiple option', async () => { - const returnPayload = { - close: true, - open: true, - start: true, - stop: true, - }; - - upsertUIReindexOption.mockResolvedValue(returnPayload); - - const resp = await server.inject({ - method: 'PUT', - url: '/api/upgrade_assistant/telemetry/ui_reindex', - payload: { - close: true, - open: true, - start: true, - stop: true, - }, - }); - - expect(JSON.parse(resp.payload)).toEqual(returnPayload); - }); - - it('returns an error if it throws', async () => { - upsertUIReindexOption.mockRejectedValue(new Error(`scary error!`)); - const resp = await server.inject({ - method: 'PUT', - url: '/api/upgrade_assistant/telemetry/ui_reindex', - payload: { - start: false, - }, - }); - - expect(resp.statusCode).toEqual(500); - }); - }); -}); diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/routes/telemetry.ts b/x-pack/legacy/plugins/upgrade_assistant/server/routes/telemetry.ts deleted file mode 100644 index 6def6d1e72ea33..00000000000000 --- a/x-pack/legacy/plugins/upgrade_assistant/server/routes/telemetry.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Boom from 'boom'; -import Joi from 'joi'; -import { UpgradeAssistantTelemetryServer } from '../../common/types'; -import { upsertUIOpenOption } from '../lib/telemetry/es_ui_open_apis'; -import { upsertUIReindexOption } from '../lib/telemetry/es_ui_reindex_apis'; - -export function registerTelemetryRoutes(server: UpgradeAssistantTelemetryServer) { - server.route({ - path: '/api/upgrade_assistant/telemetry/ui_open', - method: 'PUT', - options: { - validate: { - payload: Joi.object({ - overview: Joi.boolean().default(false), - cluster: Joi.boolean().default(false), - indices: Joi.boolean().default(false), - }), - }, - }, - async handler(request) { - try { - return await upsertUIOpenOption(server, request); - } catch (e) { - return Boom.boomify(e, { statusCode: 500 }); - } - }, - }); - - server.route({ - path: '/api/upgrade_assistant/telemetry/ui_reindex', - method: 'PUT', - options: { - validate: { - payload: Joi.object({ - close: Joi.boolean().default(false), - open: Joi.boolean().default(false), - start: Joi.boolean().default(false), - stop: Joi.boolean().default(false), - }), - }, - }, - async handler(request) { - try { - return await upsertUIReindexOption(server, request); - } catch (e) { - return Boom.boomify(e, { statusCode: 500 }); - } - }, - }); -} diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/__tests__/__snapshots__/kibana_global_help.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/__tests__/__snapshots__/kibana_global_help.test.tsx.snap deleted file mode 100644 index b3f749b12d9d14..00000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/__tests__/__snapshots__/kibana_global_help.test.tsx.snap +++ /dev/null @@ -1,43 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renderUptimeKibanaGlobalHelp renders links with expected urls 1`] = ` - - - - - For Uptime specific information - - - - - - - - - - -`; diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/__tests__/kibana_global_help.test.tsx b/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/__tests__/kibana_global_help.test.tsx deleted file mode 100644 index a4791a41e03478..00000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/__tests__/kibana_global_help.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { renderUptimeKibanaGlobalHelp } from '../kibana_global_help'; - -describe('renderUptimeKibanaGlobalHelp', () => { - it('renders links with expected urls', () => { - expect(renderUptimeKibanaGlobalHelp('https://elastic.co/', 'master')).toMatchSnapshot(); - }); -}); diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/kibana_global_help.tsx b/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/kibana_global_help.tsx deleted file mode 100644 index 8e730dcc293104..00000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/kibana_global_help.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiLink, EuiSpacer, EuiHorizontalRule, EuiButton, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; - -export const renderUptimeKibanaGlobalHelp = (docsSiteUrl: string, docLinkVersion: string) => ( - - - - For Uptime specific information - - - - - - - - - -); diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx b/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx index 44191d7e61e0d6..94bfe79a6ca6e4 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx +++ b/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx @@ -9,12 +9,12 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { get } from 'lodash'; import { AutocompleteProviderRegister } from 'src/plugins/data/public'; +import { i18n as i18nFormatter } from '@kbn/i18n'; import { CreateGraphQLClient } from './framework_adapter_types'; import { UptimeApp, UptimeAppProps } from '../../../uptime_app'; import { getIntegratedAppAvailability } from './capabilities_adapter'; import { INTEGRATED_SOLUTIONS, PLUGIN } from '../../../../common/constants'; import { getTelemetryMonitorPageLogger, getTelemetryOverviewPageLogger } from '../telemetry'; -import { renderUptimeKibanaGlobalHelp } from './kibana_global_help'; import { UMFrameworkAdapter, BootstrapUptimeApp } from '../../lib'; import { createApolloClient } from './apollo_client_adapter'; @@ -52,12 +52,20 @@ export const getKibanaFrameworkAdapter = ( logMonitorPageLoad: getTelemetryMonitorPageLogger('true', basePath.get()), logOverviewPageLoad: getTelemetryOverviewPageLogger('true', basePath.get()), renderGlobalHelpControls: () => - setHelpExtension((element: HTMLElement) => { - ReactDOM.render( - renderUptimeKibanaGlobalHelp(ELASTIC_WEBSITE_URL, DOC_LINK_VERSION), - element - ); - return () => ReactDOM.unmountComponentAtNode(element); + setHelpExtension({ + appName: i18nFormatter.translate('xpack.uptime.header.appName', { + defaultMessage: 'Uptime', + }), + links: [ + { + linkType: 'documentation', + href: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/xpack-uptime.html`, + }, + { + linkType: 'discuss', + href: 'https://discuss.elastic.co/c/uptime', + }, + ], }), routerBasename: basePath.prepend(PLUGIN.ROUTER_BASE_NAME), setBadge, diff --git a/x-pack/package.json b/x-pack/package.json index 402c3e95159f3b..628e77a9fd544e 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -176,10 +176,9 @@ "@babel/core": "^7.5.5", "@babel/register": "^7.5.5", "@babel/runtime": "^7.5.5", - "@elastic/ctags-langserver": "^0.1.11", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "1.0.5", - "@elastic/eui": "14.9.0", + "@elastic/eui": "16.0.0", "@elastic/filesaver": "1.1.2", "@elastic/javascript-typescript-langserver": "^0.3.3", "@elastic/lsp-extension": "^0.1.2", diff --git a/x-pack/plugins/licensing/public/plugin.test.ts b/x-pack/plugins/licensing/public/plugin.test.ts index 8ede881cad47e3..60dfd1f6cb260a 100644 --- a/x-pack/plugins/licensing/public/plugin.test.ts +++ b/x-pack/plugins/licensing/public/plugin.test.ts @@ -38,6 +38,25 @@ describe('licensing plugin', () => { expect(license.uid).toBe('fetched'); }); + + it('data re-fetch call marked as a system api', async () => { + const sessionStorage = coreMock.createStorage(); + plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage); + + const coreSetup = coreMock.createSetup(); + const fetchedLicense = licenseMock.create(); + coreSetup.http.get.mockResolvedValue(fetchedLicense); + + const { refresh } = await plugin.setup(coreSetup); + + refresh(); + + expect(coreSetup.http.get.mock.calls[0][1]).toMatchObject({ + headers: { + 'kbn-system-api': 'true', + }, + }); + }); }); describe('#license$', () => { @@ -238,7 +257,7 @@ describe('licensing plugin', () => { }, }, request: { - url: 'http://10.10.10.10:5601/api/xpack/v1/info', + url: 'http://10.10.10.10:5601/api/licensing/info', }, }; expect(coreSetup.http.get).toHaveBeenCalledTimes(0); diff --git a/x-pack/plugins/licensing/public/plugin.ts b/x-pack/plugins/licensing/public/plugin.ts index c1b13418aa3e71..79ad6f289b67e6 100644 --- a/x-pack/plugins/licensing/public/plugin.ts +++ b/x-pack/plugins/licensing/public/plugin.ts @@ -5,7 +5,7 @@ */ import { Subject, Subscription, merge } from 'rxjs'; -import { takeUntil, tap } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; import { CoreSetup, Plugin, PluginInitializerContext } from 'src/core/public'; @@ -31,8 +31,9 @@ export class LicensingPlugin implements Plugin { */ private removeInterceptor?: () => void; private licenseFetchSubscription?: Subscription; + private storageSubscription?: Subscription; - private infoEndpoint = '/api/xpack/v1/info'; + private readonly infoEndpoint = '/api/licensing/info'; private prevSignature?: string; constructor( @@ -76,18 +77,16 @@ export class LicensingPlugin implements Plugin { ); this.licenseFetchSubscription = fetchSubscription; - const license$ = update$.pipe( - tap(license => { - if (license.error) { - this.prevSignature = undefined; - // Prevent reusing stale license if the fetch operation fails - this.removeSaved(); - } else { - this.prevSignature = license.signature; - this.save(license); - } - }) - ); + this.storageSubscription = update$.subscribe(license => { + if (license.isAvailable) { + this.prevSignature = license.signature; + this.save(license); + } else { + this.prevSignature = undefined; + // Prevent reusing stale license if the fetch operation fails + this.removeSaved(); + } + }); this.removeInterceptor = core.http.intercept({ response: async httpResponse => { @@ -107,7 +106,7 @@ export class LicensingPlugin implements Plugin { refresh: () => { manualRefresh$.next(); }, - license$, + license$: update$, }; } @@ -124,11 +123,20 @@ export class LicensingPlugin implements Plugin { this.licenseFetchSubscription.unsubscribe(); this.licenseFetchSubscription = undefined; } + if (this.storageSubscription !== undefined) { + this.storageSubscription.unsubscribe(); + this.storageSubscription = undefined; + } } private fetchLicense = async (core: CoreSetup): Promise => { try { - const response = await core.http.get(this.infoEndpoint); + const response = await core.http.get(this.infoEndpoint, { + headers: { + 'kbn-system-api': 'true', + }, + }); + return new License({ license: response.license, features: response.features, diff --git a/x-pack/plugins/licensing/server/plugin.ts b/x-pack/plugins/licensing/server/plugin.ts index 3c93b55723787c..d3dc84c05e25c8 100644 --- a/x-pack/plugins/licensing/server/plugin.ts +++ b/x-pack/plugins/licensing/server/plugin.ts @@ -24,6 +24,8 @@ import { License } from '../common/license'; import { createLicenseUpdate } from '../common/license_update'; import { ElasticsearchError, RawLicense, RawFeatures } from './types'; +import { registerRoutes } from './routes'; + import { LicenseConfigType } from './licensing_config'; import { createRouteHandlerContext } from './licensing_route_handler_context'; @@ -92,6 +94,7 @@ export class LicensingPlugin implements Plugin { const { refresh, license$ } = this.createLicensePoller(dataClient, config.pollingFrequency); core.http.registerRouteHandlerContext('licensing', createRouteHandlerContext(license$)); + registerRoutes(core.http.createRouter()); return { refresh, diff --git a/x-pack/plugins/licensing/server/routes/index.ts b/x-pack/plugins/licensing/server/routes/index.ts new file mode 100644 index 00000000000000..26b3bc6292dd62 --- /dev/null +++ b/x-pack/plugins/licensing/server/routes/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { IRouter } from 'src/core/server'; +import { registerInfoRoute } from './info'; + +export function registerRoutes(router: IRouter) { + registerInfoRoute(router); +} diff --git a/x-pack/plugins/licensing/server/routes/info.ts b/x-pack/plugins/licensing/server/routes/info.ts new file mode 100644 index 00000000000000..cad873014e2715 --- /dev/null +++ b/x-pack/plugins/licensing/server/routes/info.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { IRouter } from 'src/core/server'; + +export function registerInfoRoute(router: IRouter) { + router.get({ path: '/api/licensing/info', validate: false }, (context, request, response) => { + return response.ok({ + body: context.licensing.license, + }); + }); +} diff --git a/x-pack/plugins/security/server/authentication/providers/kerberos.test.ts b/x-pack/plugins/security/server/authentication/providers/kerberos.test.ts index 4ab47cb95b9a31..27105793fc9664 100644 --- a/x-pack/plugins/security/server/authentication/providers/kerberos.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/kerberos.test.ts @@ -574,7 +574,7 @@ describe('KerberosAuthenticationProvider', () => { sinon.assert.calledWithExactly(mockOptions.tokens.invalidate, tokenPair); expect(authenticationResult.redirected()).toBe(true); - expect(authenticationResult.redirectURL).toBe('/logged_out'); + expect(authenticationResult.redirectURL).toBe('/mock-server-basepath/logged_out'); }); }); }); diff --git a/x-pack/plugins/security/server/authentication/providers/kerberos.ts b/x-pack/plugins/security/server/authentication/providers/kerberos.ts index 0e31dd3d51abaf..767eab7b4311d9 100644 --- a/x-pack/plugins/security/server/authentication/providers/kerberos.ts +++ b/x-pack/plugins/security/server/authentication/providers/kerberos.ts @@ -107,7 +107,7 @@ export class KerberosAuthenticationProvider extends BaseAuthenticationProvider { return DeauthenticationResult.failed(err); } - return DeauthenticationResult.redirectTo('/logged_out'); + return DeauthenticationResult.redirectTo(`${this.options.basePath.serverBasePath}/logged_out`); } /** diff --git a/x-pack/plugins/security/server/authentication/providers/oidc.test.ts b/x-pack/plugins/security/server/authentication/providers/oidc.test.ts index 7e00d8f282f621..c1d7dcca4c78ff 100644 --- a/x-pack/plugins/security/server/authentication/providers/oidc.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/oidc.test.ts @@ -598,7 +598,7 @@ describe('OIDCAuthenticationProvider', () => { }); expect(authenticationResult.redirected()).toBe(true); - expect(authenticationResult.redirectURL).toBe('/base-path/logged_out'); + expect(authenticationResult.redirectURL).toBe('/mock-server-basepath/logged_out'); }); it('redirects user to the OpenID Connect Provider if RP initiated SLO is supported.', async () => { diff --git a/x-pack/plugins/security/server/authentication/providers/oidc.ts b/x-pack/plugins/security/server/authentication/providers/oidc.ts index 824189fa77a264..37371236453796 100644 --- a/x-pack/plugins/security/server/authentication/providers/oidc.ts +++ b/x-pack/plugins/security/server/authentication/providers/oidc.ts @@ -431,7 +431,9 @@ export class OIDCAuthenticationProvider extends BaseAuthenticationProvider { return DeauthenticationResult.redirectTo(redirect); } - return DeauthenticationResult.redirectTo(`${this.options.basePath.get(request)}/logged_out`); + return DeauthenticationResult.redirectTo( + `${this.options.basePath.serverBasePath}/logged_out` + ); } catch (err) { this.logger.debug(`Failed to deauthenticate user: ${err.message}`); return DeauthenticationResult.failed(err); diff --git a/x-pack/plugins/security/server/authentication/providers/pki.test.ts b/x-pack/plugins/security/server/authentication/providers/pki.test.ts index 35d827c3a9bd19..76442733e7368e 100644 --- a/x-pack/plugins/security/server/authentication/providers/pki.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/pki.test.ts @@ -583,7 +583,7 @@ describe('PKIAuthenticationProvider', () => { expect(mockOptions.tokens.invalidate).toHaveBeenCalledWith({ accessToken: 'foo' }); expect(authenticationResult.redirected()).toBe(true); - expect(authenticationResult.redirectURL).toBe('/logged_out'); + expect(authenticationResult.redirectURL).toBe('/mock-server-basepath/logged_out'); }); }); }); diff --git a/x-pack/plugins/security/server/authentication/providers/pki.ts b/x-pack/plugins/security/server/authentication/providers/pki.ts index fa3e1959ba7de1..c7d431422a2485 100644 --- a/x-pack/plugins/security/server/authentication/providers/pki.ts +++ b/x-pack/plugins/security/server/authentication/providers/pki.ts @@ -111,7 +111,7 @@ export class PKIAuthenticationProvider extends BaseAuthenticationProvider { return DeauthenticationResult.failed(err); } - return DeauthenticationResult.redirectTo('/logged_out'); + return DeauthenticationResult.redirectTo(`${this.options.basePath.serverBasePath}/logged_out`); } /** diff --git a/x-pack/plugins/security/server/authentication/providers/saml.test.ts b/x-pack/plugins/security/server/authentication/providers/saml.test.ts index 7ef1d934a7d130..27702f70865ea6 100644 --- a/x-pack/plugins/security/server/authentication/providers/saml.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/saml.test.ts @@ -1045,7 +1045,7 @@ describe('SAMLAuthenticationProvider', () => { }); expect(authenticationResult.redirected()).toBe(true); - expect(authenticationResult.redirectURL).toBe('/logged_out'); + expect(authenticationResult.redirectURL).toBe('/mock-server-basepath/logged_out'); }); it('redirects to /logged_out if `redirect` field in SAML logout response is not defined.', async () => { @@ -1069,7 +1069,7 @@ describe('SAMLAuthenticationProvider', () => { }); expect(authenticationResult.redirected()).toBe(true); - expect(authenticationResult.redirectURL).toBe('/logged_out'); + expect(authenticationResult.redirectURL).toBe('/mock-server-basepath/logged_out'); }); it('relies on SAML logout if query string is not empty, but does not include SAMLRequest.', async () => { @@ -1095,7 +1095,7 @@ describe('SAMLAuthenticationProvider', () => { }); expect(authenticationResult.redirected()).toBe(true); - expect(authenticationResult.redirectURL).toBe('/logged_out'); + expect(authenticationResult.redirectURL).toBe('/mock-server-basepath/logged_out'); }); it('relies on SAML invalidate call even if access token is presented.', async () => { @@ -1119,7 +1119,7 @@ describe('SAMLAuthenticationProvider', () => { ); expect(authenticationResult.redirected()).toBe(true); - expect(authenticationResult.redirectURL).toBe('/logged_out'); + expect(authenticationResult.redirectURL).toBe('/mock-server-basepath/logged_out'); }); it('redirects to /logged_out if `redirect` field in SAML invalidate response is null.', async () => { @@ -1139,7 +1139,7 @@ describe('SAMLAuthenticationProvider', () => { ); expect(authenticationResult.redirected()).toBe(true); - expect(authenticationResult.redirectURL).toBe('/logged_out'); + expect(authenticationResult.redirectURL).toBe('/mock-server-basepath/logged_out'); }); it('redirects to /logged_out if `redirect` field in SAML invalidate response is not defined.', async () => { @@ -1159,7 +1159,7 @@ describe('SAMLAuthenticationProvider', () => { ); expect(authenticationResult.redirected()).toBe(true); - expect(authenticationResult.redirectURL).toBe('/logged_out'); + expect(authenticationResult.redirectURL).toBe('/mock-server-basepath/logged_out'); }); it('redirects user to the IdP if SLO is supported by IdP in case of SP initiated logout.', async () => { diff --git a/x-pack/plugins/security/server/authentication/providers/saml.ts b/x-pack/plugins/security/server/authentication/providers/saml.ts index a8683796293afe..faa19239fcc3b1 100644 --- a/x-pack/plugins/security/server/authentication/providers/saml.ts +++ b/x-pack/plugins/security/server/authentication/providers/saml.ts @@ -228,7 +228,9 @@ export class SAMLAuthenticationProvider extends BaseAuthenticationProvider { return DeauthenticationResult.redirectTo(redirect); } - return DeauthenticationResult.redirectTo('/logged_out'); + return DeauthenticationResult.redirectTo( + `${this.options.basePath.serverBasePath}/logged_out` + ); } catch (err) { this.logger.debug(`Failed to deauthenticate user: ${err.message}`); return DeauthenticationResult.failed(err); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 5b59036b89f49b..8312ba3d0c83fc 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3359,7 +3359,6 @@ "xpack.apm.errorsTable.occurrencesColumnLabel": "オカレンス", "xpack.apm.errorsTable.unhandledLabel": "未対応", "xpack.apm.featureRegistry.apmFeatureName": "APM", - "xpack.apm.feedbackMenu.provideFeedbackTitle": "APM のフィードバックを提供", "xpack.apm.filter.environment.allLabel": "すべて", "xpack.apm.filter.environment.label": "環境", "xpack.apm.filter.environment.notDefinedLabel": "未定義", @@ -5845,7 +5844,6 @@ "xpack.infra.homePage.noMetricsIndicesTitle": "メトリックインデックスがないようです。", "xpack.infra.homePage.toolbar.kqlSearchFieldPlaceholder": "インフラストラクチャーデータを検索… (例: host.name:host-1)", "xpack.infra.homePage.toolbar.showingLastOneMinuteDataText": "指定期間の最後の 1 分間のデータを表示中", - "xpack.infra.infrastructure.infrastructureHelpContent.feedbackLinkText": "インフラストラクチャーのフィードバックを提供", "xpack.infra.infrastructureDescription": "インフラストラクチャーを閲覧します", "xpack.infra.infrastructureMetricsExplorerPage.documentTitle": "{previousTitle} | メトリックエクスプローラー", "xpack.infra.infrastructureSnapshotPage.documentTitle": "{previousTitle} | インベントリ", @@ -5896,7 +5894,6 @@ "xpack.infra.logs.stopStreamingButtonLabel": "ストリーム停止", "xpack.infra.logs.streamingDescription": "新しいエントリーをストリーム中...", "xpack.infra.logs.streamingNewEntriesText": "新しいエントリーをストリーム中", - "xpack.infra.logsPage.logsHelpContent.feedbackLinkText": "ログのフィードバックを提供", "xpack.infra.logsPage.noLoggingIndicesDescription": "追加しましょう!", "xpack.infra.logsPage.noLoggingIndicesInstructionsActionLabel": "セットアップの手順を表示", "xpack.infra.logsPage.noLoggingIndicesTitle": "ログインデックスがないようです。", @@ -6420,8 +6417,6 @@ "xpack.maps.heatmap.colorRampLabel": "色の範囲", "xpack.maps.heatmapLegend.coldLabel": "コールド", "xpack.maps.heatmapLegend.hotLabel": "ホット", - "xpack.maps.helpMenu.docLabel": "Maps ドキュメンテーション", - "xpack.maps.helpMenu.feedbackLinkText": "Maps アプリケーションに関するフィードバックを提供", "xpack.maps.inspector.centerLatLabel": "中央緯度", "xpack.maps.inspector.centerLonLabel": "中央経度", "xpack.maps.inspector.mapboxStyleTitle": "マップボックススタイル", @@ -10367,8 +10362,6 @@ "xpack.siem.certificate.fingerprint.clientCertLabel": "クライアント証明書", "xpack.siem.certificate.fingerprint.serverCertLabel": "サーバー証明書", "xpack.siem.chart.dataNotAvailableTitle": "チャートデータが利用できません", - "xpack.siem.chrome.help.feedback": "フィードバックを送信", - "xpack.siem.chrome.help.title": "SIEM アプリケーションのヘルプ", "xpack.siem.clipboard.copied": "コピー完了", "xpack.siem.clipboard.copy": "コピー", "xpack.siem.clipboard.to.the.clipboard": "クリップボードに", @@ -11799,8 +11792,6 @@ "xpack.uptime.filterBar.options.location.name": "場所", "xpack.uptime.filterBar.options.portLabel": "ポート", "xpack.uptime.filterBar.options.schemeLabel": "スキーム", - "xpack.uptime.header.helpLinkAriaLabel": "ディスカッションページへ移動", - "xpack.uptime.header.helpLinkText": "アップタイムのフィードバックを提供", "xpack.uptime.integrationLink.missingDataMessage": "この統合に必要なデータが見つかりませんでした。", "xpack.uptime.monitorCharts.checkStatus.series.downCountLabel": "ダウンカウント", "xpack.uptime.monitorCharts.checkStatus.series.upCountLabel": "アップカウント", @@ -11862,8 +11853,6 @@ "xpack.uptime.snapshotHistogram.series.upLabel": "アップ", "xpack.uptime.uptimeFeatureCatalogueTitle": "起動時間", "xpack.uptime.emptyState.noDataMessage": "アップタイムデータが見つかりませんでした", - "xpack.uptime.header.docsLinkAriaLabel": "アップタイムドキュメンテーションに移動", - "xpack.uptime.header.documentationLinkText": "アップタイムドキュメンテーション", "xpack.uptime.pingList.collapseRow": "縮小", "xpack.uptime.pingList.durationMsColumnFormatting": "{millis}ミリ秒", "xpack.uptime.pingList.expandedRow.bodySize": "本文サイズは {bodyBytes} です。", @@ -12666,8 +12655,6 @@ "xpack.lens.functions.mergeTables.help": "いくつかの Kibana 表を 1 つの表に結合するのをアシストします", "xpack.lens.functions.renameColumns.help": "データベースの列の名前の変更をアシストします", "xpack.lens.functions.renameColumns.idMap.help": "キーが古い列 ID で値が対応する新しい列 ID となるように JSON エンコーディングされたオブジェクトです。他の列 ID はすべてのそのままです。", - "xpack.lens.helpMenu.docLabel": "レンズドキュメンテーション", - "xpack.lens.helpMenu.feedbackLinkText": "レンズアプリケーションに関するフィードバックを提供", "xpack.lens.indexPattern.avg": "平均", "xpack.lens.indexPattern.avgOf": "{name} の平均", "xpack.lens.indexPattern.cardinality": "ユニークカウント", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 2b3c788125e6ab..f90d2f9522c311 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3360,7 +3360,6 @@ "xpack.apm.errorsTable.occurrencesColumnLabel": "发生次数", "xpack.apm.errorsTable.unhandledLabel": "未处理", "xpack.apm.featureRegistry.apmFeatureName": "APM", - "xpack.apm.feedbackMenu.provideFeedbackTitle": "提供 APM 的反馈", "xpack.apm.filter.environment.allLabel": "全部", "xpack.apm.filter.environment.label": "环境", "xpack.apm.filter.environment.notDefinedLabel": "未定义", @@ -5847,7 +5846,6 @@ "xpack.infra.homePage.noMetricsIndicesTitle": "似乎您没有任何指标索引。", "xpack.infra.homePage.toolbar.kqlSearchFieldPlaceholder": "搜索基础设施数据……(例如 host.name:host-1)", "xpack.infra.homePage.toolbar.showingLastOneMinuteDataText": "在选定时间显示过去 1 分钟的数据", - "xpack.infra.infrastructure.infrastructureHelpContent.feedbackLinkText": "提供 Infrastructure 的反馈", "xpack.infra.infrastructureDescription": "浏览您的基础设施", "xpack.infra.infrastructureMetricsExplorerPage.documentTitle": "{previousTitle} | 指标浏览器", "xpack.infra.infrastructureSnapshotPage.documentTitle": "{previousTitle} | 库存", @@ -5898,7 +5896,6 @@ "xpack.infra.logs.stopStreamingButtonLabel": "停止流式传输", "xpack.infra.logs.streamingDescription": "正在流式传输新条目……", "xpack.infra.logs.streamingNewEntriesText": "正在流式传输新条目", - "xpack.infra.logsPage.logsHelpContent.feedbackLinkText": "提供 Logs 的反馈", "xpack.infra.logsPage.noLoggingIndicesDescription": "让我们添加一些!", "xpack.infra.logsPage.noLoggingIndicesInstructionsActionLabel": "查看设置说明", "xpack.infra.logsPage.noLoggingIndicesTitle": "似乎您没有任何日志索引。", @@ -6422,8 +6419,6 @@ "xpack.maps.heatmap.colorRampLabel": "颜色范围", "xpack.maps.heatmapLegend.coldLabel": "冷", "xpack.maps.heatmapLegend.hotLabel": "热", - "xpack.maps.helpMenu.docLabel": "地图文档", - "xpack.maps.helpMenu.feedbackLinkText": "提供 Maps 应用程序的反馈", "xpack.maps.inspector.centerLatLabel": "中心纬度", "xpack.maps.inspector.centerLonLabel": "中心经度", "xpack.maps.inspector.mapboxStyleTitle": "Mapbox 样式", @@ -10457,8 +10452,6 @@ "xpack.siem.certificate.fingerprint.clientCertLabel": "客户端证书", "xpack.siem.certificate.fingerprint.serverCertLabel": "服务器证书", "xpack.siem.chart.dataNotAvailableTitle": "图表数据不可用", - "xpack.siem.chrome.help.feedback": "提交反馈", - "xpack.siem.chrome.help.title": "SIEM 应用程序帮助", "xpack.siem.clipboard.copied": "已复制", "xpack.siem.clipboard.copy": "复制", "xpack.siem.clipboard.to.the.clipboard": "至剪贴板", @@ -11889,8 +11882,6 @@ "xpack.uptime.filterBar.options.location.name": "位置", "xpack.uptime.filterBar.options.portLabel": "端口", "xpack.uptime.filterBar.options.schemeLabel": "方案", - "xpack.uptime.header.helpLinkAriaLabel": "前往我们的讨论页", - "xpack.uptime.header.helpLinkText": "提供运行时间反馈", "xpack.uptime.integrationLink.missingDataMessage": "未找到此集成的所需数据。", "xpack.uptime.monitorCharts.checkStatus.series.downCountLabel": "关闭计数", "xpack.uptime.monitorCharts.checkStatus.series.upCountLabel": "运行计数", @@ -11952,8 +11943,6 @@ "xpack.uptime.snapshotHistogram.series.upLabel": "运行", "xpack.uptime.uptimeFeatureCatalogueTitle": "运行时间", "xpack.uptime.emptyState.noDataMessage": "未找到任何运行时间数据", - "xpack.uptime.header.docsLinkAriaLabel": "前往 Uptime 文档", - "xpack.uptime.header.documentationLinkText": "Uptime 文档", "xpack.uptime.pingList.collapseRow": "折叠", "xpack.uptime.pingList.durationMsColumnFormatting": "{millis} 毫秒", "xpack.uptime.pingList.expandedRow.bodySize": "正文大小为 {bodyBytes}。", @@ -12756,8 +12745,6 @@ "xpack.lens.functions.mergeTables.help": "将任何数目的 kibana 表合并成单个表的助手", "xpack.lens.functions.renameColumns.help": "用于重命名数据表列的助手", "xpack.lens.functions.renameColumns.idMap.help": "旧列 ID 为键且相应新列 ID 为值的 JSON 编码对象。所有其他列 ID 都将保留。", - "xpack.lens.helpMenu.docLabel": "Lens 文档", - "xpack.lens.helpMenu.feedbackLinkText": "提供 Lens 应用程序的反馈", "xpack.lens.indexPattern.avg": "平均值", "xpack.lens.indexPattern.avgOf": "{name} 的平均值", "xpack.lens.indexPattern.cardinality": "唯一计数", diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts index b9b40be3a04b3e..648944a9256b29 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts @@ -12,7 +12,7 @@ const ES_TEST_INDEX_NAME = 'functional-test-actions-index'; // eslint-disable-next-line import/no-default-export export default function indexTest({ getService }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts index 44970a9260c42f..0c05ad3e3e68aa 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/execute.ts @@ -18,7 +18,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - const es = getService('es'); + const es = getService('legacyEs'); const retry = getService('retry'); const esTestIndexTool = new ESTestIndexTool(es, retry); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts index 0a300c4ce65da4..c43e159bbe8ca5 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts @@ -20,7 +20,7 @@ import { // eslint-disable-next-line import/no-default-export export default function alertTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const retry = getService('retry'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const esTestIndexTool = new ESTestIndexTool(es, retry); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts index bd0226d024d1fd..d94556d6cedda3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts @@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createAlertTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const supertestWithoutAuth = getService('supertestWithoutAuth'); describe('create', () => { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts index 6d5147e9f87b81..aab683df097404 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts @@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createDeleteTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const supertestWithoutAuth = getService('supertestWithoutAuth'); describe('delete', () => { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts index 8a9b7e3fc35c44..d2076e0f92b3c3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createDisableAlertTests({ getService }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts index 543805fb83b189..528db61dba21ca 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createEnableAlertTests({ getService }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts index 74a1255dacfe59..7e971d033b5c42 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts @@ -12,7 +12,7 @@ const ES_TEST_INDEX_NAME = 'functional-test-actions-index'; // eslint-disable-next-line import/no-default-export export default function indexTest({ getService }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts index ba8a5aa9160a53..e97b6d480c4703 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts @@ -17,7 +17,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const retry = getService('retry'); const esTestIndexTool = new ESTestIndexTool(es, retry); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts.ts index badec079d68285..28634c46b6350a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts.ts @@ -19,7 +19,7 @@ import { // eslint-disable-next-line import/no-default-export export default function alertTests({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); - const es = getService('es'); + const es = getService('legacyEs'); const retry = getService('retry'); const esTestIndexTool = new ESTestIndexTool(es, retry); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts index 17ec83b5b4f18a..80459690af7329 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts @@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createAlertTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); describe('create', () => { const objectRemover = new ObjectRemover(supertest); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts index 3ef501cfaa5887..3aea982f948ea2 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts @@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createDeleteTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); describe('delete', () => { const objectRemover = new ObjectRemover(supertest); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts index 664e74835d4158..750f94201216a7 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createDisableAlertTests({ getService }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const supertestWithoutAuth = getService('supertestWithoutAuth'); describe('disable', () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts index 2a8de1f6e31c37..00cd40c0e80cd5 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function createEnableAlertTests({ getService }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const supertestWithoutAuth = getService('supertestWithoutAuth'); describe('enable', () => { diff --git a/x-pack/test/api_integration/apis/beats/assign_tags_to_beats.js b/x-pack/test/api_integration/apis/beats/assign_tags_to_beats.js index 27435d49c252fa..e4637d3807d4da 100644 --- a/x-pack/test/api_integration/apis/beats/assign_tags_to_beats.js +++ b/x-pack/test/api_integration/apis/beats/assign_tags_to_beats.js @@ -10,7 +10,7 @@ import { ES_INDEX_NAME } from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const randomness = getService('randomness'); describe('assign_tags_to_beats', () => { diff --git a/x-pack/test/api_integration/apis/beats/create_enrollment_tokens.js b/x-pack/test/api_integration/apis/beats/create_enrollment_tokens.js index 6cd4fdf22bb41f..09cfb33e4fad24 100644 --- a/x-pack/test/api_integration/apis/beats/create_enrollment_tokens.js +++ b/x-pack/test/api_integration/apis/beats/create_enrollment_tokens.js @@ -10,7 +10,7 @@ import { ES_INDEX_NAME } from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); describe('create_enrollment_token', () => { it('should create one token by default', async () => { diff --git a/x-pack/test/api_integration/apis/beats/enroll_beat.js b/x-pack/test/api_integration/apis/beats/enroll_beat.js index 4b4767e1d98497..59c42db7c1f81b 100644 --- a/x-pack/test/api_integration/apis/beats/enroll_beat.js +++ b/x-pack/test/api_integration/apis/beats/enroll_beat.js @@ -12,7 +12,7 @@ import { ES_INDEX_NAME } from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); const randomness = getService('randomness'); - const es = getService('es'); + const es = getService('legacyEs'); describe('enroll_beat', () => { let validEnrollmentToken; diff --git a/x-pack/test/api_integration/apis/beats/get_beat.js b/x-pack/test/api_integration/apis/beats/get_beat.js index 07cc056e3af990..03667a53920c96 100644 --- a/x-pack/test/api_integration/apis/beats/get_beat.js +++ b/x-pack/test/api_integration/apis/beats/get_beat.js @@ -10,7 +10,7 @@ import { ES_INDEX_NAME } from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); describe('get_beat_configuration', () => { const archive = 'beats/list'; diff --git a/x-pack/test/api_integration/apis/beats/index.js b/x-pack/test/api_integration/apis/beats/index.js index da47fdbf77fc7a..8ca7390ad5b1fc 100644 --- a/x-pack/test/api_integration/apis/beats/index.js +++ b/x-pack/test/api_integration/apis/beats/index.js @@ -7,7 +7,7 @@ import { ES_INDEX_NAME } from './constants'; export default function ({ getService, loadTestFile }) { - const es = getService('es'); + const es = getService('legacyEs'); describe('beats', () => { const cleanup = () => diff --git a/x-pack/test/api_integration/apis/beats/remove_tags_from_beats.js b/x-pack/test/api_integration/apis/beats/remove_tags_from_beats.js index 1548aff1182b33..dde8916dd24d5a 100644 --- a/x-pack/test/api_integration/apis/beats/remove_tags_from_beats.js +++ b/x-pack/test/api_integration/apis/beats/remove_tags_from_beats.js @@ -10,7 +10,7 @@ import { ES_INDEX_NAME } from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const randomness = getService('randomness'); describe('remove_tags_from_beats', () => { diff --git a/x-pack/test/api_integration/apis/beats/set_config.js b/x-pack/test/api_integration/apis/beats/set_config.js index 5e15145cf47c8e..21a09333dc31a2 100644 --- a/x-pack/test/api_integration/apis/beats/set_config.js +++ b/x-pack/test/api_integration/apis/beats/set_config.js @@ -9,7 +9,7 @@ import { ES_INDEX_NAME } from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); describe('set_config', () => { diff --git a/x-pack/test/api_integration/apis/beats/set_tag.js b/x-pack/test/api_integration/apis/beats/set_tag.js index ee9a601b25096c..630c3772b1661a 100644 --- a/x-pack/test/api_integration/apis/beats/set_tag.js +++ b/x-pack/test/api_integration/apis/beats/set_tag.js @@ -9,7 +9,7 @@ import { ES_INDEX_NAME } from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); describe('set_tag', () => { it('should create a tag', async () => { diff --git a/x-pack/test/api_integration/apis/beats/update_beat.js b/x-pack/test/api_integration/apis/beats/update_beat.js index 228d651a590e6e..82582b553886c5 100644 --- a/x-pack/test/api_integration/apis/beats/update_beat.js +++ b/x-pack/test/api_integration/apis/beats/update_beat.js @@ -11,7 +11,7 @@ import moment from 'moment'; export default function ({ getService }) { const supertest = getService('supertest'); const randomness = getService('randomness'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); describe('update_beat', () => { diff --git a/x-pack/test/api_integration/apis/es/has_privileges.js b/x-pack/test/api_integration/apis/es/has_privileges.js index 5cbbefd8f2f370..37d6d734995524 100644 --- a/x-pack/test/api_integration/apis/es/has_privileges.js +++ b/x-pack/test/api_integration/apis/es/has_privileges.js @@ -11,7 +11,7 @@ export default function ({ getService }) { describe('has_privileges', () => { before(async () => { - const es = getService('es'); + const es = getService('legacyEs'); await es.shield.postPrivileges({ body: { @@ -104,7 +104,7 @@ export default function ({ getService }) { }); // Create privilege - const es = getService('es'); + const es = getService('legacyEs'); await es.shield.postPrivileges({ body: { [application]: { diff --git a/x-pack/test/api_integration/apis/es/post_privileges.js b/x-pack/test/api_integration/apis/es/post_privileges.js index 4b1695487f832c..1c8f723b7a278a 100644 --- a/x-pack/test/api_integration/apis/es/post_privileges.js +++ b/x-pack/test/api_integration/apis/es/post_privileges.js @@ -9,7 +9,7 @@ export default function ({ getService }) { describe('post_privileges', () => { it('should allow privileges to be updated', async () => { - const es = getService('es'); + const es = getService('legacyEs'); const application = 'foo'; const response = await es.shield.postPrivileges({ body: { diff --git a/x-pack/test/api_integration/apis/index.js b/x-pack/test/api_integration/apis/index.js index 86ef4458990390..ca339e9f407f22 100644 --- a/x-pack/test/api_integration/apis/index.js +++ b/x-pack/test/api_integration/apis/index.js @@ -27,5 +27,6 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./siem')); loadTestFile(require.resolve('./short_urls')); loadTestFile(require.resolve('./lens')); + loadTestFile(require.resolve('./licensing')); }); } diff --git a/x-pack/test/api_integration/apis/lens/telemetry.ts b/x-pack/test/api_integration/apis/lens/telemetry.ts index 6428ef9f478d4a..5e6830c8f4689b 100644 --- a/x-pack/test/api_integration/apis/lens/telemetry.ts +++ b/x-pack/test/api_integration/apis/lens/telemetry.ts @@ -22,7 +22,7 @@ const COMMON_HEADERS = { // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es: Client = getService('es'); + const es: Client = getService('legacyEs'); const callCluster: CallCluster = (((path: 'search', searchParams: SearchParams) => { return es[path].call(es, searchParams); }) as unknown) as CallCluster; diff --git a/x-pack/test/api_integration/apis/licensing/index.ts b/x-pack/test/api_integration/apis/licensing/index.ts new file mode 100644 index 00000000000000..f14d5102f6f4e2 --- /dev/null +++ b/x-pack/test/api_integration/apis/licensing/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function licensingIntegrationTests({ loadTestFile }: FtrProviderContext) { + describe('Licensing', () => { + loadTestFile(require.resolve('./info')); + }); +} diff --git a/x-pack/test/api_integration/apis/licensing/info.ts b/x-pack/test/api_integration/apis/licensing/info.ts new file mode 100644 index 00000000000000..0b48080616fb98 --- /dev/null +++ b/x-pack/test/api_integration/apis/licensing/info.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('Info', () => { + describe('GET /api/licensing/info', () => { + it('returns licensing information', async () => { + const response = await supertest.get('/api/licensing/info').expect(200); + + expect(response.body).property('features'); + expect(response.body).property('license'); + expect(response.body).property('signature'); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/logstash/cluster/load.js b/x-pack/test/api_integration/apis/logstash/cluster/load.js index a20c524f0a8f8a..36b992559e15b8 100644 --- a/x-pack/test/api_integration/apis/logstash/cluster/load.js +++ b/x-pack/test/api_integration/apis/logstash/cluster/load.js @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); describe('load', () => { it('should return the ES cluster info', async () => { diff --git a/x-pack/test/api_integration/apis/management/cross_cluster_replication/follower_indices.js b/x-pack/test/api_integration/apis/management/cross_cluster_replication/follower_indices.js index 8ed10ccf31dce8..cd128d92498cf5 100644 --- a/x-pack/test/api_integration/apis/management/cross_cluster_replication/follower_indices.js +++ b/x-pack/test/api_integration/apis/management/cross_cluster_replication/follower_indices.js @@ -14,7 +14,7 @@ import { registerHelpers as registerFollowerIndicesnHelpers } from './follower_i export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { addCluster, deleteAllClusters } = registerRemoteClustersHelpers(supertest); const { diff --git a/x-pack/test/api_integration/apis/management/index_lifecycle_management/indices.js b/x-pack/test/api_integration/apis/management/index_lifecycle_management/indices.js index d0d08101e54448..020d6fc5741b9d 100644 --- a/x-pack/test/api_integration/apis/management/index_lifecycle_management/indices.js +++ b/x-pack/test/api_integration/apis/management/index_lifecycle_management/indices.js @@ -13,7 +13,7 @@ import { getPolicyPayload } from './fixtures'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { getIndex, diff --git a/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js b/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js index 5ce1d7d956d671..bc8b2af4014232 100644 --- a/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js +++ b/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js @@ -13,7 +13,7 @@ import { initElasticsearchHelpers } from './lib'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { getNodesStats } = initElasticsearchHelpers(es); const { loadNodes, getNodeDetails } = registerHelpers({ supertest }); diff --git a/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js b/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js index c13ae4a15c97ef..598db2ddc8a650 100644 --- a/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js +++ b/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js @@ -15,7 +15,7 @@ import { DEFAULT_POLICY_NAME } from './constants'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { createIndex, diff --git a/x-pack/test/api_integration/apis/management/index_lifecycle_management/templates.js b/x-pack/test/api_integration/apis/management/index_lifecycle_management/templates.js index 7dcf3c995e3cea..5c6e3d0e89c81d 100644 --- a/x-pack/test/api_integration/apis/management/index_lifecycle_management/templates.js +++ b/x-pack/test/api_integration/apis/management/index_lifecycle_management/templates.js @@ -12,7 +12,7 @@ import { registerHelpers as registerPoliciesHelpers } from './policies.helpers'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { createIndexTemplate, cleanUp: cleanUpEsResources } = initElasticsearchHelpers(es); diff --git a/x-pack/test/api_integration/apis/management/index_management/indices.js b/x-pack/test/api_integration/apis/management/index_management/indices.js index 09215eff20c692..9302b0fe0e16b2 100644 --- a/x-pack/test/api_integration/apis/management/index_management/indices.js +++ b/x-pack/test/api_integration/apis/management/index_management/indices.js @@ -11,7 +11,7 @@ import { registerHelpers } from './indices.helpers'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { createIndex, diff --git a/x-pack/test/api_integration/apis/management/index_management/mapping.js b/x-pack/test/api_integration/apis/management/index_management/mapping.js index 91f995a7d80453..0a2713e9407f1c 100644 --- a/x-pack/test/api_integration/apis/management/index_management/mapping.js +++ b/x-pack/test/api_integration/apis/management/index_management/mapping.js @@ -11,7 +11,7 @@ import { registerHelpers } from './mapping.helpers'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { createIndex, diff --git a/x-pack/test/api_integration/apis/management/index_management/settings.js b/x-pack/test/api_integration/apis/management/index_management/settings.js index dc41f530085b17..d9a9e19fe2490b 100644 --- a/x-pack/test/api_integration/apis/management/index_management/settings.js +++ b/x-pack/test/api_integration/apis/management/index_management/settings.js @@ -11,7 +11,7 @@ import { registerHelpers } from './settings.helpers'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { createIndex, diff --git a/x-pack/test/api_integration/apis/management/index_management/stats.js b/x-pack/test/api_integration/apis/management/index_management/stats.js index 58dac9f5b911ac..743b1f596e9d76 100644 --- a/x-pack/test/api_integration/apis/management/index_management/stats.js +++ b/x-pack/test/api_integration/apis/management/index_management/stats.js @@ -11,7 +11,7 @@ import { registerHelpers } from './stats.helpers'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { createIndex, diff --git a/x-pack/test/api_integration/apis/management/index_management/templates.js b/x-pack/test/api_integration/apis/management/index_management/templates.js index a6a74932184993..00a97e55c013c0 100644 --- a/x-pack/test/api_integration/apis/management/index_management/templates.js +++ b/x-pack/test/api_integration/apis/management/index_management/templates.js @@ -11,7 +11,7 @@ import { registerHelpers } from './templates.helpers'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { cleanUp: cleanUpEsResources, diff --git a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js index 25bc503682bc7f..78a04b729ba661 100644 --- a/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js +++ b/x-pack/test/api_integration/apis/management/rollup/index_patterns_extensions.js @@ -13,7 +13,7 @@ import { getRandomString } from './lib'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { createIndexWithMappings, diff --git a/x-pack/test/api_integration/apis/management/rollup/rollup.js b/x-pack/test/api_integration/apis/management/rollup/rollup.js index 5ac4e06adb23a9..6e99af2ba8e329 100644 --- a/x-pack/test/api_integration/apis/management/rollup/rollup.js +++ b/x-pack/test/api_integration/apis/management/rollup/rollup.js @@ -11,7 +11,7 @@ import { registerHelpers } from './rollup.test_helpers'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { createIndexWithMappings, diff --git a/x-pack/test/api_integration/apis/management/rollup/rollup_search.js b/x-pack/test/api_integration/apis/management/rollup/rollup_search.js index 2a38cd563f3128..073473f202fa31 100644 --- a/x-pack/test/api_integration/apis/management/rollup/rollup_search.js +++ b/x-pack/test/api_integration/apis/management/rollup/rollup_search.js @@ -12,7 +12,7 @@ import { getRandomString } from './lib'; export default function ({ getService }) { const supertest = getService('supertest'); - const es = getService('es'); + const es = getService('legacyEs'); const { createIndexWithMappings, diff --git a/x-pack/test/api_integration/apis/monitoring/common/mappings_exist.js b/x-pack/test/api_integration/apis/monitoring/common/mappings_exist.js index 6d3dbe68c77ace..2443448ccd0621 100644 --- a/x-pack/test/api_integration/apis/monitoring/common/mappings_exist.js +++ b/x-pack/test/api_integration/apis/monitoring/common/mappings_exist.js @@ -13,7 +13,7 @@ import * as beatsMetrics from '../../../../../legacy/plugins/monitoring/server/l import * as apmMetrics from '../../../../../legacy/plugins/monitoring/server/lib/metrics/apm/metrics'; export default function ({ getService }) { - const es = getService('es'); + const es = getService('legacyEs'); const metricSets = [ { diff --git a/x-pack/test/api_integration/apis/security/roles.js b/x-pack/test/api_integration/apis/security/roles.js index d1d4c3c7b7af88..7108656783f526 100644 --- a/x-pack/test/api_integration/apis/security/roles.js +++ b/x-pack/test/api_integration/apis/security/roles.js @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; export default function ({ getService }) { - const es = getService('es'); + const es = getService('legacyEs'); const supertest = getService('supertest'); const config = getService('config'); const basic = config.get('esTestCluster.license') === 'basic'; diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts b/x-pack/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts index de03fff7edcf70..582864795015cb 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts +++ b/x-pack/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts @@ -10,7 +10,7 @@ import { TelemetrySavedObjectAttributes } from '../../../../../src/legacy/core_p import { FtrProviderContext } from '../../ftr_provider_context'; export default function optInTest({ getService }: FtrProviderContext) { - const client: Client = getService('es'); + const client: Client = getService('legacyEs'); const supertest = getService('supertest'); describe('/api/telemetry/v2/optIn API Telemetry User has seen OptIn Notice', () => { diff --git a/x-pack/test/api_integration/apis/uptime/graphql/monitor_states.ts b/x-pack/test/api_integration/apis/uptime/graphql/monitor_states.ts index 9dca3f40c63034..c305bb99c28f79 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/monitor_states.ts +++ b/x-pack/test/api_integration/apis/uptime/graphql/monitor_states.ts @@ -106,7 +106,7 @@ export default function({ getService }: FtrProviderContext) { before(async () => { const index = 'heartbeat-8.0.0'; - const es = getService('es'); + const es = getService('legacyEs'); dateRangeStart = new Date().toISOString(); checks = await makeChecks(es, index, testMonitorId, 1, numIps, {}, d => { if (d.summary) { diff --git a/x-pack/test/api_integration/apis/uptime/index.js b/x-pack/test/api_integration/apis/uptime/index.js index 8f4e4ab9a7ea10..6eb77fb584133d 100644 --- a/x-pack/test/api_integration/apis/uptime/index.js +++ b/x-pack/test/api_integration/apis/uptime/index.js @@ -5,7 +5,7 @@ */ export default function ({ getService, loadTestFile }) { - const es = getService('es'); + const es = getService('legacyEs'); describe('uptime', () => { before(() => diff --git a/x-pack/test/api_integration/services/index.ts b/x-pack/test/api_integration/services/index.ts index 7e3b747c81993a..4be89172e24f0b 100644 --- a/x-pack/test/api_integration/services/index.ts +++ b/x-pack/test/api_integration/services/index.ts @@ -30,7 +30,7 @@ export const services = { esSupertest: kibanaApiIntegrationServices.esSupertest, supertest: kibanaApiIntegrationServices.supertest, - es: LegacyEsProvider, + legacyEs: LegacyEsProvider, esSupertestWithoutAuth: EsSupertestWithoutAuthProvider, infraOpsGraphQLClient: InfraOpsGraphQLClientProvider, infraOpsGraphQLClientFactory: InfraOpsGraphQLClientFactoryProvider, diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 67bc8bd38ff1c9..7cbab3cdcf4f2e 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -25,9 +25,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const savedQueryManagementComponent = getService('savedQueryManagementComponent'); async function setDiscoverTimeRange() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); } describe('security', () => { diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts index 243d5a0b6b6d0d..4c2a8b4bed3061 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts @@ -21,9 +21,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const appsMenu = getService('appsMenu'); async function setDiscoverTimeRange() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); } describe('spaces', () => { diff --git a/x-pack/test/functional/apps/monitoring/beats/beat_detail.js b/x-pack/test/functional/apps/monitoring/beats/beat_detail.js index d352579e011604..d17e233a484e88 100644 --- a/x-pack/test/functional/apps/monitoring/beats/beat_detail.js +++ b/x-pack/test/functional/apps/monitoring/beats/beat_detail.js @@ -17,8 +17,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/beats', { - from: '2017-12-19 17:14:09.000', - to: '2017-12-19 18:15:09.000', + from: 'Dec 19, 2017 @ 17:14:09.000', + to: 'Dec 19, 2017 @ 18:15:09.000', }); // go to beats detail diff --git a/x-pack/test/functional/apps/monitoring/beats/cluster.js b/x-pack/test/functional/apps/monitoring/beats/cluster.js index 8ee93066254d58..9a2532adfbae88 100644 --- a/x-pack/test/functional/apps/monitoring/beats/cluster.js +++ b/x-pack/test/functional/apps/monitoring/beats/cluster.js @@ -15,8 +15,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/beats', { - from: '2017-12-19 17:14:09.000', - to: '2017-12-19 18:15:09.000', + from: 'Dec 19, 2017 @ 17:14:09.000', + to: 'Dec 19, 2017 @ 18:15:09.000', }); }); diff --git a/x-pack/test/functional/apps/monitoring/beats/listing.js b/x-pack/test/functional/apps/monitoring/beats/listing.js index 700b5d593ecb8e..7a27a7b5f219d3 100644 --- a/x-pack/test/functional/apps/monitoring/beats/listing.js +++ b/x-pack/test/functional/apps/monitoring/beats/listing.js @@ -17,8 +17,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/beats', { - from: '2017-12-19 17:14:09.000', - to: '2017-12-19 18:15:09.000', + from: 'Dec 19, 2017 @ 17:14:09.000', + to: 'Dec 19, 2017 @ 18:15:09.000', }); // go to beats listing diff --git a/x-pack/test/functional/apps/monitoring/beats/overview.js b/x-pack/test/functional/apps/monitoring/beats/overview.js index 1c8b70a4628433..16c198be0432f1 100644 --- a/x-pack/test/functional/apps/monitoring/beats/overview.js +++ b/x-pack/test/functional/apps/monitoring/beats/overview.js @@ -17,8 +17,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/beats', { - from: '2017-12-19 17:14:09.000', - to: '2017-12-19 18:15:09.000', + from: 'Dec 19, 2017 @ 17:14:09.000', + to: 'Dec 19, 2017 @ 18:15:09.000', }); // go to beats overview diff --git a/x-pack/test/functional/apps/monitoring/cluster/alerts.js b/x-pack/test/functional/apps/monitoring/cluster/alerts.js index 1e61518947d2ea..2a0dbec6f3b9ea 100644 --- a/x-pack/test/functional/apps/monitoring/cluster/alerts.js +++ b/x-pack/test/functional/apps/monitoring/cluster/alerts.js @@ -23,8 +23,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-yellow-platinum', { - from: '2017-08-29 17:23:47.528', - to: '2017-08-29 17:25:50.701', + from: 'Aug 29, 2017 @ 17:23:47.528', + to: 'Aug 29, 2017 @ 17:25:50.701', }); // ensure cluster alerts are shown on overview @@ -51,8 +51,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-yellow-platinum--with-10-alerts', { - from: '2017-08-29 17:23:47.528', - to: '2017-08-29 17:25:50.701', + from: 'Aug 29, 2017 @ 17:23:47.528', + to: 'Aug 29, 2017 @ 17:25:50.701', }); // ensure cluster alerts are shown on overview @@ -166,8 +166,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-yellow-platinum', { - from: '2017-08-29 17:23:47.528', - to: '2017-08-29 17:25:50.701', + from: 'Aug 29, 2017 @ 17:23:47.528', + to: 'Aug 29, 2017 @ 17:25:50.701', }); // ensure cluster alerts are shown on overview diff --git a/x-pack/test/functional/apps/monitoring/cluster/list.js b/x-pack/test/functional/apps/monitoring/cluster/list.js index 70e89689a89dbc..399b392cad7972 100644 --- a/x-pack/test/functional/apps/monitoring/cluster/list.js +++ b/x-pack/test/functional/apps/monitoring/cluster/list.js @@ -21,8 +21,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/multicluster', { - from: '2017-08-15 21:00:00.000', - to: '2017-08-16 00:00:00.000', + from: 'Aug 15, 2017 @ 21:00:00.000', + to: 'Aug 16, 2017 @ 00:00:00.000', }); await clusterList.assertDefaults(); @@ -76,8 +76,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/multi-basic', { - from: '2017-09-07 20:12:04.011', - to: '2017-09-07 20:18:55.733', + from: 'Sep 7, 2017 @ 20:12:04.011', + to: 'Sep 7, 2017 @ 20:18:55.733', }); await clusterList.assertDefaults(); diff --git a/x-pack/test/functional/apps/monitoring/cluster/overview.js b/x-pack/test/functional/apps/monitoring/cluster/overview.js index 8242150e404eb6..3396426e953808 100644 --- a/x-pack/test/functional/apps/monitoring/cluster/overview.js +++ b/x-pack/test/functional/apps/monitoring/cluster/overview.js @@ -16,8 +16,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-green-gold', { - from: '2017-08-23 21:29:35.267', - to: '2017-08-23 21:47:25.556', + from: 'Aug 23, 2017 @ 21:29:35.267', + to: 'Aug 23, 2017 @ 21:47:25.556', }); }); @@ -71,8 +71,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-yellow-platinum', { - from: '2017-08-29 17:23:47.528', - to: '2017-08-29 17:25:50.701', + from: 'Aug 29, 2017 @ 17:23:47.528', + to: 'Aug 29, 2017 @ 17:25:50.701', }); }); @@ -121,8 +121,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-yellow-basic', { - from: '2017-08-29 17:55:43.879', - to: '2017-08-29 18:01:34.958', + from: 'Aug 29, 2017 @ 17:55:43.879', + to: 'Aug 29, 2017 @ 18:01:34.958', }); }); diff --git a/x-pack/test/functional/apps/monitoring/elasticsearch/index_detail.js b/x-pack/test/functional/apps/monitoring/elasticsearch/index_detail.js index c3fe5f9273a897..7109f9363e1b7e 100644 --- a/x-pack/test/functional/apps/monitoring/elasticsearch/index_detail.js +++ b/x-pack/test/functional/apps/monitoring/elasticsearch/index_detail.js @@ -25,8 +25,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-three-nodes-shard-relocation', { - from: '2017-10-05 20:31:48.354', - to: '2017-10-05 20:35:12.176' + from: 'Oct 5, 2017 @ 20:31:48.354', + to: 'Oct 5, 2017 @ 20:35:12.176' }); // go to indices listing @@ -83,8 +83,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-red-platinum', { - from: '2017-10-06 19:53:06.748', - to: '2017-10-06 20:15:30.212' + from: 'Oct 6, 2017 @ 19:53:06.748', + to: 'Oct 6, 2017 @ 20:15:30.212' }); // go to indices listing diff --git a/x-pack/test/functional/apps/monitoring/elasticsearch/indices.js b/x-pack/test/functional/apps/monitoring/elasticsearch/indices.js index 1ed7c15a9ecf1f..6500f373807a43 100644 --- a/x-pack/test/functional/apps/monitoring/elasticsearch/indices.js +++ b/x-pack/test/functional/apps/monitoring/elasticsearch/indices.js @@ -17,8 +17,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-red-platinum', { - from: '2017-10-06 19:53:06.748', - to: '2017-10-06 20:15:30.212', + from: 'Oct 6, 2017 @ 19:53:06.748', + to: 'Oct 6, 2017 @ 20:15:30.212', }); // go to indices listing diff --git a/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail.js b/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail.js index 99dce6c1b89b6f..6521753de506a1 100644 --- a/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail.js +++ b/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail.js @@ -19,8 +19,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-three-nodes-shard-relocation', { - from: '2017-10-05 20:31:48.354', - to: '2017-10-05 20:35:12.176' + from: 'Oct 5, 2017 @ 20:31:48.354', + to: 'Oct 5, 2017 @ 20:35:12.176' }); // go to nodes listing @@ -74,8 +74,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-red-platinum', { - from: '2017-10-06 19:53:06.748', - to: '2017-10-06 20:15:30.212' + from: 'Oct 6, 2017 @ 19:53:06.748', + to: 'Oct 6, 2017 @ 20:15:30.212' }); // go to nodes listing diff --git a/x-pack/test/functional/apps/monitoring/elasticsearch/nodes.js b/x-pack/test/functional/apps/monitoring/elasticsearch/nodes.js index 7ad09e034e13b9..94ad5e493d73e1 100644 --- a/x-pack/test/functional/apps/monitoring/elasticsearch/nodes.js +++ b/x-pack/test/functional/apps/monitoring/elasticsearch/nodes.js @@ -21,8 +21,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-three-nodes-shard-relocation', { - from: '2017-10-05 20:28:28.475', - to: '2017-10-05 20:34:38.341', + from: 'Oct 5, 2017 @ 20:28:28.475', + to: 'Oct 5, 2017 @ 20:34:38.341', }); // go to nodes listing @@ -196,8 +196,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-three-nodes-shard-relocation', { - from: '2017-10-05 20:31:48.354', - to: '2017-10-05 20:35:12.176', + from: 'Oct 5, 2017 @ 20:31:48.354', + to: 'Oct 5, 2017 @ 20:35:12.176', }); // go to nodes listing diff --git a/x-pack/test/functional/apps/monitoring/elasticsearch/overview.js b/x-pack/test/functional/apps/monitoring/elasticsearch/overview.js index a0e91e2336e082..b5429cdc891f94 100644 --- a/x-pack/test/functional/apps/monitoring/elasticsearch/overview.js +++ b/x-pack/test/functional/apps/monitoring/elasticsearch/overview.js @@ -17,8 +17,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-three-nodes-shard-relocation', { - from: '2017-10-05 20:31:48.354', - to: '2017-10-05 20:35:12.176' + from: 'Oct 5, 2017 @ 20:31:48.354', + to: 'Oct 5, 2017 @ 20:35:12.176' }); // go to overview diff --git a/x-pack/test/functional/apps/monitoring/elasticsearch/shards.js b/x-pack/test/functional/apps/monitoring/elasticsearch/shards.js index ab02eead163338..4a40d686af4968 100644 --- a/x-pack/test/functional/apps/monitoring/elasticsearch/shards.js +++ b/x-pack/test/functional/apps/monitoring/elasticsearch/shards.js @@ -21,8 +21,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-three-nodes-shard-relocation', { - from: '2017-10-05 19:34:48.000', - to: '2017-10-05 20:35:12.000', + from: 'Oct 5, 2017 @ 19:34:48.000', + to: 'Oct 5, 2017 @ 20:35:12.000', }); }); diff --git a/x-pack/test/functional/apps/monitoring/kibana/instance.js b/x-pack/test/functional/apps/monitoring/kibana/instance.js index 33448fc4b206a5..0d4e06fb2b98f1 100644 --- a/x-pack/test/functional/apps/monitoring/kibana/instance.js +++ b/x-pack/test/functional/apps/monitoring/kibana/instance.js @@ -17,8 +17,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-yellow-platinum', { - from: '2017-08-29 17:24:14.254', - to: '2017-08-29 17:25:44.142', + from: 'Aug 29, 2017 @ 17:24:14.254', + to: 'Aug 29, 2017 @ 17:25:44.142', }); // go to kibana instance diff --git a/x-pack/test/functional/apps/monitoring/kibana/instances.js b/x-pack/test/functional/apps/monitoring/kibana/instances.js index 28bf108d59f242..8f72806eee4419 100644 --- a/x-pack/test/functional/apps/monitoring/kibana/instances.js +++ b/x-pack/test/functional/apps/monitoring/kibana/instances.js @@ -17,8 +17,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-yellow-platinum', { - from: '2017-08-29 17:24:14.254', - to: '2017-08-29 17:25:44.142', + from: 'Aug 29, 2017 @ 17:24:14.254', + to: 'Aug 29, 2017 @ 17:25:44.142', }); // go to kibana instances diff --git a/x-pack/test/functional/apps/monitoring/kibana/overview.js b/x-pack/test/functional/apps/monitoring/kibana/overview.js index 7282e48fc8a919..c5da75e37a77f6 100644 --- a/x-pack/test/functional/apps/monitoring/kibana/overview.js +++ b/x-pack/test/functional/apps/monitoring/kibana/overview.js @@ -17,8 +17,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/singlecluster-yellow-platinum', { - from: '2017-08-29 17:24:14.254', - to: '2017-08-29 17:25:44.142', + from: 'Aug 29, 2017 @ 17:24:14.254', + to: 'Aug 29, 2017 @ 17:25:44.142', }); // go to kibana overview diff --git a/x-pack/test/functional/apps/monitoring/logstash/pipelines.js b/x-pack/test/functional/apps/monitoring/logstash/pipelines.js index fb9fcf8bab8bb4..f4d2a5a4a20a5e 100644 --- a/x-pack/test/functional/apps/monitoring/logstash/pipelines.js +++ b/x-pack/test/functional/apps/monitoring/logstash/pipelines.js @@ -19,8 +19,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/logstash-pipelines', { - from: '2018-01-22 09:10:00.000', - to: '2018-01-22 09:41:00.000', + from: 'Jan 22, 2018 @ 09:10:00.000', + to: 'Jan 22, 2018 @ 09:41:00.000', }); // go to pipelines listing diff --git a/x-pack/test/functional/apps/monitoring/time_filter.js b/x-pack/test/functional/apps/monitoring/time_filter.js index 0afcada14be5fb..ae5b11e8e0b325 100644 --- a/x-pack/test/functional/apps/monitoring/time_filter.js +++ b/x-pack/test/functional/apps/monitoring/time_filter.js @@ -17,8 +17,8 @@ export default function ({ getService, getPageObjects }) { before(async () => { await setup('monitoring/multicluster', { - from: '2017-08-15 21:00:00.000', - to: '2017-08-16 00:00:00.000', + from: 'Aug 15, 2017 @ 21:00:00.000', + to: 'Aug 16, 2017 @ 00:00:00.000', }); await clusterList.assertDefaults(); }); @@ -35,7 +35,7 @@ export default function ({ getService, getPageObjects }) { }); it('should send another request when changing the time picker', async () => { - await PageObjects.timePicker.setAbsoluteRange('2016-08-15 21:00:00.000', '2016-08-16 00:00:00.000'); + await PageObjects.timePicker.setAbsoluteRange('Aug 15, 2016 @ 21:00:00.000', 'Aug 16, 2016 @ 00:00:00.000'); await clusterList.assertNoData(); }); }); diff --git a/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js b/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js index dca5534fb68b31..95bae4b24a5350 100644 --- a/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js +++ b/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js @@ -11,7 +11,7 @@ import mockRolledUpData, { mockIndices } from './hybrid_index_helper'; export default function ({ getService, getPageObjects }) { - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); const retry = getService('retry'); const PageObjects = getPageObjects(['common', 'settings']); diff --git a/x-pack/test/functional/apps/rollup_job/rollup_jobs.js b/x-pack/test/functional/apps/rollup_job/rollup_jobs.js index eeff20105aed22..0888f0972592ec 100644 --- a/x-pack/test/functional/apps/rollup_job/rollup_jobs.js +++ b/x-pack/test/functional/apps/rollup_job/rollup_jobs.js @@ -11,7 +11,7 @@ import { mockIndices } from './hybrid_index_helper'; export default function ({ getService, getPageObjects }) { - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['rollup', 'common']); diff --git a/x-pack/test/functional/apps/security/rbac_phase1.js b/x-pack/test/functional/apps/security/rbac_phase1.js index 9b8be614c9f5bf..3f9f2f6bdbe87d 100644 --- a/x-pack/test/functional/apps/security/rbac_phase1.js +++ b/x-pack/test/functional/apps/security/rbac_phase1.js @@ -85,8 +85,6 @@ export default function ({ getService, getPageObjects }) { // this is to acertain that all role assigned to the user can perform actions like creating a Visualization it('rbac all role can save a visualization', async function () { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; const vizName1 = 'Visualization VerticalBarChart'; log.debug('log in as kibanauser with rbac_all role'); @@ -96,8 +94,10 @@ export default function ({ getService, getPageObjects }) { log.debug('clickVerticalBarChart'); await PageObjects.visualize.clickVerticalBarChart(); await PageObjects.visualize.clickNewSearch(); - log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + log.debug('Set absolute time range from \"' + + PageObjects.timePicker.defaultStartTime + '\" to \"' + + PageObjects.timePicker.defaultEndTime + '\"'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.visualize.waitForVisualization(); await PageObjects.visualize.saveVisualizationExpectSuccess(vizName1); await PageObjects.security.logout(); diff --git a/x-pack/test/functional/apps/uptime/monitor.ts b/x-pack/test/functional/apps/uptime/monitor.ts index ecbe893b1f2c06..034ccad4815a1f 100644 --- a/x-pack/test/functional/apps/uptime/monitor.ts +++ b/x-pack/test/functional/apps/uptime/monitor.ts @@ -19,8 +19,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { after(async () => await esArchiver.unload(archive)); it('loads and displays uptime data based on date range', async () => { await pageObjects.uptime.loadDataAndGoToMonitorPage( - '2019-09-10 12:40:08.078', - '2019-09-11 19:40:08.078', + 'Sep 10, 2019 @ 12:40:08.078', + 'Sep 11, 2019 @ 19:40:08.078', '0000-intermittent', '0000-intermittent' ); diff --git a/x-pack/test/functional/apps/uptime/overview.ts b/x-pack/test/functional/apps/uptime/overview.ts index cdb904537a0f25..9a337c86185fe0 100644 --- a/x-pack/test/functional/apps/uptime/overview.ts +++ b/x-pack/test/functional/apps/uptime/overview.ts @@ -12,8 +12,8 @@ export default ({ getPageObjects }: FtrProviderContext) => { const pageObjects = getPageObjects(['uptime']); describe('overview page', function() { - const DEFAULT_DATE_START = '2019-09-10 12:40:08.078'; - const DEFAULT_DATE_END = '2019-09-11 19:40:08.078'; + const DEFAULT_DATE_START = 'Sep 10, 2019 @ 12:40:08.078'; + const DEFAULT_DATE_END = 'Sep 11, 2019 @ 19:40:08.078'; it('loads and displays uptime data based on date range', async () => { await pageObjects.uptime.goToUptimeOverviewAndLoadData( DEFAULT_DATE_START, diff --git a/x-pack/test/functional/apps/visualize/hybrid_visualization.ts b/x-pack/test/functional/apps/visualize/hybrid_visualization.ts index dacd0b75b126c3..03b6ed8e8e7c54 100644 --- a/x-pack/test/functional/apps/visualize/hybrid_visualization.ts +++ b/x-pack/test/functional/apps/visualize/hybrid_visualization.ts @@ -75,8 +75,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { ['2019-08-22 00:00', 'php', '11'], ['2019-08-22 16:00', 'jpg', '3'], ]; - const fromTime = '2019-08-19 01:55:07.240'; - const toTime = '2019-08-22 23:09:36.205'; + const fromTime = 'Aug 19, 2019 @ 01:55:07.240'; + const toTime = 'Aug 22, 2019 @ 23:09:36.205'; await PageObjects.common.navigateToApp('visualize'); await PageObjects.visualize.clickVisualizationByName('hybrid_histogram_line_chart'); diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 8153a8713ca2f3..480814cb027817 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -46,7 +46,9 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * Move the date filter to the specified time range, defaults to * a range that has data in our dataset. */ - goToTimeRange(fromTime = '2015-09-19 06:31:44.000', toTime = '2015-09-23 18:31:44.000') { + goToTimeRange(fromTime?: string, toTime?: string) { + fromTime = fromTime || PageObjects.timePicker.defaultStartTime; + toTime = toTime || PageObjects.timePicker.defaultEndTime; return PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); }, diff --git a/x-pack/test/functional/page_objects/reporting_page.js b/x-pack/test/functional/page_objects/reporting_page.js index 30de10c400c888..0ba0325ad56020 100644 --- a/x-pack/test/functional/page_objects/reporting_page.js +++ b/x-pack/test/functional/page_objects/reporting_page.js @@ -165,15 +165,13 @@ export function ReportingPageProvider({ getService, getPageObjects }) { async setTimepickerInDataRange() { log.debug('Reporting:setTimepickerInDataRange'); - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.timePicker.setDefaultAbsoluteRange(); } async setTimepickerInNoDataRange() { log.debug('Reporting:setTimepickerInNoDataRange'); - const fromTime = '1999-09-19 06:31:44.000'; - const toTime = '1999-09-23 18:31:44.000'; + const fromTime = 'Sep 19, 1999 @ 06:31:44.000'; + const toTime = 'Sep 23, 1999 @ 18:31:44.000'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); } } diff --git a/x-pack/test/functional/services/machine_learning/api.ts b/x-pack/test/functional/services/machine_learning/api.ts index 270722a97d6b61..2fc027a81ea8cf 100644 --- a/x-pack/test/functional/services/machine_learning/api.ts +++ b/x-pack/test/functional/services/machine_learning/api.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; import { JOB_STATE, DATAFEED_STATE } from '../../../../legacy/plugins/ml/common/constants/states'; export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const log = getService('log'); const retry = getService('retry'); const esSupertest = getService('esSupertest'); diff --git a/x-pack/test/functional/services/transform_ui/api.ts b/x-pack/test/functional/services/transform_ui/api.ts index 9050b5944dee3e..a6756e5940d72b 100644 --- a/x-pack/test/functional/services/transform_ui/api.ts +++ b/x-pack/test/functional/services/transform_ui/api.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export function TransformAPIProvider({ getService }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const log = getService('log'); const retry = getService('retry'); diff --git a/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts b/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts index 0a3b7f595c66d3..450f7b1a427dc4 100644 --- a/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts +++ b/x-pack/test/kerberos_api_integration/apis/security/kerberos_login.ts @@ -362,7 +362,7 @@ export default function({ getService }: FtrProviderContext) { // Let's delete tokens from `.security-tokens` index directly to simulate the case when // Elasticsearch automatically removes access/refresh token document from the index after // some period of time. - const esResponse = await getService('es').deleteByQuery({ + const esResponse = await getService('legacyEs').deleteByQuery({ index: '.security-tokens', q: 'doc_type:token', refresh: true, diff --git a/x-pack/test/kerberos_api_integration/services.ts b/x-pack/test/kerberos_api_integration/services.ts index 42505994e06029..e2e7a5b8844a74 100644 --- a/x-pack/test/kerberos_api_integration/services.ts +++ b/x-pack/test/kerberos_api_integration/services.ts @@ -7,7 +7,7 @@ import { services as apiIntegrationServices } from '../api_integration/services'; export const services = { - es: apiIntegrationServices.es, + legacyEs: apiIntegrationServices.legacyEs, esSupertest: apiIntegrationServices.esSupertest, supertestWithoutAuth: apiIntegrationServices.supertestWithoutAuth, }; diff --git a/x-pack/test/oidc_api_integration/apis/authorization_code_flow/oidc_auth.js b/x-pack/test/oidc_api_integration/apis/authorization_code_flow/oidc_auth.js index 7460b1f9e238c2..80ef6bd6df4ff1 100644 --- a/x-pack/test/oidc_api_integration/apis/authorization_code_flow/oidc_auth.js +++ b/x-pack/test/oidc_api_integration/apis/authorization_code_flow/oidc_auth.js @@ -482,7 +482,7 @@ export default function ({ getService }) { // Let's delete tokens from `.security-tokens` index directly to simulate the case when // Elasticsearch automatically removes access/refresh token document from the index // after some period of time. - const esResponse = await getService('es').deleteByQuery({ + const esResponse = await getService('legacyEs').deleteByQuery({ index: '.security-tokens', q: 'doc_type:token', refresh: true, diff --git a/x-pack/test/oidc_api_integration/services.ts b/x-pack/test/oidc_api_integration/services.ts index e4ff6048a8cce3..dda9f1d1a88863 100644 --- a/x-pack/test/oidc_api_integration/services.ts +++ b/x-pack/test/oidc_api_integration/services.ts @@ -7,6 +7,6 @@ import { services as apiIntegrationServices } from '../api_integration/services'; export const services = { - es: apiIntegrationServices.es, + legacyEs: apiIntegrationServices.legacyEs, supertestWithoutAuth: apiIntegrationServices.supertestWithoutAuth, }; diff --git a/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/encrypted_saved_objects_api.ts b/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/encrypted_saved_objects_api.ts index 98d653d71b5ec3..ab9f7d2cdd3395 100644 --- a/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/encrypted_saved_objects_api.ts +++ b/x-pack/test/plugin_api_integration/test_suites/encrypted_saved_objects/encrypted_saved_objects_api.ts @@ -9,7 +9,7 @@ import { SavedObject } from 'src/core/server'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ getService }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const randomness = getService('randomness'); const supertest = getService('supertest'); diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js index a8ff03dc71d247..9b4297e995cbd8 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js @@ -12,7 +12,7 @@ import supertestAsPromised from 'supertest-as-promised'; const { task: { properties: taskManagerIndexMapping } } = require('../../../../legacy/plugins/task_manager/mappings.json'); export default function ({ getService }) { - const es = getService('es'); + const es = getService('legacyEs'); const log = getService('log'); const retry = getService('retry'); const config = getService('config'); diff --git a/x-pack/test/saml_api_integration/apis/security/saml_login.ts b/x-pack/test/saml_api_integration/apis/security/saml_login.ts index 276b42423860cb..d512894fbb1f24 100644 --- a/x-pack/test/saml_api_integration/apis/security/saml_login.ts +++ b/x-pack/test/saml_api_integration/apis/security/saml_login.ts @@ -622,7 +622,7 @@ export default function({ getService }: FtrProviderContext) { // Let's delete tokens from `.security` index directly to simulate the case when // Elasticsearch automatically removes access/refresh token document from the index // after some period of time. - const esResponse = await getService('es').deleteByQuery({ + const esResponse = await getService('legacyEs').deleteByQuery({ index: '.security-tokens', q: 'doc_type:token', refresh: true, diff --git a/x-pack/test/saml_api_integration/config.ts b/x-pack/test/saml_api_integration/config.ts index 9d3029db013d3c..6ea29b0d9e56e3 100644 --- a/x-pack/test/saml_api_integration/config.ts +++ b/x-pack/test/saml_api_integration/config.ts @@ -21,7 +21,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { servers: xPackAPITestsConfig.get('servers'), services: { randomness: kibanaAPITestsConfig.get('services.randomness'), - es: kibanaAPITestsConfig.get('services.es'), + legacyEs: kibanaAPITestsConfig.get('services.legacyEs'), supertestWithoutAuth: xPackAPITestsConfig.get('services.supertestWithoutAuth'), }, junit: { diff --git a/x-pack/test/saml_api_integration/services.ts b/x-pack/test/saml_api_integration/services.ts index 307108fa097f40..d207112f6e81fa 100644 --- a/x-pack/test/saml_api_integration/services.ts +++ b/x-pack/test/saml_api_integration/services.ts @@ -8,6 +8,6 @@ import { services as apiIntegrationServices } from '../api_integration/services' export const services = { randomness: apiIntegrationServices.randomness, - es: apiIntegrationServices.es, + legacyEs: apiIntegrationServices.legacyEs, supertestWithoutAuth: apiIntegrationServices.supertestWithoutAuth, }; diff --git a/x-pack/test/saved_object_api_integration/common/services/index.ts b/x-pack/test/saved_object_api_integration/common/services/index.ts index 1ea312714fc1e9..aba605fee89471 100644 --- a/x-pack/test/saved_object_api_integration/common/services/index.ts +++ b/x-pack/test/saved_object_api_integration/common/services/index.ts @@ -12,7 +12,7 @@ import { services as kibanaApiIntegrationServices } from '../../../../../test/ap import { services as kibanaFunctionalServices } from '../../../../../test/functional/services'; export const services = { - es: LegacyEsProvider, + legacyEs: LegacyEsProvider, esSupertestWithoutAuth: apiIntegrationServices.esSupertestWithoutAuth, supertest: kibanaApiIntegrationServices.supertest, supertestWithoutAuth: apiIntegrationServices.supertestWithoutAuth, diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_create.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_create.ts index 7e00d0a9d7f759..7768665f3b941a 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_create.ts @@ -12,7 +12,7 @@ import { bulkCreateTestSuiteFactory } from '../../common/suites/bulk_create'; export default function({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { bulkCreateTest, diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/create.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/create.ts index 4cc94bf21f235d..e4adaa580c1dbe 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/create.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/create.ts @@ -11,7 +11,7 @@ import { createTestSuiteFactory } from '../../common/suites/create'; export default function({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); const { diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/import.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/import.ts index f9048d1493cc63..58859c292ce359 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/import.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/import.ts @@ -12,7 +12,7 @@ import { importTestSuiteFactory } from '../../common/suites/import'; export default function({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { importTest, diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/index.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/index.ts index 1b1e9762a54e5d..bb42c5422ece51 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/index.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/index.ts @@ -8,7 +8,7 @@ import { createUsersAndRoles } from '../../common/lib/create_users_and_roles'; import { FtrProviderContext } from '../../common/ftr_provider_context'; export default function({ getService, loadTestFile }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const supertest = getService('supertest'); describe('saved objects security and spaces enabled', function() { diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve_import_errors.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve_import_errors.ts index fa070ac76ce572..6c91fe6310170d 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve_import_errors.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve_import_errors.ts @@ -12,7 +12,7 @@ import { resolveImportErrorsTestSuiteFactory } from '../../common/suites/resolve export default function({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { resolveImportErrorsTest, diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_create.ts b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_create.ts index 2a5757fb0cc579..943a22c4399c77 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_create.ts @@ -11,7 +11,7 @@ import { bulkCreateTestSuiteFactory } from '../../common/suites/bulk_create'; export default function({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { bulkCreateTest, diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/create.ts b/x-pack/test/saved_object_api_integration/security_only/apis/create.ts index bb2bf7e0e0d7ad..60a9fa0a86aa65 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/create.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/create.ts @@ -10,7 +10,7 @@ import { createTestSuiteFactory } from '../../common/suites/create'; export default function({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); const { diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/import.ts b/x-pack/test/saved_object_api_integration/security_only/apis/import.ts index 8ad7e070edf546..770410dcfed819 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/import.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/import.ts @@ -11,7 +11,7 @@ import { importTestSuiteFactory } from '../../common/suites/import'; export default function({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { importTest, diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/index.ts b/x-pack/test/saved_object_api_integration/security_only/apis/index.ts index 6e930338a11845..bb637a9bc4c90e 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/index.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/index.ts @@ -8,7 +8,7 @@ import { createUsersAndRoles } from '../../common/lib/create_users_and_roles'; import { FtrProviderContext } from '../../common/ftr_provider_context'; export default function({ getService, loadTestFile }: FtrProviderContext) { - const es = getService('es'); + const es = getService('legacyEs'); const supertest = getService('supertest'); describe('saved objects security only enabled', function() { diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/resolve_import_errors.ts b/x-pack/test/saved_object_api_integration/security_only/apis/resolve_import_errors.ts index e1d72f8641e936..59d50c16c259a8 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/resolve_import_errors.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/resolve_import_errors.ts @@ -11,7 +11,7 @@ import { resolveImportErrorsTestSuiteFactory } from '../../common/suites/resolve export default function({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { resolveImportErrorsTest, diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts index 6b67c63ed349ae..d89e3b4f8605be 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts @@ -25,7 +25,7 @@ const expectNamespaceSpecifiedBadRequest = (resp: { [key: string]: any }) => { export default function({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { bulkCreateTest, diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts index d6bd0b497759fc..cf34f7505cdb20 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts @@ -23,7 +23,7 @@ const expectNamespaceSpecifiedBadRequest = (resp: { [key: string]: any }) => { export default function({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); - const es = getService('es'); + const es = getService('legacyEs'); const esArchiver = getService('esArchiver'); const { diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/import.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/import.ts index c8b2e68291dc39..c78a0e1cc2ccec 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/import.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/import.ts @@ -11,7 +11,7 @@ import { importTestSuiteFactory } from '../../common/suites/import'; export default function({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { importTest, diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve_import_errors.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve_import_errors.ts index 93d60ebbf4d6d4..22a7ab81e5530f 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve_import_errors.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve_import_errors.ts @@ -11,7 +11,7 @@ import { resolveImportErrorsTestSuiteFactory } from '../../common/suites/resolve export default function({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { resolveImportErrorsTest, diff --git a/x-pack/test/spaces_api_integration/common/config.ts b/x-pack/test/spaces_api_integration/common/config.ts index 23514c635d3562..dffc6c524cc6ec 100644 --- a/x-pack/test/spaces_api_integration/common/config.ts +++ b/x-pack/test/spaces_api_integration/common/config.ts @@ -34,7 +34,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) testFiles: [require.resolve(`../${name}/apis/`)], servers: config.xpack.api.get('servers'), services: { - es: LegacyEsProvider, + legacyEs: LegacyEsProvider, esSupertestWithoutAuth: config.xpack.api.get('services.esSupertestWithoutAuth'), supertest: config.kibana.api.get('services.supertest'), supertestWithoutAuth: config.xpack.api.get('services.supertestWithoutAuth'), diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space.ts index c6c41f7cd3a977..1c4ca2fe206628 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space.ts @@ -13,7 +13,7 @@ import { copyToSpaceTestSuiteFactory } from '../../common/suites/copy_to_space'; export default function copyToSpaceSpacesAndSecuritySuite({ getService }: TestInvoker) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { copyToSpaceTest, diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/delete.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/delete.ts index 25d7317d7dd906..df4a2f51b4872a 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/delete.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/delete.ts @@ -13,7 +13,7 @@ import { deleteTestSuiteFactory } from '../../common/suites/delete'; export default function deleteSpaceTestSuite({ getService }: TestInvoker) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { deleteTest, diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts index 300949f41f0369..e918ab0b538418 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts @@ -9,7 +9,7 @@ import { TestInvoker } from '../../common/lib/types'; // eslint-disable-next-line import/no-default-export export default function({ loadTestFile, getService }: TestInvoker) { - const es = getService('es'); + const es = getService('legacyEs'); const supertest = getService('supertest'); describe('spaces api with security', function() { diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/copy_to_space.ts b/x-pack/test/spaces_api_integration/spaces_only/apis/copy_to_space.ts index b1427a9b77be06..4781c4bea9b30d 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/spaces_only/apis/copy_to_space.ts @@ -11,7 +11,7 @@ import { copyToSpaceTestSuiteFactory } from '../../common/suites/copy_to_space'; export default function copyToSpacesOnlySuite({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { copyToSpaceTest, diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/delete.ts b/x-pack/test/spaces_api_integration/spaces_only/apis/delete.ts index 350b6824297784..64cbb7a02776c1 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/delete.ts +++ b/x-pack/test/spaces_api_integration/spaces_only/apis/delete.ts @@ -12,7 +12,7 @@ import { deleteTestSuiteFactory } from '../../common/suites/delete'; export default function deleteSpaceTestSuite({ getService }: TestInvoker) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); const { deleteTest, diff --git a/x-pack/test/token_api_integration/auth/header.js b/x-pack/test/token_api_integration/auth/header.js index 2317f62e638091..4b27fd5db31663 100644 --- a/x-pack/test/token_api_integration/auth/header.js +++ b/x-pack/test/token_api_integration/auth/header.js @@ -6,7 +6,7 @@ export default function ({ getService }) { const supertest = getService('supertestWithoutAuth'); - const es = getService('es'); + const es = getService('legacyEs'); async function createToken() { const { access_token: accessToken } = await es.shield.getAccessToken({ diff --git a/x-pack/test/token_api_integration/auth/session.js b/x-pack/test/token_api_integration/auth/session.js index 60450218948230..8a9f1d7a3f2295 100644 --- a/x-pack/test/token_api_integration/auth/session.js +++ b/x-pack/test/token_api_integration/auth/session.js @@ -132,7 +132,7 @@ export default function ({ getService }) { // Let's delete tokens from `.security` index directly to simulate the case when // Elasticsearch automatically removes access/refresh token document from the index // after some period of time. - const esResponse = await getService('es').deleteByQuery({ + const esResponse = await getService('legacyEs').deleteByQuery({ index: '.security-tokens', q: 'doc_type:token', refresh: true, diff --git a/x-pack/test/token_api_integration/config.js b/x-pack/test/token_api_integration/config.js index 44086245a90c0b..8cf9bc329a3744 100644 --- a/x-pack/test/token_api_integration/config.js +++ b/x-pack/test/token_api_integration/config.js @@ -11,7 +11,7 @@ export default async function ({ readConfigFile }) { testFiles: [require.resolve('./auth')], servers: xPackAPITestsConfig.get('servers'), services: { - es: xPackAPITestsConfig.get('services.es'), + legacyEs: xPackAPITestsConfig.get('services.legacyEs'), supertestWithoutAuth: xPackAPITestsConfig.get('services.supertestWithoutAuth'), }, junit: { diff --git a/x-pack/test/upgrade_assistant_integration/config.js b/x-pack/test/upgrade_assistant_integration/config.js index 4cd4ca78d3a588..3a43d43543b61b 100644 --- a/x-pack/test/upgrade_assistant_integration/config.js +++ b/x-pack/test/upgrade_assistant_integration/config.js @@ -20,9 +20,9 @@ export default async function ({ readConfigFile }) { testFiles: [require.resolve('./upgrade_assistant')], servers: xPackFunctionalTestsConfig.get('servers'), services: { + ...kibanaCommonConfig.get('services'), supertest: kibanaAPITestsConfig.get('services.supertest'), - es: LegacyEsProvider, - esArchiver: kibanaCommonConfig.get('services.esArchiver'), + legacyEs: LegacyEsProvider, }, esArchiver: xPackFunctionalTestsConfig.get('esArchiver'), junit: { diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js index 233336c722611b..137de18fc98d9b 100644 --- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js +++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js @@ -12,7 +12,7 @@ import { ReindexStatus, REINDEX_OP_TYPE } from '../../../legacy/plugins/upgrade_ export default function ({ getService }) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); + const es = getService('legacyEs'); // Utility function that keeps polling API until reindex operation has completed or failed. const waitForReindexToComplete = async (indexName) => { diff --git a/yarn.lock b/yarn.lock index 150e7a98796fe9..85405acbcf7a0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -990,7 +990,7 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.4.4": +"@babel/runtime@^7.4.4", "@babel/runtime@^7.6.3": version "7.7.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a" integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw== @@ -1126,21 +1126,6 @@ ts-debounce "^1.0.0" uuid "^3.3.2" -"@elastic/ctags-langserver@^0.1.11": - version "0.1.11" - resolved "https://registry.yarnpkg.com/@elastic/ctags-langserver/-/ctags-langserver-0.1.11.tgz#e4725a6a763a2ff61fd02bbe2b42aa70db5d0f3f" - integrity sha512-ODNcD+zFmuhMm649/4fGQXD1msafvBaHKsk9PDXTjLrUMozrCWj99tGKqR5HcwrdkoMr/3YRBre2r4cyomtoBw== - dependencies: - "@elastic/lsp-extension" "^0.1.1" - "@elastic/node-ctags" "1.0.2" - commander "^2.11.0" - find-root "^1.1.0" - line-column "^1.0.2" - minimatch "3.0.4" - mz "^2.7.0" - parse-gitignore "1.0.1" - vscode-languageserver "^5.2.1" - "@elastic/elasticsearch@^7.4.0": version "7.4.0" resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.4.0.tgz#57f4066acf25e9d4e9b4f6376088433aae6f25d4" @@ -1188,10 +1173,10 @@ tabbable "^1.1.0" uuid "^3.1.0" -"@elastic/eui@14.9.0": - version "14.9.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-14.9.0.tgz#934ab8d51c56671635dc17ac20ec325f43ceda75" - integrity sha512-0ZztvfRO3SNgHtS8a+4i6CSG3Yc+C0Kodzc7obY5wkOzissrnbwLZdU79hU/H6DHYCt/zYDdGcrDp6BeD67RtQ== +"@elastic/eui@16.0.0": + version "16.0.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-16.0.0.tgz#511898bfbeba5ffea6ac96c077d1184d657a451d" + integrity sha512-i9t13PzrsfBUolMZ6n2X9aAYJ/wUI2NJduCQlDU4zrXfFrM1LFJ5/KSCcpgzI8VNDakeA3PTml+oqD7J0qGA3g== dependencies: "@types/lodash" "^4.14.116" "@types/numeral" "^0.0.25" @@ -1203,12 +1188,12 @@ lodash "^4.17.11" numeral "^2.0.6" prop-types "^15.6.0" - react-ace "^5.5.0" + react-ace "^7.0.5" react-beautiful-dnd "^10.1.0" react-focus-lock "^1.17.7" - react-input-autosize "^2.2.1" + react-input-autosize "^2.2.2" react-is "~16.3.0" - react-virtualized "^9.18.5" + react-virtualized "^9.21.2" resize-observer-polyfill "^1.5.0" tabbable "^3.0.0" uuid "^3.1.0" @@ -1267,7 +1252,7 @@ vscode-languageserver-types "^3.0.3" yarn "^1.12.3" -"@elastic/lsp-extension@^0.1.1", "@elastic/lsp-extension@^0.1.2": +"@elastic/lsp-extension@^0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@elastic/lsp-extension/-/lsp-extension-0.1.2.tgz#7356d951d272e833d02a81e13a0ef710f9474195" integrity sha512-yDj5Ht5KCHDwBlgrlusmLtV/Yxa5z2f3vMSYbNFotoRMup8345/ZwlFp/zmyl04iFOVpT8ouB34+Ttpzbpd3vA== @@ -1304,11 +1289,6 @@ resolved "https://registry.yarnpkg.com/@elastic/node-crypto/-/node-crypto-1.0.0.tgz#4d325df333fe1319556bb4d54214098ada1171d4" integrity sha512-bbjbEyILPRTRt0xnda18OttLtlkJBPuXx3CjISUSn9jhWqHoFMzfOaZ73D5jxZE2SaFZUrJYfPpqXP6qqPufAQ== -"@elastic/node-ctags@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@elastic/node-ctags/-/node-ctags-1.0.2.tgz#447d7694a5598f9413fe2b6f356d56f64f612dfd" - integrity sha512-EHhJ0NPlCvYy+gbzBMU4/Z/55hftfdwlAG8JwOy7g0ITmH6rFPanEnzg1WL3/L+pp8OlYHyvDLwmyg0+06y8LQ== - "@elastic/numeral@2.3.3": version "2.3.3" resolved "https://registry.yarnpkg.com/@elastic/numeral/-/numeral-2.3.3.tgz#94d38a35bd315efa7a6918b22695128fc40a885e" @@ -4875,15 +4855,10 @@ angular-ui-ace@0.2.3: resolved "https://registry.yarnpkg.com/angular-ui-ace/-/angular-ui-ace-0.2.3.tgz#3cb903428100621a367fc7f641440e97a42a26d0" integrity sha1-PLkDQoEAYho2f8f2QUQOl6QqJtA= -angular@>=1.0.6: - version "1.6.9" - resolved "https://registry.yarnpkg.com/angular/-/angular-1.6.9.tgz#bc812932e18909038412d594a5990f4bb66c0619" - integrity sha512-6igWH2GIsxV+J38wNWCh8oyjaZsrIPIDO35twloIUyjlF2Yit6UyLAWujHP05ma/LFxTsx4NtYibRoMNBXPR1A== - -angular@^1.7.8: - version "1.7.8" - resolved "https://registry.yarnpkg.com/angular/-/angular-1.7.8.tgz#b77ede272ce1b261e3be30c1451a0b346905a3c9" - integrity sha512-wtef/y4COxM7ZVhddd7JtAAhyYObq9YXKar9tsW7558BImeVYteJiTxCKeJOL45lJ/+7B4wrAC49j8gTFYEthg== +angular@>=1.0.6, angular@^1.7.9: + version "1.7.9" + resolved "https://registry.yarnpkg.com/angular/-/angular-1.7.9.tgz#e52616e8701c17724c3c238cfe4f9446fd570bc4" + integrity sha512-5se7ZpcOtu0MBFlzGv5dsM1quQDoDeUTwZrWjGtTNA7O88cD8TEk5IEKCTDa3uECV9XnvKREVUr7du1ACiWGFQ== ansi-align@^2.0.0: version "2.0.0" @@ -6779,7 +6754,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -brace@0.11.1, brace@^0.11.0: +brace@0.11.1, brace@^0.11.0, brace@^0.11.1: version "0.11.1" resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58" integrity sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg= @@ -8061,6 +8036,11 @@ cloneable-readable@^1.0.0: process-nextick-args "^2.0.0" readable-stream "^2.3.5" +clsx@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.0.4.tgz#0c0171f6d5cb2fe83848463c15fcc26b4df8c2ec" + integrity sha512-1mQ557MIZTrL/140j+JVdRM6e31/OA4vTYxXgqIIZlndyfjHpyawKZia1Im05Vp9BWmImkcNrNtFYQMyFcgJDg== + cmd-shim@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-2.1.0.tgz#e59a08d4248dda3bb502044083a4db4ac890579a" @@ -9198,6 +9178,11 @@ csstype@^2.5.7: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.3.tgz#b701e5968245bf9b08d54ac83d00b624e622a9fa" integrity sha512-rINUZXOkcBmoHWEyu7JdHu5JMzkGRoMX4ov9830WNgxf5UYxcBUO0QTKAqeJ5EZfSdlrcJYkC8WwfVW7JYi4yg== +csstype@^2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.7.tgz#20b0024c20b6718f4eda3853a1f5a1cce7f5e4a5" + integrity sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ== + cuint@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" @@ -10159,6 +10144,11 @@ diagnostics@^1.1.1: enabled "1.0.x" kuler "1.0.x" +diff-match-patch@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1" + integrity sha512-Uv3SW8bmH9nAtHKaKSanOQmj2DnlH65fUpcrMdfdaOxUG02QQ4YGZ8AE7kKOMisF7UqvOlGKVYWRvezdncW9lg== + diff-sequences@^24.0.0: version "24.0.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.0.0.tgz#cdf8e27ed20d8b8d3caccb4e0c0d8fe31a173013" @@ -10286,6 +10276,14 @@ dom-helpers@^3.3.1: dependencies: "@babel/runtime" "^7.1.2" +dom-helpers@^5.0.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.3.tgz#7233248eb3a2d1f74aafca31e52c5299cc8ce821" + integrity sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw== + dependencies: + "@babel/runtime" "^7.6.3" + csstype "^2.6.7" + dom-serialize@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" @@ -17835,14 +17833,6 @@ liftoff@^3.1.0: rechoir "^0.6.2" resolve "^1.1.7" -line-column@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/line-column/-/line-column-1.0.2.tgz#d25af2936b6f4849172b312e4792d1d987bc34a2" - integrity sha1-0lryk2tvSEkXKzEuR5LR2Ye8NKI= - dependencies: - isarray "^1.0.0" - isobject "^2.0.0" - linebreak@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/linebreak/-/linebreak-0.3.0.tgz#0526480a62c05bd679f3e9d99830e09c6a7d0ed6" @@ -19705,7 +19695,7 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mz@^2.6.0, mz@^2.7.0: +mz@^2.6.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== @@ -20648,7 +20638,7 @@ one-time@0.0.4: onetime@^1.0.0: version "1.1.0" - resolved "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + resolved "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= onetime@^2.0.0: @@ -21192,11 +21182,6 @@ parse-git-config@^1.1.1: git-config-path "^1.0.1" ini "^1.3.4" -parse-gitignore@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-gitignore/-/parse-gitignore-1.0.1.tgz#8b9dc57f17b810d495c5dfa62eb07caffe7758c7" - integrity sha512-UGyowyjtx26n65kdAMWhm6/3uy5uSrpcuH7tt+QEVudiBoVS+eqHxD5kbi9oWVRwj7sCzXqwuM+rUGw7earl6A== - parse-headers@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.1.tgz#6ae83a7aa25a9d9b700acc28698cd1f1ed7e9536" @@ -22685,6 +22670,17 @@ react-ace@^5.9.0: lodash.isequal "^4.1.1" prop-types "^15.5.8" +react-ace@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-7.0.5.tgz#798299fd52ddf3a3dcc92afc5865538463544f01" + integrity sha512-3iI+Rg2bZXCn9K984ll2OF4u9SGcJH96Q1KsUgs9v4M2WePS4YeEHfW2nrxuqJrAkE5kZbxaCE79k6kqK0YBjg== + dependencies: + brace "^0.11.1" + diff-match-patch "^1.0.4" + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + prop-types "^15.7.2" + react-addons-create-fragment@^15.6.2: version "15.6.2" resolved "https://registry.yarnpkg.com/react-addons-create-fragment/-/react-addons-create-fragment-15.6.2.tgz#a394de7c2c7becd6b5475ba1b97ac472ce7c74f8" @@ -23415,6 +23411,18 @@ react-virtualized@^9.18.5: prop-types "^15.6.0" react-lifecycles-compat "^3.0.4" +react-virtualized@^9.21.2: + version "9.21.2" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.21.2.tgz#02e6df65c1e020c8dbf574ec4ce971652afca84e" + integrity sha512-oX7I7KYiUM7lVXQzmhtF4Xg/4UA5duSA+/ZcAvdWlTLFCoFYq1SbauJT5gZK9cZS/wdYR6TPGpX/dqzvTqQeBA== + dependencies: + babel-runtime "^6.26.0" + clsx "^1.0.1" + dom-helpers "^5.0.0" + loose-envify "^1.3.0" + prop-types "^15.6.0" + react-lifecycles-compat "^3.0.4" + react-vis@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/react-vis/-/react-vis-1.8.2.tgz#0e0aebc427e50856a01b666569ffad0411ef050f"